Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

overload2

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

overload2 - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

example/mutable.js

29

CHANGELOG.md

@@ -5,2 +5,31 @@ # overload2

## [0.2.0] - 2017-06, Mutable Param
### What's New?
Since this section, programmers are allowed to use mutable param. E.g.
```javascript
overload2()
.overload('+', Function, function(/*Array*/ data, callback) {
// ...
})
.overload(Error, '*', function(err, /*Array*/ rest) {
// ...
})
```
For more details, see the testcase named [mutable.js](./test/mutable.js).
__ATTENTION:__
Before, `'*'` is alias of `overload2.Type.ANY`. And now, it is mapped to any number of arguments of any type.
### Fixed
Next bugs have been fixed in this version:
* Failed to run the default method when no predefined paramlist matched.
In previous version, `TypeError: Cannot read property 'apply' of undefined` will be thrown when overload2 tries to execute the default method.
## [0.1.0] - 2017-06

@@ -7,0 +36,0 @@

@@ -36,2 +36,8 @@ /**

)
.overload(
'*', Date, '*',
function select(some, d, others) {
return d.getDay();
}
)
;

@@ -55,2 +61,5 @@

var day = getDay('a', 'b', new Date('2000-1-1'), 'c', 'd');
assert.equal(day, 6);
console.log('THIS LINE REACHED MEANS EVERYTHING IS OK.');

311

overload2.js
/**
* 函数多态的中量级实现。
* @author youngoat@163.com
*
* 注释中的术语表:
* * 参数 = 定义中的形式参数
* * 值组 = 多态函数调用时的实际参数表
*/

@@ -97,6 +101,24 @@

// 数据是否包含指定元素。
// 数组是否包含指定元素。
, has = function(arr, item) {
return arr.indexOf(item) >= 0;
}
// 数组中的每个元素是否均能通过指定函数的测试。
, eachMatch = function(arr, fn) {
var matched = true;
for (var i = 0; matched && i < arr.length; i++) {
matched = fn(arr[i], i);
}
return matched;
}
// 数组中任意元素通过指定函数的测试。
, onceMatch = function(arr, fn) {
var matched = false;
for (var i = 0; !matched && i < arr.length; i++) {
matched = fn(arr[i], i);
}
return matched;
}
;

@@ -182,7 +204,5 @@

return new Type(function(value) {
var matched = true;
for (var i = 0; i < types.length && matched; i++) {
matched = types[i].match(value);
}
return matched;
return eachMatch(types, function(type) {
return type.match(value);
});
});

@@ -195,7 +215,5 @@ };

return new Type(function(value) {
var matched = false;
for (var i = 0; i < types.length && !matched; i++) {
matched = types[i].match(value);
}
return matched;
return onceMatch(types, function(type) {
return type.match(value);
});
});

@@ -258,5 +276,67 @@ };

this.type = null;
this.minSize = 1;
this.maxSize = 1;
this.nil = false;
this.undef = false;
var setSize = (function(size) {
if (typeof size == 'string') {
// 清除所有空白字符。
size = size.replace(/\s/g, '');
if (size == '*' || size == '...') {
this.minSize = 0;
this.maxSize = Infinity;
return true;
}
if (size == '+') {
this.minSize = 1;
this.maxSize = Infinity;
return true;
}
// 区间形式。
if (/^\{.+\}$/.test(size)) {
size = size.slice(1, -1);
}
if (/^\d+$/.test(size)) {
this.minSize = this.maxSize = parseInt(size);
return true;
}
if (/^,(\d+)$/.test(size)) {
this.minSize = 0;
this.maxSize = parseInt(RegExp.$1);
return true;
}
if (/^(\d+),$/.test(size)) {
this.minSize = parseInt(RegExp.$1);
this.maxSize = Infinity;
return true;
}
if (/^(\d+),(\d+)$/.test(size)) {
this.minSize = parseInt(RegExp.$1);
this.maxSize = parseInt(RegExp.$2);
return true;
}
return false;
}
if (Number.isInteger(size) && size > 0) {
this.minSize = this.maxSize = size;
return true;
}
return false;
}).bind(this);
var type = arguments[0], decos = [];
// ---------------------------
// 处理特殊的参数占位符。
if (setSize(type)) {
this.type = Type.ANY;
return this;
}
// ---------------------------
// 处理数据类型。

