There are a lot of discussions on this topic on the Internet. I sorted it out myself according to various situations. Finally, I can approach the perfect realization of deep copy. Welcome to discuss.
Objects in javascript are reference types. When copying objects, you should consider whether to use shallow copy or deep copy.
Direct assignment
An object is a reference type. If it is directly assigned to another object, only one reference will be assigned. In fact, the two variables point to the same data object. If the properties of one object change, the other will also change.
Example 1, simple example:
let human1 = { id: 1, name: "happy" }; human2 = human1; // Here is direct assignment console.log(human1); // {id: 1, name: 'happy'} console.log(human2); // {id: 1, name: 'happy'} // Changing the name of human1 will also change the name of human2 human1.name = "life"; console.log(human1); // {id: 1, name: 'life'} console.log(human2); // {id: 1, name: 'life'}
Example 2: passing an object as a parameter is also a passing reference:
let human1 = { id: 1, name: "happy" }; console.log(human1); // {id: 1, name: 'happy'} function foo(human) { // The name of the human object has been changed here human.name = "life"; } foo(human1); // A passing object is a passing reference console.log(human1); // {id: 1, name: 'life'}
Shallow copy
Shallow copy only copies the first layer of the object. If the attribute value of the first layer is an object, the attribute only copies a reference.
let object1 = { a: 1, b: { // b is the object b1: 2 } }; object2 = Object.assign({}, object1); // Here is the shallow copy, in which the b object only copies the reference // A is a regular type and will not affect each other object1.a = 10; console.log(object1.a); // 10 console.log(object2.a); // 1 // b is an object that affects each other object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 20
If you want to achieve full replication, you need to use deep copy.
Deep copy
Sen copy means that not only one layer should be copied, but also the inner layer should be copied if it is an object.
1. Method of JSON object
If the object can be confirmed as a JSON object, you can use the method of JSON object.
Follow the example above:
let object1 = { a: 1, b: { // b is the object b1: 2 } }; object2 = JSON.parse(JSON.stringify(object1)); // Deep copy // A is a regular type and will not affect each other object1.a = 10; console.log(object1.a); // 10 console.log(object2.a); // 1 // b is an object and will not affect each other object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 2
The principle of deep copy here is to convert the object into a json string first, and then into a json object. After the middle is converted into a json string, it has nothing to do with the original object.
Advantages of this method:
The implementation is very simple.
Disadvantages:
If a property value is a function, it cannot be copied and the data will be lost. In addition, prototype objects cannot be copied.
So this approach is only suitable for object validation, which is pure json data.
2. Recursive replication
Because you need to copy layer by layer, it's easy to think of using recursion. Refer to the following implementation:
function deepCopy(source) { // If it is not an object or null, it returns directly if (typeof source !== 'object' || source === null) { return source; } let target = {}; // Traverse replication properties for (let k in source) { if (!source.hasOwnProperty(k)) { continue; } if (typeof source[k] === 'object') { // If it is an object, it is copied recursively target[k] = deepCopy(source[k]); continue; } let descriptor = Object.getOwnPropertyDescriptor(source, k); Object.defineProperty(target, k, descriptor); } return target; }
Because it is copied layer by layer, after the copy is completed, the two objects will not affect each other, and methods can also be supported.
let object1 = { a: 1, b: { // b is the object b1: 2 }, f: function() { // f is the method console.log(3); } }; object2 = deepCopy(object1); // Deep copy, you can also copy functions. object1.f(); // 3 object2.f(); // 3 // b is an object and will not affect each other object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 2
Copy prototype object
However, there is still a problem with this method, that is, the prototype object cannot be copied. Make some improvements:
// Set let target = {}; Change to the following way // Ensure that the prototype is also copied let target = Object.create(Object.getPrototypeOf(source));
This is enough. Take an example to verify:
function Human() { this.id = 1; } Human.prototype.bar = function() { console.log("bar"); }; let human1 = new Human(); human2 = deepCopy(human1); console.log("human1", human1); console.log("human2", human2);
View the prototypes of the following two objects:
Perfect copy.
Of course, there is a problem with this method, that is, recursion itself is too deep, which is easy to cause stack overflow. However, in practice, it is also recommended not to copy very large objects. There should be another good solution.
Reference documents
JS enables deep copy: https://www.cnblogs.com/dobeco/p/11295316.html
Object.assign(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.create(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Object.getPrototypeOf(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
Object.defineProperty(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.getOwnPropertyDescriptor(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
hasOwnProperty(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty