Add bindCallback and setCallbackData.

This commit is contained in:
John-David Dalton
2014-12-26 13:34:17 -06:00
parent 4e73daab79
commit ceaa2c1619

220
lodash.js
View File

@@ -1789,71 +1789,35 @@
}
/**
* The base implementation of `_.callback`.
* The base implementation of `_.callback` which supports specifying the
* number of arguments to provide to `func`.
*
* @private
* @param {*} [func=_.identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {number} [argCount] The number of arguments the callback accepts.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {number} [argCount] The number of arguments to provide to `func`.
* @returns {Function} Returns the new function.
*/
function baseCallback(func, thisArg, argCount) {
var type = typeof func;
if (type == 'function') {
if (typeof thisArg == 'undefined') {
return func;
}
var data = getData(func);
var data = typeof thisArg != 'undefined' && getData(func);
if (typeof data == 'undefined') {
var support = lodash.support;
if (support.funcNames) {
data = !func.name;
}
data = data || !support.funcDecomp;
if (!data) {
var source = fnToString.call(func);
if (!support.funcNames) {
data = !reFuncName.test(source);
}
if (!data) {
// Check if `func` references the `this` keyword and store the result.
data = reThis.test(source) || isNative(func);
baseSetData(func, data);
}
}
setCallbackData(func);
}
// Exit early if there are no `this` references or `func` is bound.
if (data === false || (data !== true && data[1] & BIND_FLAG)) {
return func;
}
switch (argCount) {
case 1: return function(value) {
return func.call(thisArg, value);
};
case 3: return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(thisArg, accumulator, value, index, collection);
};
case 5: return function(value, other, key, object, source) {
return func.call(thisArg, value, other, key, object, source);
};
}
return function() {
return func.apply(thisArg, arguments);
};
// Avoid binding if there are no `this` references or `func` is already bound.
return (data === false || (data !== true && data[1] & BIND_FLAG))
? func
: bindCallback(func, thisArg, argCount);
}
if (func == null) {
return identity;
}
// Handle "_.pluck" and "_.where" style callback shorthands.
if (type == 'object') {
// baseMatches
return matches(func);
}
return (argCount ? baseProperty : property)(func);
return type == 'object'
? baseMatches(func, argCount)
: baseProperty(argCount ? String(func) : func);
}
/**
@@ -2404,11 +2368,11 @@
* @param {Object} source The object to inspect.
* @param {Array} props The source property names to match.
* @param {Array} values The source values to match.
* @param {Array} strictCompareFlags Strict comparison flags for source values.
* @param {Function} [customizer] The function to customize comparing objects.
* @param {Array} [strictCompareFlags=[]] Strict comparison flags for source values.
* @returns {boolean} Returns `true` if `object` is a match, else `false`.
*/
function baseIsMatch(object, props, values, customizer, strictCompareFlags) {
function baseIsMatch(object, props, values, strictCompareFlags, customizer) {
var index = -1,
length = props.length;
@@ -2463,6 +2427,45 @@
return result;
}
/**
* The base implementation of `_.matches` which supports specifying whether
* `source` is cloned.
*
* @private
* @param {Object} source The object of property values to match.
* @param {boolean} [isCloned] Specify cloning the source object.
* @returns {Function} Returns the new function.
*/
function baseMatches(source, isCloned) {
var props = keys(source),
length = props.length;
if (length == 1) {
var key = props[0],
value = source[key];
if (isStrictComparable(value)) {
return function(object) {
return object != null && value === object[key] && hasOwnProperty.call(object, key);
};
}
}
var notCloned = !isCloned,
values = Array(length),
strictCompareFlags = Array(length);
while (length--) {
value = source[props[length]];
var isStrict = isStrictComparable(value);
values[length] = (isStrict || notCloned) ? value : baseClone(value, true, clonePassthru);
strictCompareFlags[length] = isStrict;
}
return function(object) {
return baseIsMatch(object, props, values, strictCompareFlags);
};
}
/**
* The base implementation of `_.merge` without support for argument juggling,
* multiple sources, and `this` binding `customizer` functions.
@@ -2527,7 +2530,7 @@
}
/**
* The base implementation of `_.property` which doesn't coerce `key` to a string.
* The base implementation of `_.property` which does not coerce `key` to a string.
*
* @private
* @param {string} key The name of the property to retrieve.
@@ -2770,6 +2773,36 @@
return high;
}
/**
* A specialized version of `_.bind` for callbacks with support for specifying
* the number of arguments to provide to `func`.
*
* @private
* @param {Function} func The function to bind.
* @param {*} thisArg The `this` binding of `func`.
* @param {number} [argCount] The number of arguments to provide to `func`.
* @returns {Function} Returns the new function.
*/
function bindCallback(func, thisArg, argCount) {
switch (argCount) {
case 1: return function(value) {
return func.call(thisArg, value);
};
case 3: return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(thisArg, accumulator, value, index, collection);
};
case 5: return function(value, other, key, object, source) {
return func.call(thisArg, value, other, key, object, source);
};
}
return function() {
return func.apply(thisArg, arguments);
};
}
/**
* Creates a clone of the given array buffer.
*
@@ -3220,7 +3253,7 @@
}
/**
* A specialized version of `baseIsEqualDeep`, for arrays-only, which supports
* A specialized version of `baseIsEqualDeep` for arrays with support for
* partial deep comparisons.
*
* @private
@@ -3312,7 +3345,7 @@
}
/**
* A specialized version of `baseIsEqualDeep`, for objects-only, which supports
* A specialized version of `baseIsEqualDeep` for objects with support for
* partial deep comparisons.
*
* @private
@@ -3772,6 +3805,34 @@
};
}());
/**
* A specialized version of `setData` for callbacks which sets metadata to
* indicate whether `func` is eligible for `this` binding.
*
* @private
* @param {Function} func The function to associate metadata with.
* @returns {Function} Returns `func`.
*/
function setCallbackData(func) {
var support = lodash.support;
if (support.funcNames) {
var data = !func.name;
}
data = data || !support.funcDecomp;
if (!data) {
var source = fnToString.call(func);
if (!support.funcNames) {
data = !reFuncName.test(source);
}
if (!data) {
// Check if `func` references the `this` keyword and store the result.
data = reThis.test(source) || isNative(func);
baseSetData(func, data);
}
}
return func;
}
/**
* A fallback implementation of `_.isPlainObject` which checks if `value`
* is an object created by the `Object` constructor or has a `[[Prototype]]`
@@ -6623,7 +6684,7 @@
* @memberOf _
* @category Function
* @param {Function} func The function to bind.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {*} thisArg The `this` binding of `func`.
* @param {...*} [args] The arguments to be partially applied.
* @returns {Function} Returns the new bound function.
* @example
@@ -7910,11 +7971,13 @@
* // => true
*/
function isMatch(object, source, customizer, thisArg) {
var props = keys(source);
var props = keys(source),
length = props.length;
if (typeof customizer == 'function') {
customizer = baseCallback(customizer, thisArg, 3);
}
else if (props.length == 1) {
else if (length == 1) {
var key = props[0],
value = source[key];
@@ -7922,8 +7985,14 @@
return object != null && value === object[key] && hasOwnProperty.call(object, key);
}
}
var values = baseValues(source, props);
return baseIsMatch(object, props, values, customizer);
var values = Array(length),
strictCompareFlags = Array(length);
while (length--) {
value = values[length] = source[props[length]];
strictCompareFlags[length] = isStrictComparable(value);
}
return baseIsMatch(object, props, values, strictCompareFlags, customizer);
}
/**
@@ -9912,7 +9981,7 @@
* @alias iteratee
* @category Utility
* @param {*} [func=_.identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {*} [thisArg] The `this` binding of `func`.
* @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
* @returns {Function} Returns the new function.
* @example
@@ -10008,36 +10077,7 @@
* // => { 'user': 'barney', 'age': 36 }
*/
function matches(source) {
var props = keys(source),
length = props.length;
if (length == 1) {
var key = props[0],
value = source[key];
if (isStrictComparable(value)) {
return function(object) {
return object != null && value === object[key] && hasOwnProperty.call(object, key);
};
}
}
var values = Array(length),
strictCompareFlags = Array(length);
while (length--) {
value = source[props[length]];
var isStrict = isStrictComparable(value);
values[length] = isStrict ? value : baseClone(value, true, clonePassthru);
strictCompareFlags[length] = isStrict;
}
return baseMatches(props, values, strictCompareFlags);
}
function baseMatches(source, props, values, strictCompareFlags) {
return function(object) {
return baseIsMatch(object, props, values, null, strictCompareFlags);
};
return baseMatches(source, true);
}
/**