mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-01 15:57:48 +00:00
Add _.curry.
Former-commit-id: 33282039186323ad4f58d7d7ec19e3c3974dc9b3
This commit is contained in:
5
build.js
5
build.js
@@ -120,6 +120,7 @@
|
||||
'contains': ['baseEach', 'getIndexOf', 'isString'],
|
||||
'countBy': ['createAggregator'],
|
||||
'createCallback': ['baseCreateCallback', 'baseIsEqual', 'isObject', 'keys'],
|
||||
'curry': ['createBound'],
|
||||
'debounce': ['isObject'],
|
||||
'defaults': ['createIterator'],
|
||||
'defer': ['bind'],
|
||||
@@ -390,6 +391,7 @@
|
||||
'bindKey',
|
||||
'createCallback',
|
||||
'compose',
|
||||
'curry',
|
||||
'debounce',
|
||||
'defer',
|
||||
'delay',
|
||||
@@ -560,6 +562,7 @@
|
||||
'bindKey',
|
||||
'cloneDeep',
|
||||
'createCallback',
|
||||
'curry',
|
||||
'findIndex',
|
||||
'findKey',
|
||||
'findLast',
|
||||
@@ -3867,7 +3870,7 @@
|
||||
if (!isLodash('range')) {
|
||||
source = source.replace(matchFunction(source, 'range'), function(match) {
|
||||
return match
|
||||
.replace(/typeof *step[^:]+:/, '+step ||')
|
||||
.replace(/typeof *step[^:]+:/, '')
|
||||
.replace(/\(step.*\|\|.+?\)/, 'step')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
'countBy',
|
||||
'createCallback',
|
||||
'criteria',
|
||||
'curry',
|
||||
'debounce',
|
||||
'defaults',
|
||||
'defer',
|
||||
|
||||
136
lodash.js
136
lodash.js
@@ -1059,7 +1059,7 @@
|
||||
setBindData(func, bindData);
|
||||
}
|
||||
// exit early if there are no `this` references or `func` is bound
|
||||
if (bindData !== true && !(bindData && bindData[4])) {
|
||||
if (bindData !== true && !(bindData && bindData[1] & 1)) {
|
||||
return func;
|
||||
}
|
||||
switch (argCount) {
|
||||
@@ -1416,44 +1416,57 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that, when called, invokes `func` with the `this` binding
|
||||
* of `thisArg` and prepends any `partialArgs` to the arguments provided to the
|
||||
* bound function.
|
||||
* Creates a function that, when called, either curries or invokes `func`
|
||||
* with an optional `this` binding and partially applied arguments.
|
||||
*
|
||||
* @private
|
||||
* @param {Function|String} func The function to bind or the method name.
|
||||
* @param {Mixed} thisArg The `this` binding of `func`.
|
||||
* @param {Array} partialArgs An array of arguments to be prepended to those provided to the new function.
|
||||
* @param {Array} partialRightArgs An array of arguments to be appended to those provided to the new function.
|
||||
* @param {Boolean} [isPartial=false] A flag to indicate performing only partial application.
|
||||
* @param {Boolean} [isAlt=false] A flag to indicate `_.bindKey` or `_.partialRight` behavior.
|
||||
* @param {Function|String} func The function or method name to reference.
|
||||
* @param {Number} bitmask The bitmask of method flags to compose.
|
||||
* The bitmask may be composed of the following flags:
|
||||
* 1 - `_.bind`
|
||||
* 2 - `_.bindKey`
|
||||
* 4 - `_.curry`
|
||||
* 8 - `_.partial`
|
||||
* 16 - `_.partialRight`
|
||||
* @param {Array} [partialArgs] An array of arguments to prepend to those
|
||||
* provided to the new function.
|
||||
* @param {Array} [partialRightArgs] An array of arguments to append to those
|
||||
* provided to the new function.
|
||||
* @param {Mixed} [thisArg] The `this` binding of `func`.
|
||||
* @param {Number} [arity] The arity of `func`.
|
||||
* @returns {Function} Returns the new bound function.
|
||||
*/
|
||||
function createBound(func, thisArg, partialArgs, partialRightArgs, isPartial, isAlt) {
|
||||
var isBindKey = isAlt && !isPartial,
|
||||
isFunc = isFunction(func);
|
||||
function createBound(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
|
||||
var isBind = bitmask & 1,
|
||||
isBindKey = bitmask & 2,
|
||||
isCurry = bitmask & 4,
|
||||
isPartialRight = bitmask & 16;
|
||||
|
||||
// throw if `func` is not a function when not behaving as `_.bindKey`
|
||||
if (!isFunc && !isBindKey) {
|
||||
if (!isBindKey && !isFunction(func)) {
|
||||
throw new TypeError;
|
||||
}
|
||||
var args = func.__bindData__;
|
||||
if (args) {
|
||||
push.apply(args[2], partialArgs);
|
||||
push.apply(args[3], partialRightArgs);
|
||||
|
||||
// add `thisArg` to previous `_.partial` and `_.partialRight` arguments
|
||||
if (!isPartial && args[4]) {
|
||||
args[1] = thisArg;
|
||||
args[4] = false;
|
||||
args[5] = isAlt;
|
||||
var bindData = func && func.__bindData__;
|
||||
if (bindData) {
|
||||
if (isBind && !(bindData[1] & 1)) {
|
||||
bindData[4] = thisArg;
|
||||
}
|
||||
return createBound.apply(null, args);
|
||||
if (isCurry && !(bindData[1] & 4)) {
|
||||
bindData[5] = arity;
|
||||
}
|
||||
if (partialArgs) {
|
||||
push.apply(bindData[2] || (bindData[2] = []), partialArgs);
|
||||
}
|
||||
if (partialRightArgs) {
|
||||
push.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
|
||||
}
|
||||
bindData[1] |= bitmask;
|
||||
return createBound.apply(null, bindData);
|
||||
}
|
||||
// use `Function#bind` if it exists and is fast
|
||||
// (in V8 `Function#bind` is slower except when partially applied)
|
||||
if (!isPartial && !isAlt && !partialRightArgs.length && (support.fastBind || (nativeBind && partialArgs.length))) {
|
||||
args = [func, thisArg];
|
||||
if (isBind && !(isBindKey || isCurry || isPartialRight) &&
|
||||
(support.fastBind || (nativeBind && partialArgs.length))) {
|
||||
var args = [func, thisArg];
|
||||
push.apply(args, partialArgs);
|
||||
var bound = nativeBind.call.apply(nativeBind, args);
|
||||
}
|
||||
@@ -1462,15 +1475,22 @@
|
||||
// `Function#bind` spec
|
||||
// http://es5.github.io/#x15.3.4.5
|
||||
var args = arguments,
|
||||
thisBinding = isPartial ? this : thisArg;
|
||||
thisBinding = isBind ? thisArg : this;
|
||||
|
||||
if (isBindKey) {
|
||||
func = thisArg[key];
|
||||
}
|
||||
if (partialArgs.length || partialRightArgs.length) {
|
||||
if (partialArgs) {
|
||||
unshift.apply(args, partialArgs);
|
||||
}
|
||||
if (partialRightArgs) {
|
||||
push.apply(args, partialRightArgs);
|
||||
}
|
||||
if (isCurry && args.length < arity) {
|
||||
bindData[2] = args;
|
||||
bindData[3] = null;
|
||||
return createBound(bound, bitmask & ~8 & ~16);
|
||||
}
|
||||
if (isBindKey) {
|
||||
func = thisBinding[key];
|
||||
}
|
||||
if (this instanceof bound) {
|
||||
// ensure `new bound` is an instance of `func`
|
||||
thisBinding = createObject(func.prototype);
|
||||
@@ -1484,12 +1504,12 @@
|
||||
};
|
||||
}
|
||||
// take a snapshot of `arguments` before juggling
|
||||
args = nativeSlice.call(arguments);
|
||||
bindData = nativeSlice.call(arguments);
|
||||
if (isBindKey) {
|
||||
var key = thisArg;
|
||||
thisArg = func;
|
||||
}
|
||||
setBindData(bound, args);
|
||||
setBindData(bound, bindData);
|
||||
return bound;
|
||||
}
|
||||
|
||||
@@ -4628,7 +4648,7 @@
|
||||
*/
|
||||
function range(start, end, step) {
|
||||
start = +start || 0;
|
||||
step = typeof step == 'number' ? step : 1;
|
||||
step = typeof step == 'number' ? step : (+step || 1);
|
||||
|
||||
if (end == null) {
|
||||
end = start;
|
||||
@@ -4678,6 +4698,8 @@
|
||||
*
|
||||
* console.log(evens);
|
||||
* // => [2, 4, 6]
|
||||
*
|
||||
*
|
||||
*/
|
||||
function remove(array, callback, thisArg) {
|
||||
var index = -1,
|
||||
@@ -5043,7 +5065,7 @@
|
||||
* // => 'hi moe'
|
||||
*/
|
||||
function bind(func, thisArg) {
|
||||
return createBound(func, thisArg, nativeSlice.call(arguments, 2), []);
|
||||
return createBound(func, 9, nativeSlice.call(arguments, 2), null, thisArg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5117,7 +5139,7 @@
|
||||
* // => 'hi, moe!'
|
||||
*/
|
||||
function bindKey(object, key) {
|
||||
return createBound(object, key, nativeSlice.call(arguments, 2), [], false, true);
|
||||
return createBound(object, 11, nativeSlice.call(arguments, 2), null, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5231,6 +5253,39 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function which accepts one or more arguments of `func` that when
|
||||
* invoked either executes `func` returning its result, if all `func` arguments
|
||||
* have been provided, or returns a function that accepts one or more of the
|
||||
* remaining `func` arguments, and so on. The arity of `func` can be specified
|
||||
* if `func.length` is not sufficient.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Functions
|
||||
* @param {Function} func The function to curry.
|
||||
* @param {Number} [arity=func.length] The arity of `func`.
|
||||
* @returns {Function} Returns the new curried function.
|
||||
* @example
|
||||
*
|
||||
* var curried = _.curry(function(a, b, c) {
|
||||
* console.log(a + b + c);
|
||||
* });
|
||||
*
|
||||
* curried(1)(2)(3);
|
||||
* // => 6
|
||||
*
|
||||
* curried(1, 2)(3);
|
||||
* // => 6
|
||||
*
|
||||
* curried(1, 2, 3);
|
||||
* // => 6
|
||||
*/
|
||||
function curry(func, arity) {
|
||||
arity = typeof arity == 'number' ? arity : (+arity || func.length);
|
||||
return createBound(func, 4, null, null, null, arity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that will delay the execution of `func` until after
|
||||
* `wait` milliseconds have elapsed since the last time it was invoked.
|
||||
@@ -5484,7 +5539,7 @@
|
||||
* // => 'hi moe'
|
||||
*/
|
||||
function partial(func) {
|
||||
return createBound(func, null, nativeSlice.call(arguments, 1), [], true);
|
||||
return createBound(func, 8, nativeSlice.call(arguments, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5515,7 +5570,7 @@
|
||||
* // => { '_': _, 'jq': $ }
|
||||
*/
|
||||
function partialRight(func) {
|
||||
return createBound(func, null, [], nativeSlice.call(arguments, 1), true, true);
|
||||
return createBound(func, 16, null, nativeSlice.call(arguments, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6181,6 +6236,7 @@
|
||||
lodash.compose = compose;
|
||||
lodash.countBy = countBy;
|
||||
lodash.createCallback = createCallback;
|
||||
lodash.curry = curry;
|
||||
lodash.debounce = debounce;
|
||||
lodash.defaults = defaults;
|
||||
lodash.defer = defer;
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
'bindKey',
|
||||
'createCallback',
|
||||
'compose',
|
||||
'curry',
|
||||
'debounce',
|
||||
'defer',
|
||||
'delay',
|
||||
@@ -289,6 +290,7 @@
|
||||
'bindKey',
|
||||
'cloneDeep',
|
||||
'createCallback',
|
||||
'curry',
|
||||
'findIndex',
|
||||
'findKey',
|
||||
'findLast',
|
||||
|
||||
180
test/test.js
180
test/test.js
@@ -664,6 +664,38 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.curry');
|
||||
|
||||
(function() {
|
||||
test('should curry based on the number of arguments provided', function() {
|
||||
function func(a, b, c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
var curried = _.curry(func);
|
||||
|
||||
equal(curried(1)(2)(3), 6);
|
||||
equal(curried(1, 2)(3), 6);
|
||||
equal(curried(1, 2, 3), 6);
|
||||
});
|
||||
|
||||
test('should not alter the `this` binding', function() {
|
||||
function func(a, b, c) {
|
||||
return this[a] + this[b] + this[c];
|
||||
}
|
||||
|
||||
var object = { 'a': 1, 'b': 2, 'c': 3 };
|
||||
|
||||
equal(_.curry(_.bind(func, object), 3)('a')('b')('c'), 6);
|
||||
equal(_.bind(_.curry(func), object)('a')('b')('c'), 6);
|
||||
|
||||
object.func = _.curry(func);
|
||||
equal(object.func('a', 'b', 'c'), 6);
|
||||
});
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.debounce');
|
||||
|
||||
(function() {
|
||||
@@ -1700,7 +1732,7 @@
|
||||
deepEqual(_.initial(array, 0), [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('should allow a falsey `array` argument', function() {
|
||||
test('should accept a falsey `array` argument', function() {
|
||||
_.forEach(falsey, function(index, value) {
|
||||
try {
|
||||
var actual = index ? _.initial(value) : _.initial();
|
||||
@@ -2585,12 +2617,15 @@
|
||||
equal(func(fn)(arg), arg);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should not alter the `this` binding of either function', function() {
|
||||
test('`_.' + methodName + '` should not alter the `this` binding', function() {
|
||||
var object = { 'a': 1 },
|
||||
fn = function() { return this.a; };
|
||||
|
||||
strictEqual(func(_.bind(fn, object))(), object.a);
|
||||
strictEqual(_.bind(func(fn), object)(), object.a);
|
||||
|
||||
object.fn = func(fn);
|
||||
strictEqual(object.fn(), object.a);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2974,7 +3009,7 @@
|
||||
deepEqual(_.rest(array, 0), [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('should allow a falsey `array` argument', function() {
|
||||
test('should accept a falsey `array` argument', function() {
|
||||
_.forEach(falsey, function(index, value) {
|
||||
try {
|
||||
var actual = index ? _.rest(value) : _.rest();
|
||||
@@ -3066,7 +3101,7 @@
|
||||
(function() {
|
||||
var args = arguments;
|
||||
|
||||
test('should allow a falsey `object` argument', function() {
|
||||
test('should accept a falsey `object` argument', function() {
|
||||
_.forEach(falsey, function(index, value) {
|
||||
try {
|
||||
var actual = index ? _.size(value) : _.size();
|
||||
@@ -4074,9 +4109,9 @@
|
||||
deepEqual(_.values(args), [[3]], message('remove'));
|
||||
});
|
||||
|
||||
test('should allow falsey primary arguments', function() {
|
||||
test('should accept falsey primary arguments', function() {
|
||||
function message(methodName) {
|
||||
return '`_.' + methodName + '` should allow falsey primary arguments';
|
||||
return '`_.' + methodName + '` should accept falsey primary arguments';
|
||||
}
|
||||
|
||||
deepEqual(_.difference(null, array), [], message('difference'));
|
||||
@@ -4084,9 +4119,9 @@
|
||||
deepEqual(_.union(null, array), array, message('union'));
|
||||
});
|
||||
|
||||
test('should allow falsey secondary arguments', function() {
|
||||
test('should accept falsey secondary arguments', function() {
|
||||
function message(methodName) {
|
||||
return '`_.' + methodName + '` should allow falsey secondary arguments';
|
||||
return '`_.' + methodName + '` should accept falsey secondary arguments';
|
||||
}
|
||||
|
||||
deepEqual(_.difference(array, null), array, message('difference'));
|
||||
@@ -4100,63 +4135,64 @@
|
||||
QUnit.module('lodash methods');
|
||||
|
||||
(function() {
|
||||
test('should allow falsey arguments', function() {
|
||||
var allMethods = _.reject(_.functions(_), function(methodName) {
|
||||
return /^_/.test(methodName);
|
||||
});
|
||||
|
||||
var returnArrays = [
|
||||
'at',
|
||||
'compact',
|
||||
'difference',
|
||||
'filter',
|
||||
'flatten',
|
||||
'functions',
|
||||
'initial',
|
||||
'intersection',
|
||||
'invoke',
|
||||
'keys',
|
||||
'map',
|
||||
'pairs',
|
||||
'pluck',
|
||||
'range',
|
||||
'reject',
|
||||
'rest',
|
||||
'shuffle',
|
||||
'sortBy',
|
||||
'times',
|
||||
'toArray',
|
||||
'union',
|
||||
'uniq',
|
||||
'values',
|
||||
'where',
|
||||
'without',
|
||||
'zip'
|
||||
];
|
||||
|
||||
var rejectFalsey = [
|
||||
'after',
|
||||
'bind',
|
||||
'compose',
|
||||
'curry',
|
||||
'debounce',
|
||||
'defer',
|
||||
'delay',
|
||||
'memoize',
|
||||
'once',
|
||||
'partial',
|
||||
'partialRight',
|
||||
'tap',
|
||||
'throttle',
|
||||
'wrap'
|
||||
];
|
||||
|
||||
var acceptFalsey = _.difference(allMethods, rejectFalsey);
|
||||
|
||||
test('should accept falsey arguments', function() {
|
||||
var isExported = '_' in window,
|
||||
oldDash = window._;
|
||||
|
||||
var returnArrays = [
|
||||
'at',
|
||||
'compact',
|
||||
'difference',
|
||||
'filter',
|
||||
'flatten',
|
||||
'functions',
|
||||
'initial',
|
||||
'intersection',
|
||||
'invoke',
|
||||
'keys',
|
||||
'map',
|
||||
'pairs',
|
||||
'pluck',
|
||||
'range',
|
||||
'reject',
|
||||
'rest',
|
||||
'shuffle',
|
||||
'sortBy',
|
||||
'times',
|
||||
'toArray',
|
||||
'union',
|
||||
'uniq',
|
||||
'values',
|
||||
'where',
|
||||
'without',
|
||||
'zip'
|
||||
];
|
||||
|
||||
var allMethods = _.reject(_.functions(_), function(methodName) {
|
||||
return /^_/.test(methodName);
|
||||
});
|
||||
|
||||
var funcs = _.difference(allMethods, [
|
||||
'after',
|
||||
'bind',
|
||||
'bindAll',
|
||||
'bindKey',
|
||||
'compose',
|
||||
'debounce',
|
||||
'defer',
|
||||
'delay',
|
||||
'functions',
|
||||
'memoize',
|
||||
'once',
|
||||
'partial',
|
||||
'partialRight',
|
||||
'tap',
|
||||
'throttle',
|
||||
'wrap'
|
||||
]);
|
||||
|
||||
_.forEach(funcs, function(methodName) {
|
||||
_.forEach(acceptFalsey, function(methodName) {
|
||||
var actual = [],
|
||||
expected = _.map(falsey, function() { return []; }),
|
||||
func = _[methodName],
|
||||
@@ -4180,7 +4216,27 @@
|
||||
if (_.indexOf(returnArrays, methodName) > -1) {
|
||||
deepEqual(actual, expected, '_.' + methodName + ' returns an array');
|
||||
}
|
||||
ok(pass, '`_.' + methodName + '` allows falsey arguments');
|
||||
ok(pass, '`_.' + methodName + '` accepts falsey arguments');
|
||||
});
|
||||
});
|
||||
|
||||
test('should reject falsey arguments', function() {
|
||||
_.forEach(rejectFalsey, function(methodName) {
|
||||
var actual = [],
|
||||
expected = _.map(falsey, function() { return true; }),
|
||||
func = _[methodName];
|
||||
|
||||
_.forEach(falsey, function(value, index) {
|
||||
var pass = false;
|
||||
try {
|
||||
index ? func(value) : func();
|
||||
} catch(e) {
|
||||
pass = true;
|
||||
}
|
||||
actual.push(pass);
|
||||
});
|
||||
|
||||
deepEqual(actual, expected, '`_.' + methodName + '` rejects falsey arguments');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user