From 31a7ac1e817da0c25f024f9d11d94da86217dcf9 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Fri, 10 Jul 2015 00:28:49 -0700 Subject: [PATCH] Ensure `_.assign`, `_.extend`, `_.defaults`, and `_.merge` coerce values to objects. --- lodash.src.js | 39 +++++++++++++++++++-------------------- test/test.js | 13 ++++++++----- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lodash.src.js b/lodash.src.js index d78e63e77..fb46a8423 100644 --- a/lodash.src.js +++ b/lodash.src.js @@ -2989,6 +2989,7 @@ customizer = length < 3 ? undefined : customizer; length = 1; } + object = toObject(object); while (++index < length) { var source = sources[index]; if (source) { @@ -4017,7 +4018,12 @@ * @returns {*} Returns the value to assign to the destination object. */ function mergeDefaults(objectValue, sourceValue) { - return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults); + if (objectValue === undefined) { + return sourceValue; + } + return isObject(objectValue) + ? merge(objectValue, sourceValue, mergeDefaults) + : objectValue; } /** @@ -8575,9 +8581,6 @@ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } */ var merge = createAssigner(function baseMerge(object, source, customizer, stackA, stackB) { - if (!isObject(object)) { - return object; - } var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), props = isSrcArr ? undefined : keysIn(source); @@ -8639,9 +8642,11 @@ */ var assign = createAssigner(function(object, source, customizer) { var props = keys(source); - return customizer - ? copyObjectWith(source, props, customizer, object) - : copyObject(source, props, object) + if (customizer) { + copyObjectWith(source, props, customizer, object); + } else { + copyObject(source, props, object); + } }); /** @@ -8705,11 +8710,7 @@ * // => { 'user': 'barney', 'age': 36 } */ var defaults = restParam(function(args) { - var object = args[0]; - if (object == null) { - return object; - } - args.push(extendDefaults); + args.push(undefined, extendDefaults); return extend.apply(undefined, args); }); @@ -8732,11 +8733,7 @@ * */ var defaultsDeep = restParam(function(args) { - var object = args[0]; - if (object == null) { - return object; - } - args.push(mergeDefaults); + args.push(undefined, mergeDefaults); return merge.apply(undefined, args); }); @@ -8758,9 +8755,11 @@ */ var extend = createAssigner(function(object, source, customizer) { var props = keysIn(source); - return customizer - ? copyObjectWith(source, props, customizer, object) - : copyObject(source, props, object) + if (customizer) { + copyObjectWith(source, props, customizer, object); + } else { + copyObject(source, props, object); + } }); /** diff --git a/test/test.js b/test/test.js index e1dbf8cab..93d9caf28 100644 --- a/test/test.js +++ b/test/test.js @@ -5058,12 +5058,15 @@ isAssign = methodName == 'assign', isDefaults = methodName == 'defaults'; - test('`_.' + methodName + '` should pass thru falsey `object` values', 1, function() { + test('`_.' + methodName + '` should coerce primitives to objects', 1, function() { + var expected = _.map(falsey, _.constant(true)); + var actual = _.map(falsey, function(object, index) { - return index ? func(object) : func(); + var result = index ? func(object) : func(); + return _.isEqual(result, Object(object)); }); - deepEqual(actual, falsey); + deepEqual(actual, expected); }); test('`_.' + methodName + '` should assign own ' + (isAssign ? '' : 'and inherited ') + 'source properties', 1, function() { @@ -5122,7 +5125,7 @@ var actual = _.map([null, undefined], function(value) { try { - return _.isEqual(func(value, { 'a': 1 }), value); + return _.isEqual(func(value, { 'a': 1 }), {}); } catch(e) { return false; } @@ -11667,7 +11670,7 @@ expected = { 'a': { 'b': 1, 'c': 3 } }; var defaultsDeep = _.partialRight(_.merge, function deep(value, other) { - return _.merge(value, other, deep); + return _.isObject(value) ? _.merge(value, other, deep) : value; }); deepEqual(defaultsDeep(object, source), expected);