@@ -279,3 +359,8 @@

for (i = 1; i < arguments.length; i++) {
decos = decos.concat(arguments[i].trim().split(/\s+/));
if (typeof arguments[i] == 'string') {
decos = decos.concat(arguments[i].trim().split(/\s+/));
}
else if (Number.isInteger(arguments[i])) {
decos.push(arguments[i]);
}
}

@@ -285,3 +370,6 @@ for (i = 0; i < decos.length; i++) {

// 修饰符不区分大小写。
// 尝试作为量化修饰符应用。
if (setSize(rawDeco)) continue;
// 普通修饰符不区分大小写。
decos[i] = decos[i].toUpperCase();

@@ -298,6 +386,6 @@

// 参数是否可以为 null
this.nil = has(decos, 'null');
this.nil = has(decos, 'NULL');
// 参数是否可以为 undefined
this.undef = has(decos, 'undefined');
this.undef = has(decos, 'UNDEFINED');
}

@@ -307,3 +395,5 @@

// 判断值是否合乎参数限定。
/**
* 判断值是否合乎参数限定。
*/
Param.prototype.satisfy = function(value) {

@@ -321,11 +411,16 @@ if (value === null && this.nil) return true;

var params = [];
var minSize = 0;
var maxSize = 0;
for (var i = 0, args; i < arguments.length; i++) {
args = (arguments[i] instanceof Array) ? arguments[i] : [ arguments[i] ];
params[i] = Param.parse.apply(null, args);
minSize += params[i].minSize;
maxSize += params[i].maxSize;
}
this.length = params.length;
this.item = function(i) {
return params[i];
};
// 指定匹配值组的最小和最大长度。
this.minSize = minSize;
this.maxSize = maxSize;
this.params = params;
}

@@ -336,16 +431,138 @@

