Add placeholder support to _.bind, _.partial, and _.partialRight.

This commit is contained in:
John-David Dalton
2014-02-14 00:40:43 -08:00
parent 9cc06a94ec
commit ee4d9890e7
9 changed files with 592 additions and 224 deletions

View File

@@ -1,7 +1,7 @@
/**
* @license
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
* Build: `lodash underscore -o ./dist/lodash.underscore.js`
* Build: `lodash underscore moduleId="none" -o ./dist/lodash.underscore.js`
* Copyright 2012-2014 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.6.0 <http://underscorejs.org/LICENSE>
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
@@ -423,7 +423,8 @@
function baseBind(data) {
var func = data[0],
thisArg = data[3],
partialArgs = data[4];
partialArgs = data[4],
partialHolders = data[6];
function bound() {
// `Function#bind` spec
@@ -432,8 +433,7 @@
// avoid `arguments` object deoptimizations by using `slice` instead
// of `Array.prototype.slice.call` and not assigning `arguments` to a
// variable as a ternary expression
var args = slice(partialArgs);
push.apply(args, arguments);
var args = composeArgs(partialArgs, partialHolders, arguments);
}
// mimic the constructor's `return` behavior
// http://es5.github.io/#x13.2.2
@@ -523,7 +523,9 @@
arity = data[2],
thisArg = data[3],
partialArgs = data[4],
partialRightArgs = data[5];
partialRightArgs = data[5],
partialHolders = data[6],
partialRightHolders = data[7];
var isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG,
@@ -534,23 +536,22 @@
function bound() {
var thisBinding = isBind ? thisArg : this;
if (partialArgs) {
var args = slice(partialArgs);
push.apply(args, arguments);
var args = composeArgs(partialArgs, partialHolders, arguments);
}
if (partialRightArgs || isCurry) {
args || (args = slice(arguments));
if (partialRightArgs) {
push.apply(args, partialRightArgs);
}
if (partialRightArgs) {
args = composeArgsRight(partialRightArgs, partialRightHolders, args || arguments);
}
if (isCurry) {
var argsLength = arguments.length;
if (isCurry && argsLength < arity) {
if (argsLength < arity) {
args || (args = slice(arguments));
bitmask |= PARTIAL_FLAG;
bitmask &= ~PARTIAL_RIGHT_FLAG
if (!isCurryBound) {
bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
}
var newArity = nativeMax(0, arity - argsLength);
return baseCreateWrapper([func, bitmask, newArity, thisArg, args]);
return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, []]);
}
}
args || (args = arguments);
@@ -899,6 +900,68 @@
return result;
}
/**
* Creates an array that is the composition of partially applied arguments,
* placeholders, and provided arguments into a single array of arguments.
*
* @private
* @param {Array} partialArg An array of arguments to prepend to those provided.
* @param {Array} partialHolders An array of `partialArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
* @returns {Array} Returns a new array of composed arguments.
*/
function composeArgs(partialArgs, partialHolders, args) {
var index = -1,
length = partialHolders.length,
leftIndex = -1,
leftLength = partialArgs.length,
argsLength = nativeMax(args.length - length, 0),
result = Array(argsLength + leftLength);
while (++leftIndex < leftLength) {
result[leftIndex] = partialArgs[leftIndex];
}
while (++index < length) {
result[partialHolders[index]] = args[index];
}
while (length < argsLength) {
result[leftIndex++] = args[length++];
}
return result;
}
/**
* This function is like `composeArgs` except that the arguments composition
* is tailored for `_.partialRight`.
*
* @private
* @param {Array} partialRightArg An array of arguments to append to those provided.
* @param {Array} partialHolders An array of `partialRightArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
* @returns {Array} Returns a new array of composed arguments.
*/
function composeArgsRight(partialRightArgs, partialRightHolders, args) {
var index = -1,
length = partialRightHolders.length,
argsIndex = -1,
argsLength = nativeMax(args.length - length, 0),
rightIndex = -1,
rightLength = partialRightArgs.length,
result = Array(argsLength + rightLength);
while (++argsIndex < argsLength) {
result[argsIndex] = args[argsIndex];
}
var pad = argsIndex;
while (++rightIndex < rightLength) {
result[pad + rightIndex] = partialRightArgs[rightIndex];
}
while (++index < length) {
result[pad + partialHolders[index]] = args[argsIndex++];
}
return result;
}
/**
* Creates a function that aggregates a collection, creating an object or
* array composed from the results of running each element of the collection
@@ -953,9 +1016,11 @@
* provided to the new function.
* @param {Array} [partialRightArgs] An array of arguments to append to those
* provided to the new function.
* @param {Array} [partialHolders] An array of `partialArgs` placeholder indexes.
* @param {Array} [partialRightArgs] An array of `partialRightArgs` placeholder indexes.
* @returns {Function} Returns the new function.
*/
function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs) {
function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders) {
var isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG,
isPartial = bitmask & PARTIAL_FLAG,
@@ -977,13 +1042,39 @@
} else if (arity < 0) {
arity = 0;
}
if (isPartial) {
partialHolders = getHolders(partialArgs);
}
if (isPartialRight) {
partialRightHolders = getHolders(partialRightArgs);
}
// fast path for `_.bind`
var data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs];
var data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders];
return (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG))
? baseBind(data)
: baseCreateWrapper(data);
}
/**
* Finds the indexes of all placeholder elements in a given array.
*
* @private
* @param {Array} array The array to inspect.
* @returns {Array} Returns a new array of placeholder indexes.
*/
function getHolders(array) {
var index = -1,
length = array.length,
result = [];
while (++index < length) {
if (array[index] === lodash) {
result.push(index);
}
}
return result;
}
/**
* Gets the appropriate "indexOf" function. If the `_.indexOf` method is
* customized this method returns the custom method, otherwise it returns
@@ -3375,7 +3466,7 @@
}
var delayed = function() {
var remaining = wait - (now() - stamp);
if (remaining <= 0) {
if (remaining <= 0 || remaining > wait) {
if (maxTimeoutId) {
clearTimeout(maxTimeoutId);
}
@@ -3420,7 +3511,7 @@
lastCalled = stamp;
}
var remaining = maxWait - (stamp - lastCalled),
isCalled = remaining <= 0;
isCalled = remaining <= 0 || remaining > maxWait;
if (isCalled) {
if (maxTimeoutId) {
@@ -5231,7 +5322,7 @@
// define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module
define('underscore', function() {
define(function() {
return lodash;
});
}