Deep clone an object array in JavaScript is not simple. Because it still brings the reference of previous object. This could lead a problem in your application. So in this topic, I will divide into two ways:

Bad Practice

The most best answer in Stackoverflow, to deep clone an object javascript is just like this:

1
JSON.parse(JSON.stringify());

Then why this is bad practice?

Convertion Problem

The problem is JSON.stringify() cloud lead into wrong convertion. Because it just convert the javascript Object into String. You can not control for spesific instance inside the object.

Example:

1
2
3
4
JSON.stringify({ key: undefined });
JSON.stringify({ key: Symbol() });
JSON.stringify({ key: function(){} });
// all will be converted to just "{}"

Note:

  • Date() will also parsed to string, so you will lose it.

Not Eficient (very slow)

I have test to deep clone 1 million object and this take higher than 1 seconds (1823ms). This will freeze your application since this way is blocking (synchronous). You have to be more tricky to avoid this blocking for handle big object.

Good Practice

We should go back to use primitive way by using loop and recursion.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(array) {
var clone, i;
// if parameter is not array or object then just return it so you will not lose spesific instance.
// ex. Date(), Symbol(), function(), etc.
if (typeof array !== 'object' || !array) return array;
// if object is array then convert this to string
if ('[object Array]' === Object.prototype.toString.apply(array)) {
clone = [];
var len = array.length;
for (i = 0; i < len; i++) clone[i] = deepClone(array[i]);
return clone;
}
// handle object (skip if doesn't have property)
clone = {};
for (i in array) if (array.hasOwnProperty(i)) clone[i] = deepClone(array[i]);
return clone;
}

Then why this is good practice?

Easy to modify

Because using loop you can put any condition inside the logic.

5x faster

This is 5x faster comparing with JSON.parse(JSON.stringify()). I have test this with 1 million object and it just takes 150ms.

Benchmark

Before I write this topic, I have research carefully about deepClone, especially for the performance in NodeJS.

json-parse-stringify.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var array = [];
var clone = [];
var start = 1;
var end = 1000000;

console.log(new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log('Build '+end+' object');

function buildObject() {
return new Promise((resolve,reject) => {
for(var i =start;i<=end;i++) {
array.push({ id:i, name:'user_'+i });
}
resolve();
});
}

buildObject().then(resolve => {
console.log(new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log(array.length+' object has been build');
console.log('======');
return resolve;
}).then(resolve => {
console.log('Start clone '+array.length+' object: '+new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
var time_start = Date.now();
clone = JSON.parse(JSON.stringify(array));
var time_end = Date.now();
console.log('Finish clone '+array.length+' object: '+new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log('Total execution time: '+ (time_end-time_start)+'ms');
console.log('======');
return resolve;
});

loop-recursive.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var array = [];
var clone = [];

var start = 1;
var end = 1000000;

console.log(new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log('Build '+end+' object');

function buildObject() {
return new Promise((resolve,reject) => {
for(var i =start;i<=end;i++) {
array.push({ id:i, name:'user_'+i });
}
resolve();
});
}

function deepClone(array) {
var clone, i;
// if parameter is not array or object then just return it so you will not lose spesific instance.
// ex. Date(), Symbol(), function(), etc.
if (typeof array !== 'object' || !array) return array;
// if object is array then convert this to string
if ('[object Array]' === Object.prototype.toString.apply(array)) {
clone = [];
var len = array.length;
for (i = 0; i < len; i++) clone[i] = deepClone(array[i]);
return clone;
}
// handle object (skip if doesn't have property)
clone = {};
for (i in array) if (array.hasOwnProperty(i)) clone[i] = deepClone(array[i]);
return clone;
}

buildObject().then(resolve => {
console.log(new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log(array.length+' object has been build');
console.log('======');
return resolve;
}).then(resolve => {
console.log('Start clone '+array.length+' object: '+new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
var time_start = Date.now();
clone = deepClone(array);
var time_end = Date.now();
console.log('Finish clone '+array.length+' object: '+new Date().toISOString().replace('T',' ').replace('Z','').substr(0,19));
console.log('Total execution time: '+ (time_end-time_start)+'ms');
console.log('======');
return resolve;
});

Conclusion

I know there is another way to deepClone an object in javascript. You could using jQuery or Lodash, but the performance is very bad. I recommend you to using native way.

So this is only small research from me about deep clone object in javascript. The decision is going back to yours.

Thank you.