/**
* 判断值组是否合乎参数值限定。
* 判断值组是否合乎参数定义。
* @deprecated
*/
ParamList.prototype.satisfy = function(args) {
// 参数表长度检查。
if (args.length != this.length) {
if (args.length != this.params.length) {
return false;
}
// 参数类型核对
var matched = true;
for (var i = 0; matched && i < this.length; i++) {
matched = this.item(i).satisfy(args[i]);
// 参数类型核对。
return eachMatch(this.params, function(param, index) {
return param.satisfy(args[index]);
});
}
/**
* 尝试按参数定义解析值组。
* @return false 匹配失败
* @return Array 匹配成功,返回与定义一一对应的新值组(因为参数可能是可变长度的)
*/
ParamList.prototype.parse = function(args) {
// ---------------------------
// 检查值组的长度是否在参数列表定义的区间内。
if (args.length < this.minSize || this.maxSize < args.length) {
return null;
}
return matched;
// ---------------------------
// 若整个参数组非可变长,则适用简易匹配逻辑。
if (this.minSize == this.maxSize) {
var matched = eachMatch(this.params, function(param, index) {
return param.satisfy(args[index]);
});
return matched ? args : null;
}
// ---------------------------
// 否则,适用复杂匹配逻辑。
// 注意:此函数将被递归调用。
var matching = function(args, params) {
var newArgs = [];
var argCursor = 0, paramCursor = 0;
for (; argCursor < args.length; argCursor++) {
// 如果所有形式参数匹配成功之后,仍有多余的实参未参加匹配,则认为整个参数表匹配失败。
if (paramCursor >= params.length) {
return null;
}
// 当前参数。
var param = params[paramCursor];
// 当前实参。
var arg = args[argCursor];
// ---------------------------
// 如果形式参数对应单个实参(不包括多于 1 的定长),则按简易逻辑处理。
if (param.minSize == param.maxSize == 1) {
if (!param.satisfy(arg)) {
// 如果实参与形式参数不匹配,则终止后续匹配,整个参数表匹配失败。
return null;
}
else {
newArgs.push(arg);
paramCursor++;
}
continue;
}
// ---------------------------
// 否则,适用复杂逻辑。
// 如果剩余实参数量不足以匹配当前形式参数,则匹配失败。
if (args.length - argCursor < param.minSize) {
return null;
}
// 依次储存当前形式参数匹配的实参。
var paramArgs = [];
for (; argCursor < args.length && param.satisfy(args[argCursor]); argCursor++) {
paramArgs.push(args[argCursor]);
}
// 如当前形式参数匹配实参个数未达到最小值,则认为当前形式参数匹配失败,整个参数表匹配失败。
if (paramArgs.length < param.minSize) {
return null;
}
var restParams = params.slice(++paramCursor);
var restArgs = args.slice(argCursor);
// 抵达匹配边界时,若仅匹配了最小数量的实参,或者已是最后一个形式参数,则直接固定。
if (paramArgs.length == param.minSize || restParams.length == 0) {
newArgs.push(paramArgs);
argCursor--;
}
// 否则,须尝试让贤。
else {
// 步步让贤,直到让无可让。
do {
var restNewArgs = matching(restArgs, restParams);
// 剩余参数匹配成功,则拼接匹配结果,整个参数表匹配成功。
if (restNewArgs != null) {
newArgs.push(paramArgs);
return newArgs.concat(restNewArgs);
}
// 已无余量。
else if (paramArgs.length == 0) {
break;
}
else {
restArgs.unshift(paramArgs.pop());
}
} while (paramArgs.length >= param.minSize);
return null;
}
}
for (; newArgs.length < params.length; paramCursor++) {
if (params[paramCursor].minSize == 0) {
newArgs.push([]);
}
else {
return null;
}
}
return newArgs;
};
return matching(Array.from(args), this.params);
};

@@ -374,7 +591,9 @@

if (typeof args[0] == 'number') {
if (args.length > 1) {
throw new ERR.Arguments(2, args.length + 1);
}
this.paramCount = args[0];
// 支持可变长度参数后,用于代表值组长度的数字,也可以用 Param 实例来表征。
// 在此我们仍保留 argLength,是出于提高性能的考虑。
if (typeof args[0] == 'number' && args.length == 1) {
// if (args.length > 1) {
// throw new ERR.Arguments(2, args.length + 1);
// }
this.argLength = args[0];
}

@@ -399,9 +618,15 @@ else if (args[0] instanceof ParamList) {

Overload.prototype.exec = function(scope, args) {
// 若指定了形参数量,而实参数量不符,则直接跳过。
if (typeof this.paramCount == 'number' && (args.length != this.paramCount)) {
return false;
// 对于每一个重载实现,值组长度和形参表是互斥的。
// 或者指定值组长度,或者指定形参表,而不是同时指定。
// 如指定值组长度,则仅判断长度。
if (typeof this.argLength == 'number') {
if (args.length != this.argLength) return false;
}
if (this.params && !this.params.satisfy(args)) {
return false;
// 否则,须尝试匹配值组与参数列表。
else {
// 尝试获取按形式参数列表重整后的值组。
args = this.params.parse(args);
if (!args) return false;
}

@@ -452,7 +677,13 @@

if (this._defaultMethod) {
return this.defaultMethod.apply(scope, args);
return this._defaultMethod.apply(scope, args);
}
// 表示没有任何重载形参与实参匹配。
throw new ERR.Unmatching('Unmatching arguments: ' + args);
var types = [];
for (var i = 0, type; i < args.length; i++) {
type = typeof args[i];
if (type === 'object') type = args[i].constructor.name;
types.push(type);
}
throw new ERR.Unmatching('Unmatching arguments: ' + types.join(', '));
};

@@ -585,3 +816,3 @@

var TYPE_ALIAS =
{ '*' : Type.ANY
{ '?' : Type.ANY
, 'any' : Type.ANY

@@ -588,0 +819,0 @@ , 'boolean' : Type.BOOLEAN

2

package.json
{
"name": "overload2",
"version": "0.1.0",
"version": "0.2.0",
"description": "Elegant solution for function overloading in JavaScript",

@@ -5,0 +5,0 @@ "main": "overload2.js",

# overload2
__Elegant solution for function overloading in JavaScript.__
[![Build Status](https://travis-ci.org/YounGoat/ecmascript.overload2.svg?branch=master)](https://travis-ci.org/YounGoat/ecmascript.overload2)
[![Coverage Status](https://coveralls.io/repos/github/YounGoat/ecmascript.overload2/badge.svg?branch=master)](https://coveralls.io/github/YounGoat/ecmascript.overload2?branch=master)
[![NPM total downloads](https://img.shields.io/npm/dt/overload2.svg)](https://www.npmjs.com/package/overload2)
[![NPM version](https://img.shields.io/npm/v/overload2.svg)](https://www.npmjs.com/package/overload2)
[![NPM license](https://img.shields.io/npm/l/overload2.svg)](./LICENSE.txt)
[![GitHub stars](https://img.shields.io/github/stars/YounGoat/ecmascript.overload2.svg?style=social&label=Star)](https://github.com/YounGoat/ecmascript.overload2/stargazers)
When you are tired with writing tasteless code to do with arguments, *overload2* will __MAKE THINGS EASY__.

@@ -19,2 +12,3 @@

* [Datatypes](#datatypes)
* [Mutable Parameter](#mutable)
* [Move Forward](#move-forward)

@@ -33,4 +27,4 @@ * [APIs](#apis)

<a name="get-started"></a>
## Get Started
<a name="get-started"></a>

@@ -67,5 +61,14 @@ Install *overload2* firstly.

.overload(
4, // length of arguments
function four() { console.log('too many arguments'); }
2, // length of arguments
function md(month, date) {
var d = new Date;
return d.setMonth(month - 1), d.setDate(date), d.getDay();
}
)
.overload(
'*', Date, '*',
function select(some, d, others) {
return d.getDay();
}
)
;

@@ -82,11 +85,20 @@

getDay(1, 2, 3, 4);
// four() invoked
getDay(12, 1);
// md() invoked
getDay('foo', 'bar', new Date, 'quz');
// select() invoked
```
<a name="datatypes"></a>
## Datatypes
<a name="datatypes"></a>
According to *overload2* , there are different ways to define a datatype.
* [Constructor Function](#constructor-function)
* [Customized Datatype](#customized-datatype)
* [Predefined Datatype](#predefined-datatype)
* [Datatype Alias](#datatype-alias)
* [Create Datatype With Factory Method](#create-datatype-with-factory-method)
### Constructor Function

@@ -151,3 +163,3 @@

| :--------- | :------------------------------ |
| * | __overload2.Type.ANY__ |
| ? | __overload2.Type.ANY__ |
| any | __overload2.Type.ANY__ |

@@ -177,4 +189,56 @@ | boolean | __overload2.Type.BOOLEAN__ |

<a name="mutable"></a>
## Mutable Parameter
By appending size decorator, we can define mutable parameters. E.g.
```javascript
var add = overload2()
.overload('number *', function(numbers) {
var ret = 0;
numbers.forEach(function(number) { ret += number; })
return ret;
})
.overload('boolean {2,3}', function(bools) {
var ret = false;
for (var i = 0; !ret && i < bools.length; i++) {
ret = bools[i];
}
return ret;
})
.overload([ Date, '+' ], function(dates) {
var date = dates[0];
for (var i = 1; i < dates.length; i++) {
if (date < dates[i]) {
date = dates[i];
}
}
return date;
})
;
```
Size decorators look like [repetition in regular expression](http://www.regular-expressions.info/repeat.html). Here are some examples for overload param with size decorators:
```javascript
[ Date, '{2,}'] // takes at least 2 arguments which are instances of Date
'number *' // takes any number (including zero) of arguments of type number
'*' // takes any number (including zero) of arguments of any type
'+' // takes at least one argument
'?' // takes one argument
'{2}' // takes 2 arguments
'{2,4}' // takes 2 to 4 arguments
'{2,}' // takes at least 2 arguments
'{,4{}' // takes no more than 4 arguments
// The braces may be omitted, so the following are also valid.
'2'
'2,4'
'2,'
',4'
```
<a name="move-forward"></a>
## Move Forward
<a name="move-forward"></a>

@@ -198,4 +262,4 @@ Beyond the basic use, *overload2* is also suitable with more complex and large-scale programs. See the class hierarchy shown below:

<a name="apis"></a>
## APIs
<a name="apis"></a>

@@ -209,4 +273,4 @@ * [overload2()](#api-overload2)

<a name="api-overload2"></a>
### overload2(), Create An Overloaded Function
<a name="api-overload2"></a>

@@ -224,4 +288,4 @@ `overload2` itself is a function, when invoked, it will return an overloded function instance.

<a name="api-class-type"></a>
### class overload2.Type
<a name="api-class-type"></a>

@@ -236,4 +300,4 @@ To define a datatype in context of *overload2*, there are different ways including `overload2.Type`. And all other datatypes will be converted to instances of `overload2.Type` before being used.

<a name="api-class-param"></a>
### class overload2.Param
<a name="api-class-param"></a>

@@ -259,4 +323,4 @@ A Param is made up of a Type and some decorators. Available decorators are:

<a name="api-class-paramlist"></a>
### class overload2.ParamList
<a name="api-class-paramlist"></a>

@@ -272,4 +336,4 @@ * new __overload2.ParamList__( [ Param | Array | String \<param\> [ , ... ] ] )

<a name="api-class-overload"></a>
### class overload2.Overload
<a name="api-class-overload"></a>

@@ -291,4 +355,4 @@ * new __overload2.Overload__( number <argumentsNumber>, function \<implementation\> )

<a name="api-class-overloadedfunction"></a>
### class overload2.OverloadedFunction
<a name="api-class-overloadedfunction"></a>

@@ -313,4 +377,4 @@ * new __overload2.OverloadedFunction__()

<a name="examples"></a>
## Examples
<a name="examples"></a>

@@ -343,4 +407,4 @@ * [Basic Usage](./example/basic.js)

<a name="why-overload2"></a>
## Why overload2
<a name="why-overload2"></a>

@@ -357,4 +421,4 @@ There have been dozens of packages devoted to function overloading in JavaScript, and some of them are really not bad, e.g.

<a name="dependents"></a>
## Honorable Dependents
<a name="dependents"></a>

@@ -361,0 +425,0 @@ Welcome to be the first dependent of *overload2*!

@@ -14,2 +14,14 @@ var MODULE_REQUIRE

describe('Predefined datatypes', function() {
it('?', function() {
assert(Type.ANY.match(0));
assert(Type.ANY.match(1));
assert(Type.ANY.match(null));
assert(Type.ANY.match(undefined));
assert(Type.ANY.match(''));
assert(Type.ANY.match(true));
assert(Type.ANY.match(false));
assert(Type.ANY.match([]));
assert(Type.ANY.match({}));
});
it('ANY', function() {

@@ -16,0 +28,0 @@ assert(Type.ANY.match(0));

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc