Add createAssigner, defaultsOwn, and expand the callback args of _.assign and _.merge.

This commit is contained in:
John-David Dalton
2014-05-10 01:14:52 -07:00
parent e901208721
commit ee4a703407
2 changed files with 99 additions and 65 deletions

View File

@@ -248,6 +248,22 @@
return typeof objectValue == 'undefined' ? sourceValue : objectValue;
}
/**
* Used by `defaultsOwn` to customize its `_.assign` use.
*
* @private
* @param {*} objectValue The destination object property value.
* @param {*} sourceValue The source object property value.
* @param {string} key The key associated with the object and source values.
* @param {Object} object The destination object.
* @returns {*} Returns the value to assign to the destination object.
*/
function assignDefaultsOwn(objectValue, sourceValue, key, object) {
return (!hasOwnProperty.call(object, key) || typeof objectValue == 'undefined')
? sourceValue
: objectValue
}
/**
* The base implementation of `_.at` without support for strings or individual
* key arguments.
@@ -1814,17 +1830,17 @@
* @param {Array} [stackB=[]] Associates values with source counterparts.
*/
function baseMerge(object, source, callback, stackA, stackB) {
(isArray(source) ? arrayEach : baseForOwn)(source, function(source, key) {
(isArray(source) ? arrayEach : baseForOwn)(source, function(srcValue, key, source) {
var found,
isArr,
result = source,
result = srcValue,
value = object[key];
if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
if (srcValue && ((isArr = isArray(srcValue)) || isPlainObject(srcValue))) {
// avoid merging previously merged cyclic sources
var stackLength = stackA.length;
while (stackLength--) {
if ((found = stackA[stackLength] == source)) {
if ((found = stackA[stackLength] == srcValue)) {
value = stackB[stackLength];
break;
}
@@ -1832,7 +1848,7 @@
if (!found) {
var isShallow;
if (callback) {
result = callback(value, source);
result = callback(value, srcValue, key, object, source);
if ((isShallow = typeof result != 'undefined')) {
value = result;
}
@@ -1843,20 +1859,20 @@
: (isPlainObject(value) ? value : {});
}
// add `source` and associated `value` to the stack of traversed objects
stackA.push(source);
stackA.push(srcValue);
stackB.push(value);
// recursively merge objects and arrays (susceptible to call stack limits)
if (!isShallow) {
baseMerge(value, source, callback, stackA, stackB);
baseMerge(value, srcValue, callback, stackA, stackB);
}
}
}
else {
if (callback) {
result = callback(value, source);
result = callback(value, srcValue, key, object, source);
if (typeof result == 'undefined') {
result = source;
result = srcValue;
}
}
if (typeof result != 'undefined') {
@@ -2059,6 +2075,27 @@
};
}
/**
* Creates a function that assigns own enumerable properties of source
* object(s) to the destination object executing the callback to produce
* the assigned values. The callback is invoked with five arguments;
* (objectValue, sourceValue, key, object, source).
*
* @private
* @param {Function} [callback] The function to customize assigning values.
* @returns {Function} Returns the new assigner function.
*/
function createAssigner(callback) {
return function(object) {
if (!object) {
return object;
}
var args = slice(arguments);
args.push(callback);
return assign.apply(null, args);
};
}
/**
* Creates a cache object to optimize linear searches of large arrays.
*
@@ -2203,6 +2240,17 @@
: baseCreateWrapper(data);
}
/**
* This method is like `_.defaults` except that it ignores inherited
* property values when checking if a property is `undefined`.
*
* @private
* @param {Object} object The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns the destination object.
*/
var defaultsOwn = createAssigner(assignDefaultsOwn);
/**
* Finds the indexes of all placeholder elements in `array`.
*
@@ -5588,8 +5636,8 @@
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources will overwrite property assignments of previous
* sources. If a callback is provided it will be executed to produce the
* assigned values. The callback is bound to `thisArg` and invoked with two
* arguments; (objectValue, sourceValue).
* assigned values. The callback is bound to `thisArg` and invoked with
* five arguments; (objectValue, sourceValue, key, object, source).
*
* @static
* @memberOf _
@@ -5639,7 +5687,7 @@
while (++index < length) {
var key = props[index];
object[key] = callback ? callback(object[key], source[key]) : source[key];
object[key] = callback ? callback(object[key], source[key], key, object, source) : source[key];
}
}
return object;
@@ -5812,14 +5860,7 @@
* _.defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' });
* // => { 'name': 'barney', 'employer': 'slate' }
*/
function defaults(object) {
if (!object) {
return object;
}
var args = slice(arguments);
args.push(assignDefaults);
return assign.apply(null, args);
}
var defaults = createAssigner(assignDefaults);
/**
* This method is like `_.findIndex` except that it returns the key of the
@@ -6781,7 +6822,7 @@
* provided it will be executed to produce the merged values of the destination
* and source properties. If the callback returns `undefined` merging will
* be handled by the method instead. The callback is bound to `thisArg` and
* invoked with two arguments; (objectValue, sourceValue).
* invoked with five arguments; (objectValue, sourceValue, key, object, source).
*
* @static
* @memberOf _
@@ -7505,10 +7546,10 @@
// and Laura Doktorova's doT.js
// https://github.com/olado/doT
var settings = lodash.templateSettings;
options = defaults({}, options, settings);
options = defaultsOwn({}, options, settings);
string = String(string == null ? '' : string);
var imports = defaults({}, options.imports, settings.imports),
var imports = defaultsOwn({}, options.imports, settings.imports),
importsKeys = keys(imports),
importsValues = values(imports);