Skip to content

Commit

Permalink
add _.cloneDeep
Browse files Browse the repository at this point in the history
  • Loading branch information
wy1009 committed Jan 29, 2018
1 parent 20e7c6e commit 4c974f4
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
35 changes: 35 additions & 0 deletions test/objects.js
Expand Up @@ -286,6 +286,41 @@
assert.strictEqual(_.clone(null), null, 'non objects should not be changed by clone');
});

QUnit.test('cloneDeep', function(assert) {
var source = {
str: 'str',
num: 1,
bool: false,
list: [1, 2, 3],
obj: {a: 'a', b: 'b'},
nu: null,
und: void 0
};
var clone = _.cloneDeep(source);
assert.strictEqual(clone.str, 'str', 'can clone string value from source');
assert.strictEqual(clone.num, 1, 'can clone number value from source');
assert.strictEqual(clone.bool, false, 'can clone boolean value from source');
assert.deepEqual(clone.list, [1, 2, 3], 'can clone array from source');
assert.deepEqual(clone.obj, {a: 'a', b: 'b'}, 'can clone object from source');
assert.deepEqual(clone.nu, null, 'can clone null value from source');
assert.deepEqual(clone.und, void 0, 'can clone undefined value from source');

assert.notStrictEqual(clone.list, source.list, 'refers to a copy of the original array');
assert.notStrictEqual(clone.obj, source.obj, 'refers to a copy of the original object');

function F() {}
F.prototype = {a: 'a'};
var subObj = new F();
subObj.b = 'b';
clone = _.cloneDeep(subObj);
assert.deepEqual(clone, {a: 'a', b: 'b'}, 'copies all properties from source');

source = {a: 'a'};
source.obj = source;
clone = _.cloneDeep(source);
assert.deepEqual(clone.obj.a, 'a', 'can deal with circular references cases');
});

QUnit.test('create', function(assert) {
var Parent = function() {};
Parent.prototype = {foo: function() {}, bar: 2};
Expand Down
31 changes: 31 additions & 0 deletions underscore.js
Expand Up @@ -1158,6 +1158,37 @@
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

// Create a (deep-cloned) duplicate of an object.
var cloneDeep = function(obj, stack) {
if (!_.isObject(obj)) {
return obj;
}
var keys = !isArrayLike(obj) && _.allKeys(obj),
length = (keys || obj).length,
result = keys ? {} : [];

if (!stack) {
stack = [[], []];
}
var stacked = _.indexOf(stack[0], obj);
if (stacked > -1) {
return stack[1][stacked];
}
stack[0].push(obj);
stack[1].push(result);

for (var i = 0; i < length; i++) {
var key = keys ? keys[i] : i;
result[key] = cloneDeep(obj[key], stack);
}

return result;
};

_.cloneDeep = function(obj) {
return cloneDeep(obj);
};

// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
Expand Down

0 comments on commit 4c974f4

Please sign in to comment.