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 * @private
* @param {*} [func=_.identity] The value to convert to a callback. * @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 {number} [argCount] The number of arguments the callback accepts. * @param {number} [argCount] The number of arguments to provide to `func`.
* @returns {Function} Returns the new function. * @returns {Function} Returns the new function.
*/ */
function baseCallback(func, thisArg, argCount) { function baseCallback(func, thisArg, argCount) {
var type = typeof func; var type = typeof func;
if (type == 'function') { if (type == 'function') {
if (typeof thisArg == 'undefined') { var data = typeof thisArg != 'undefined' && getData(func);
return func;
}
var data = getData(func);
if (typeof data == 'undefined') { if (typeof data == 'undefined') {
var support = lodash.support; setCallbackData(func);
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);
}
}
} }
// Exit early if there are no `this` references or `func` is bound. // Avoid binding if there are no `this` references or `func` is already bound.
if (data === false || (data !== true && data[1] & BIND_FLAG)) { return (data === false || (data !== true && data[1] & BIND_FLAG))
return func; ? func
} : 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);
};
} }
if (func == null) { if (func == null) {
return identity; return identity;
} }
// Handle "_.pluck" and "_.where" style callback shorthands. // Handle "_.pluck" and "_.where" style callback shorthands.
if (type == 'object') { return type == 'object'
// baseMatches ? baseMatches(func, argCount)
return matches(func); : baseProperty(argCount ? String(func) : func);
}
return (argCount ? baseProperty : property)(func);
} }
/** /**
@@ -2404,11 +2368,11 @@
* @param {Object} source The object to inspect. * @param {Object} source The object to inspect.
* @param {Array} props The source property names to match. * @param {Array} props The source property names to match.
* @param {Array} values The source values 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 {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`. * @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, var index = -1,
length = props.length; length = props.length;
@@ -2463,6 +2427,45 @@
return result; 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, * The base implementation of `_.merge` without support for argument juggling,
* multiple sources, and `this` binding `customizer` functions. * 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 * @private
* @param {string} key The name of the property to retrieve. * @param {string} key The name of the property to retrieve.
@@ -2770,6 +2773,36 @@
return high; 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. * 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. * partial deep comparisons.
* *
* @private * @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. * partial deep comparisons.
* *
* @private * @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` * A fallback implementation of `_.isPlainObject` which checks if `value`
* is an object created by the `Object` constructor or has a `[[Prototype]]` * is an object created by the `Object` constructor or has a `[[Prototype]]`
@@ -6623,7 +6684,7 @@
* @memberOf _ * @memberOf _
* @category Function * @category Function
* @param {Function} func The function to bind. * @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. * @param {...*} [args] The arguments to be partially applied.
* @returns {Function} Returns the new bound function. * @returns {Function} Returns the new bound function.
* @example * @example
@@ -7910,11 +7971,13 @@
* // => true * // => true
*/ */
function isMatch(object, source, customizer, thisArg) { function isMatch(object, source, customizer, thisArg) {
var props = keys(source); var props = keys(source),
length = props.length;
if (typeof customizer == 'function') { if (typeof customizer == 'function') {
customizer = baseCallback(customizer, thisArg, 3); customizer = baseCallback(customizer, thisArg, 3);
} }
else if (props.length == 1) { else if (length == 1) {
var key = props[0], var key = props[0],
value = source[key]; value = source[key];
@@ -7922,8 +7985,14 @@
return object != null && value === object[key] && hasOwnProperty.call(object, key); return object != null && value === object[key] && hasOwnProperty.call(object, key);
} }
} }
var values = baseValues(source, props); var values = Array(length),
return baseIsMatch(object, props, values, customizer); 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 * @alias iteratee
* @category Utility * @category Utility
* @param {*} [func=_.identity] The value to convert to a callback. * @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`. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
* @returns {Function} Returns the new function. * @returns {Function} Returns the new function.
* @example * @example
@@ -10008,36 +10077,7 @@
* // => { 'user': 'barney', 'age': 36 } * // => { 'user': 'barney', 'age': 36 }
*/ */
function matches(source) { function matches(source) {
var props = keys(source), return baseMatches(source, true);
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);
};
} }
/** /**