Skip to content

Commit 4c974f4

Browse files
committed
add _.cloneDeep
1 parent 20e7c6e commit 4c974f4

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

test/objects.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,41 @@
286286
assert.strictEqual(_.clone(null), null, 'non objects should not be changed by clone');
287287
});
288288

289+
QUnit.test('cloneDeep', function(assert) {
290+
var source = {
291+
str: 'str',
292+
num: 1,
293+
bool: false,
294+
list: [1, 2, 3],
295+
obj: {a: 'a', b: 'b'},
296+
nu: null,
297+
und: void 0
298+
};
299+
var clone = _.cloneDeep(source);
300+
assert.strictEqual(clone.str, 'str', 'can clone string value from source');
301+
assert.strictEqual(clone.num, 1, 'can clone number value from source');
302+
assert.strictEqual(clone.bool, false, 'can clone boolean value from source');
303+
assert.deepEqual(clone.list, [1, 2, 3], 'can clone array from source');
304+
assert.deepEqual(clone.obj, {a: 'a', b: 'b'}, 'can clone object from source');
305+
assert.deepEqual(clone.nu, null, 'can clone null value from source');
306+
assert.deepEqual(clone.und, void 0, 'can clone undefined value from source');
307+
308+
assert.notStrictEqual(clone.list, source.list, 'refers to a copy of the original array');
309+
assert.notStrictEqual(clone.obj, source.obj, 'refers to a copy of the original object');
310+
311+
function F() {}
312+
F.prototype = {a: 'a'};
313+
var subObj = new F();
314+
subObj.b = 'b';
315+
clone = _.cloneDeep(subObj);
316+
assert.deepEqual(clone, {a: 'a', b: 'b'}, 'copies all properties from source');
317+
318+
source = {a: 'a'};
319+
source.obj = source;
320+
clone = _.cloneDeep(source);
321+
assert.deepEqual(clone.obj.a, 'a', 'can deal with circular references cases');
322+
});
323+
289324
QUnit.test('create', function(assert) {
290325
var Parent = function() {};
291326
Parent.prototype = {foo: function() {}, bar: 2};

underscore.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,37 @@
11581158
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
11591159
};
11601160

1161+
// Create a (deep-cloned) duplicate of an object.
1162+
var cloneDeep = function(obj, stack) {
1163+
if (!_.isObject(obj)) {
1164+
return obj;
1165+
}
1166+
var keys = !isArrayLike(obj) && _.allKeys(obj),
1167+
length = (keys || obj).length,
1168+
result = keys ? {} : [];
1169+
1170+
if (!stack) {
1171+
stack = [[], []];
1172+
}
1173+
var stacked = _.indexOf(stack[0], obj);
1174+
if (stacked > -1) {
1175+
return stack[1][stacked];
1176+
}
1177+
stack[0].push(obj);
1178+
stack[1].push(result);
1179+
1180+
for (var i = 0; i < length; i++) {
1181+
var key = keys ? keys[i] : i;
1182+
result[key] = cloneDeep(obj[key], stack);
1183+
}
1184+
1185+
return result;
1186+
};
1187+
1188+
_.cloneDeep = function(obj) {
1189+
return cloneDeep(obj);
1190+
};
1191+
11611192
// Invokes interceptor with the obj, and then returns obj.
11621193
// The primary purpose of this method is to "tap into" a method chain, in
11631194
// order to perform operations on intermediate results within the chain.

0 commit comments

Comments
 (0)