@ndhoule/arity
Version:
Wrap a function in a function that expects a given number of arguments.
157 lines (136 loc) • 3.35 kB
JavaScript
;
var objToString = Object.prototype.toString;
/**
* Determine if a value is a function.
*
* @param {*} val
* @return {boolean}
*/
// TODO: Move to lib
var isFunction = function(val) {
return typeof val === 'function';
};
/**
* Determine if a value is a number.
*
* @param {*} val
* @return {boolean}
*/
// TODO: Move to lib
var isNumber = function(val) {
var type = typeof val;
return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]');
};
/**
* Creates an array of generic, numbered argument names.
*
* @name createParams
* @api private
* @param {number} n
* @return {Array}
* @example
* argNames(2);
* //=> ['arg1', 'arg2']
*/
var createParams = function createParams(n) {
var args = [];
for (var i = 1; i <= n; i += 1) {
args.push('arg' + i);
}
return args;
};
/**
* Dynamically construct a wrapper function of `n` arity that.
*
* If at all possible, prefer a function from the arity wrapper cache above to
* avoid allocating a new function at runtime.
*
* @name createArityWrapper
* @api private
* @param {number} n
* @return {Function(Function)}
*/
var createArityWrapper = function createArityWrapper(n) {
var paramNames = createParams(n).join(', ');
var wrapperBody = ''.concat(
' return function(', paramNames, ') {\n',
' return func.apply(this, arguments);\n',
' };'
);
/* eslint-disable no-new-func */
return new Function('func', wrapperBody);
/* eslint-enable no-new-func */
};
// Cache common arity wrappers to avoid constructing them at runtime
var arityWrapperCache = [
/* eslint-disable no-unused-vars */
function(fn) {
return function() {
return fn.apply(this, arguments);
};
},
function(fn) {
return function(arg1) {
return fn.apply(this, arguments);
};
},
function(fn) {
return function(arg1, arg2) {
return fn.apply(this, arguments);
};
},
function(fn) {
return function(arg1, arg2, arg3) {
return fn.apply(this, arguments);
};
},
function(fn) {
return function(arg1, arg2, arg3, arg4) {
return fn.apply(this, arguments);
};
},
function(fn) {
return function(arg1, arg2, arg3, arg4, arg5) {
return fn.apply(this, arguments);
};
}
/* eslint-enable no-unused-vars */
];
/**
* Takes a function and an [arity](https://en.wikipedia.org/wiki/Arity) `n`, and returns a new
* function that expects `n` arguments.
*
* @name arity
* @api public
* @category Function
* @see {@link curry}
* @param {Number} n The desired arity of the returned function.
* @param {Function} fn The function to wrap.
* @return {Function} A function of n arity, wrapping `fn`.
* @example
* var add = function(a, b) {
* return a + b;
* };
*
* // Check the number of arguments this function expects by accessing `.length`:
* add.length;
* //=> 2
*
* var unaryAdd = arity(1, add);
* unaryAdd.length;
* //=> 1
*/
var arity = function arity(n, func) {
if (!isFunction(func)) {
throw new TypeError('Expected a function but got ' + typeof func);
}
n = Math.max(isNumber(n) ? n : 0, 0);
if (!arityWrapperCache[n]) {
arityWrapperCache[n] = createArityWrapper(n);
}
return arityWrapperCache[n](func);
};
/*
* Exports.
*/
module.exports = arity;