In JavaScript, when a reference type value like Object or Array is copied from one variable to another, the copy of the reference value is actually a pointer. Both variables point to the same heap object, and changing one variable affects the other. There are two types of copying: copying references and copying instances, known as shallow copies and deep copies.
First, data types
Data is divided into basic data types (String, Number, Boolean, Null, Undefined, Symbol) and object data types.
Basic data types: data stored directly on the stack
Reference data type features: store the object in the stack reference, the real data is stored in the heap memory
The reference data type stores a pointer on the stack to the starting address of the entity in the heap. When the JS engine looks for a reference value, it first retrieves its address in the stack and then retrieves the entity from the heap.
Shallow copy and deep copy
Deep and shallow copies are only for reference data types such as Object and Array.
Deep and shallow copies look like this:
Shallow copy copies only Pointers to an object, not the object itself, and the old and new objects still share the same memory. But deep copy will create another identical object, the new object and the original object do not share memory, modify the new object will not change to the original object.
Assignment and shallow copy
When we assign an object to a new variable, we assign the object’s address on the stack, not the data in the heap. That is, two objects point to the same storage space. If any object is changed, it is the content of the changed storage space. Therefore, the two objects are linked.
A shallow copy is a bitwise copy of an object that creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, the value of the primitive type is copied. If the property is a memory address (reference type), the memory address is copied, so if one object changes the address, the other object will be affected. That is, the default copy constructor only copies objects shallowly (member by member), that is, only the object space is copied, not the resource.
Let’s take a look at two examples. What changes does an assignment versus a shallow copy make to the original object?
In the example above, obj1 is the raw data, obj2 is the assignment, and obj3 is the shallow copy. We can clearly see the impact on the raw data, as shown in the following table:
Four, shallow copy implementation
1.Object.assign()
The object.assign () method copies the enumerable properties of any number of source objects to the target Object and returns the target Object. But object.assign () makes a shallow copy, copying references to the Object’s attributes rather than the Object itself.
If the copied property is of the original value type, then it is a deep copy and the change does not affect the original object.
If the copied property is a reference type, it is a shallow copy and the change affects the original object.
Note: An object with only one layer is a deep copy
2.Array.prototype.concat()
Modifying a new object changes to the original object:
A quick note about Array’s slice and concat methods: Array’s slice and concat methods do not modify the original Array, but only return a new Array that copies the elements in the original Array.
Elements of the original array are copied according to the following rules:
If the element is an object reference (not an actual object), Slice copies the object reference into the new array. Both object references refer to the same object. If the referenced object changes, the element in the new and original array changes as well.
For strings, numbers, and Booleans (not String, Number, or Boolean objects), Slice copies these values into the new array. Modifying these strings or numbers or booleans in another array will not affect the other array.
If this paragraph is difficult to understand, let’s take an example and modify the above example slightly:
4. Deconstruction
let c={ age:1, job:{ first:coding } } let d={... c} c.age='2' console.log(d.age) //1 c.jobs.first='waiter' console.log(d.jobs.first) //waiterCopy the code
Fifth, the implementation of deep copy
- Json.parse (json.stringify ()) does not recognize undefined
Json.stringify () converts a JavaScript object to a JSON string, while json.parse () converts a JSON string to an object.
Json.stringify converts an object into a JSON string, and json.parse () parses the string into an object. As you go, new objects are created, and the object opens up a new stack for deep copy.
Defect:
1. Undefined and symbol are ignored. If the property value is undefined, the property is ignored
2. While this method can implement deep copies of arrays or objects, it cannot handle functions
3. Functions cannot be serialized
4. Cannot resolve objects referenced by loop
This is because the json.stringify () method converts a JavaScript value (object or array) to a JSON string and does not accept functions.
2. Handwritten recursive method
The recursive method implements the principle of deep cloning: iterating through objects and arrays until they contain all the basic data types, and then copying, which is called deep copy
let deepCopy = function (obj) { if (typeof obj ! == 'object') return; let newObj = obj instanceof Array ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; } let a={ b:{ c:'wn' } } let d=deepCopy(a) a.b.c='qqq' console.log(d); // { // b:{ // c:'wn' // } // }Copy the code
However, the deep-copy method above encounters a circular reference and gets caught in a circular recursive process, resulting in a stack burst. Such as:
var obj1 = {
x: 1,
y: 2
};
obj1.z = obj1;
var obj2 = deepCopy(obj1);
Copy the code
Vi. Deep copy function improvement (to prevent cyclic recursion)
To solve the problem of cyclic recursion, it is only necessary to determine whether an object’s fields reference the object or any parent of the object.
Function deepClone(obj, parent = null){// 表 示 (1) let result = array.isarray (obj)? [] : {}; let _parent = parent; While (_parent){if(_parent. OriginalParent === obj){return _parent. CurrentParent; } _parent = _parent.parent; } if(obj && typeof obj === "object"){ for(let key in obj){ if(obj.hasOwnProperty(key)){ if(obj[key] && typeof obj[key] === "object"){result[key] = deepClone(obj[key],{// improve (4) originalParent: obj, currentParent: result, parent: parent }); }else{ result[key] = obj[key]; } } } } return result; Var obj1 = {x: 1, y: 2}; obj1.z = obj1; var obj2 = deepClone(obj1); console.log(obj1); ~ console.log(obj2); // It's too longCopy the code
Give it a like and pay it forward. Ha ha ha ha ha ha ha ha