0%

Deepcopy in Javascript

This article is about how to deep copy an object/multi-dimension array in Javascript. The above requirements are quite common in our daily development while there is not a good official implement on that topic. This might because that the class/function system with prototype is quite a mass thing in Javascript and a script-like language is commonly considered to be non-deep-copied. Instead, they will follow the rule of passing by references.

Here, we will dive into some ways of doing deep-copy:

JSON Object in ES5

One commonly used way is to use JSON.stringify(obj) to parse an object to string and use JSON.parse(str) to reparse the string to a new object. It is a simple solution while some bugs are here.


First: it can not deal with a circular reference like:



let a = {pwd: '****'}
a.a = a;

JSON.stringify(a) // it will return Uncaught TypeError: Converting circular structure to JSON



Secondly, the JSON parser won't deal with the functions and prototypes inside the class. So basically the only remaining thing is the attribute. That is because of the limited types that JSON format is supported.


Recursive deep-copy

This might be the universal solution if we can consider all the data types in Javascript (which is hard if we are not the one who implemented the class). Let's look into how to implement it first:


let deepcopy = (obj) => {
let _deepcopy = (obj) => {
if (!obj || typeof obj !== 'object') return obj;
let objCopy = Array.isArray(obj) ? [] : {}; // if it is an array

Object.keys(obj).forEach(key => {
if (objCopy[key]) return;
objCopy[key] = _deepCopy(obj[key])
});

objCopy.__proto__ = obj.__proto__; // copy functions and prototypes

return objCopy;
}

}

The above code is quite implementation of basic datatype deepcopy and functions. If the class involves with some newly defined types like map and set, you may also want to deal with them by adding some codes to recursive copying them.


Simple solution for Array

If you are dealing with array, the life will be much easier. You can simply call .concat() or .slice() to do the deepcopy. One thing needs to remember, if this is a multi-dim array, you may want to recursively call the above functions to deepcopy every dimention's array.



let copyArray = (arr) => {
let _copyArray = (arr) => {
let curr = [];

arr.forEach((it) => {
if (Array.isArray(it)) {
curr.push(_copyArray(it));
} else {
curr.push(it);
}
});

return curr;
}
return _copyArray(arr);
}