Implementation of JavaScript deep replication (deep clone)

  • 2020-12-22 17:33:30
  • OfStack

Within the code reuse pattern there is one called the copy property pattern (copying properties pattern). When it comes to code reuse, it's likely to be code inheritance (inheritance) in mind, but it's important to keep in mind its ultimate goal - we want to reuse code. Inheritance is only one means of code reuse, not the only one. The replication attribute is also a reuse mode, which is different from inheritance. In this pattern, an object will retrieve a member from another object by simply copying it. As anyone who has used jQuery knows, it has a $.extend () method that can be used to copy attributes in addition to extending the third party plug-in. Let's take a look at the implementation code of an extend() function (note that this is not the source code of jQuery, but a simple example) :


function extend(parent, child) {
var i;
// If no control is passed in 2 parameter child
// So create 1 A new object 
child = child || {}; 
// traverse parent All properties of an object 
// And filter the properties on the prototype 
// Then copy its own properties to child On the object 
for(i in parent) {
if(parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
// Return target object child
return child;
} 

The above code is a simple implementation that simply iterates over the members of the parent object and copies them into the child objects. Let's test 1 using the extend() method above:


var dad = {name: "Adam"};
var kid = extend(dad);
console.log(kid.name); //Adam 

We found that the extend() method already worked. There is one problem, however, and what is given above is a so-called shallow copy (shallow clone). In the case of shallow replication, if you change the property of a child object and that property happens to be an object, then the parent object will also be modified, but in many cases this is not what we want. Consider the following:


var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extend(dad) // call extend() Method will be dad Attributes to kid The above 
kid.counts.push(4); // the 4 Appended to the kid.counts In an array 
console.log(dad.counts); //[1, 2, 3, 4] 

In the above example, we can see that after modifying the kid.counts attribute (adding element 4 to it), dad.counts is also affected. This is because in the case of shallow replication, since objects are passed by reference, kid.counts and dad.counts point to the same array (or in memory they point to the same heap address).

Next, let's modify the extend() function to achieve deep replication. All we need to do is check every property of the parent object, and recursively reproduce the property of the parent object if it happens to be an object. In addition, you also need to check if the object is an array because the array literal is created differently than the object literal, which is [], and which is {}. The test array can be tested using the Object.prototype.toString () method, which returns "[object Array]" if it is an array. Let's take a look at the deep copy version of the extend() function:


function extendDeep(parent, child) {
child = child || {};
for(var i in parent) {
if(parent.hasOwnProperty(i)) {
// Checks whether the current property is an object 
if(typeof parent[i] === "object") {
// If the current property is an object, it also checks to see if it is an array 
// This is because the literal representation of an array is different from the literal representation of an object 
// The former is [], While the latter is {}
child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
// Recursive calls extend
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
} 

Now that the function for deep replication is written, let's test 1 to see if it works as expected, that is, if deep replication can be achieved:


var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
// Modify the kid the counts Properties and reads attribute 
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

From the above example, we can see that even if the child objects kid.counts and kid.reads are modified, the parent objects dad.counts and kid.reads are not changed, so our purpose is achieved.

The following is a summary of the basic ideas for implementing deep replication:

1. Check whether the current property is an object

2. Since an array is a special object, it is necessary to check whether an attribute is an array on the premise that it is an object.

3. If it is an array, create 1 [] empty array; otherwise, create 1 {} empty object and assign to the current property of the child object. Then, the extendDeep function is called recursively.

The above example allows us to implement our own method of deep replication using recursive algorithms. In fact, the new JSON object in ES5 also provides two methods for deep replication, JSON.stringify () and JSON.parse (); The former is used to turn an object into a string, and the latter to turn a string into an object. Here we use this method to implement a function of deep replication:


function extendDeep(parent, child) {
var i,
proxy;
proxy = JSON.stringify(parent); // the parent Object to a string 
proxy = JSON.parse(proxy) // To convert a string to an object, this is parent the 1 A copy of the 
child = child || {};
for(i in proxy) {
if(proxy.hasOwnProperty(i)) {
child[i] = proxy[i];
}
}
proxy = null; // because proxy Is an intermediate object that can be reclaimed 
return child;
} 

Here are the test examples:


var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
// Modify the kid the counts Properties and reads attribute 
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

The tests found that it also implemented deep replication. The latter approach is generally recommended because JSON.parse and ES91en.stringify are built-in functions that can be processed faster. In addition, the previous method uses recursive calls, and we all know that recursion is a less efficient algorithm.

About JavaScript deep copy (deep clone) implementation method to introduce so much to you, I hope to help you!


Related articles: