Make _.defaultsDeep and _.merge consistent with _.defaults in assigning undefined values. [closes #2676]

This commit is contained in:
John-David Dalton
2016-09-26 21:27:33 -07:00
parent c1c394ac60
commit b678bb4a74
2 changed files with 49 additions and 19 deletions

View File

@@ -2462,7 +2462,7 @@
*/ */
function assignMergeValue(object, key, value) { function assignMergeValue(object, key, value) {
if ((value !== undefined && !eq(object[key], value)) || if ((value !== undefined && !eq(object[key], value)) ||
(typeof key == 'number' && value === undefined && !(key in object))) { (value === undefined && !(key in object))) {
baseAssignValue(object, key, value); baseAssignValue(object, key, value);
} }
} }
@@ -3616,29 +3616,32 @@
var isCommon = newValue === undefined; var isCommon = newValue === undefined;
if (isCommon) { if (isCommon) {
var isArr = isArray(srcValue),
isTyped = !isArr && isTypedArray(srcValue);
newValue = srcValue; newValue = srcValue;
if (isArray(srcValue) || isTypedArray(srcValue)) { if (isArr || isTyped) {
if (isArray(objValue)) { if (isArray(objValue)) {
newValue = objValue; newValue = objValue;
} }
else if (isArrayLikeObject(objValue)) { else if (isArrayLikeObject(objValue)) {
newValue = copyArray(objValue); newValue = copyArray(objValue);
} }
else { else if (isTyped) {
isCommon = false; isCommon = false;
newValue = baseClone(srcValue, true); newValue = cloneTypedArray(srcValue, true);
}
else {
newValue = [];
} }
} }
else if (isPlainObject(srcValue) || isArguments(srcValue)) { else if (isPlainObject(srcValue) || isArguments(srcValue)) {
newValue = objValue;
if (isArguments(objValue)) { if (isArguments(objValue)) {
newValue = toPlainObject(objValue); newValue = toPlainObject(objValue);
} }
else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
isCommon = false; newValue = initCloneObject(srcValue);
newValue = baseClone(srcValue, true);
}
else {
newValue = objValue;
} }
} }
else { else {

View File

@@ -4512,15 +4512,20 @@
QUnit.test('should assign source properties if missing on `object`', function(assert) { QUnit.test('should assign source properties if missing on `object`', function(assert) {
assert.expect(1); assert.expect(1);
assert.deepEqual(_.defaults({ 'a': 1 }, { 'a': 2, 'b': 2 }), { 'a': 1, 'b': 2 }); var actual = _.defaults({ 'a': 1 }, { 'a': 2, 'b': 2 });
assert.deepEqual(actual, { 'a': 1, 'b': 2 });
}); });
QUnit.test('should accept multiple sources', function(assert) { QUnit.test('should accept multiple sources', function(assert) {
assert.expect(2); assert.expect(2);
var expected = { 'a': 1, 'b': 2, 'c': 3 }; var expected = { 'a': 1, 'b': 2, 'c': 3 },
assert.deepEqual(_.defaults({ 'a': 1, 'b': 2 }, { 'b': 3 }, { 'c': 3 }), expected); actual = _.defaults({ 'a': 1, 'b': 2 }, { 'b': 3 }, { 'c': 3 });
assert.deepEqual(_.defaults({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 3 }, { 'c': 2 }), expected);
assert.deepEqual(actual, expected);
actual = _.defaults({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 3 }, { 'c': 2 });
assert.deepEqual(actual, expected);
}); });
QUnit.test('should not overwrite `null` values', function(assert) { QUnit.test('should not overwrite `null` values', function(assert) {
@@ -4537,6 +4542,15 @@
assert.strictEqual(actual.a, 1); assert.strictEqual(actual.a, 1);
}); });
QUnit.test('should assign `undefined` values', function(assert) {
assert.expect(1);
var source = { 'a': undefined, 'b': 1 },
actual = _.defaults({}, source);
assert.deepEqual(actual, { 'a': undefined, 'b': 1 });
});
QUnit.test('should assign properties that shadow those on `Object.prototype`', function(assert) { QUnit.test('should assign properties that shadow those on `Object.prototype`', function(assert) {
assert.expect(2); assert.expect(2);
@@ -4560,8 +4574,11 @@
'valueOf': 7 'valueOf': 7
}; };
assert.deepEqual(_.defaults({}, source), source); var expected = lodashStable.clone(source);
assert.deepEqual(_.defaults({}, object, source), object); assert.deepEqual(_.defaults({}, source), expected);
expected = lodashStable.clone(object);
assert.deepEqual(_.defaults({}, object, source), expected);
}); });
}()); }());
@@ -4633,6 +4650,16 @@
assert.strictEqual(actual.a.b, 2); assert.strictEqual(actual.a.b, 2);
}); });
QUnit.test('should assign `undefined` values', function(assert) {
assert.expect(1);
var source = { 'a': undefined, 'b': { 'c': undefined, 'd': 1 } },
expected = lodashStable.cloneDeep(source),
actual = _.defaultsDeep({}, source);
assert.deepEqual(actual, expected);
});
QUnit.test('should merge sources containing circular references', function(assert) { QUnit.test('should merge sources containing circular references', function(assert) {
assert.expect(2); assert.expect(2);
@@ -4672,7 +4699,7 @@
assert.expect(1); assert.expect(1);
var actual = _.defaultsDeep({ 'a': ['abc'] }, { 'a': 'abc' }); var actual = _.defaultsDeep({ 'a': ['abc'] }, { 'a': 'abc' });
assert.deepEqual(actual, { 'a': ['abc'] }); assert.deepEqual(actual.a, ['abc']);
}); });
}()); }());
@@ -14993,14 +15020,14 @@
assert.deepEqual(actual, [new Foo(object)]); assert.deepEqual(actual, [new Foo(object)]);
}); });
QUnit.test('should not assign `undefined` values', function(assert) { QUnit.test('should not overwrite existing values with `undefined` values of object sources', function(assert) {
assert.expect(1); assert.expect(1);
var actual = _.merge({ 'a': 1 }, { 'a': undefined, 'b': undefined }); var actual = _.merge({ 'a': 1 }, { 'a': undefined, 'b': undefined });
assert.deepEqual(actual, { 'a': 1 }); assert.deepEqual(actual, { 'a': 1, 'b': undefined });
}); });
QUnit.test('should skip `undefined` values in array sources if a destination value exists', function(assert) { QUnit.test('should not overwrite existing values with `undefined` values of array sources', function(assert) {
assert.expect(2); assert.expect(2);
var array = [1]; var array = [1];