What is shallow copy?
Create a new object, the object to receive the source object copy or reference, if the source object’s properties is the basic data types, replication is the basic types of values for the new object, if the source object attribute is a reference data types, the copy is in memory address, if an object has changed the address in the memory, is another object will also change
How to implement shallow copy?
Method 1: Object.assign
The Assign method can be used to merge multiple objects or for shallow copiesCopy the code
Object.assign(parameter 1, parameter 2)let source = {
a: {b: 1}}let newObj = {} // Self-created objects
Object.assign(newObj,source)
console.log(newObj) //{ a: { b: 1 } }When you modify the value in newObj, both objects change newobj.a.b =2
console.log(newObj) //{ a: { b: 2 } }
console.log(source) //{ a: { b: 2 } }
Copy the code
Note:
- It does not copy the object’s inherited properties;
- It does not copy the object’s non-enumerable properties;
- You can copy
Symbol
Type property.
Object.assign loop iterates over the properties of the original Object and assigns them to the corresponding properties of the target Object by copying them
let obj = { a: {b:1 }, sym:Symbol(1)};
Object.defineProperty(obj, 'innumerable', {value:'Non-enumerable properties'.enumerable:false
});
let newObj = {};
Object.assign(newObj,obj)
obj.a.b = 2;
console.log('obj',obj);
console.log('newObj',newObj);
Copy the code
Method two: By extending the operator
let cloneObj = { ... obj };Copy the code
If there is only one layer of arrays or objects whose elements are of simple type, changing the value of the first layer will not change the value of the other object. If it is the second layer or deeper, changing one value will affect the other object
// A copy of the object
let obj = {a:1.b: {c:1}}
letnewObj = {... obj} obj.a =2
console.log(obj) //{a:2,b:{c:1}}
console.log(newObj); //{a:1,b:{c:1}} A belongs to layer 1 data
obj.b.c = 2
console.log(obj) //{a:2,b:{c:2}} c is layer 2 data
console.log(obj2); //{a:1,b:{c:2}}
Copy the code
Method 3: Slice copies arrays
The Slice method is also somewhat limited because it only works with array types. The slice method returns a new array object that uses the first two arguments of the method to determine the start and end times of the original array and does not affect or alter the original array.
let arr = [1.2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]
Copy the code
As you can see from the above code, you can manually implement a shallow copy of the code by using type judgment and iterating through the attributes assigned to the target object for the reference type object.
Implement shallow copy manually
Implementation idea:
- Make a basic copy of the base type;
- Create a new store for reference types and copy a layer of object properties.
Using type judgment, a for loop for an object that references a type iterates through the attributes assigned to the target object, and you can basically manually implement a shallow copy of the code.
The following code
const shallowClone = (target) = > {
if (typeof target === 'object'&& target ! = =null) {
const cloneTarget = Array.isArray(target) ? [] : {};for (let prop in target) {
if(target.hasOwnProperty(prop)) { cloneTarget[prop] = target[prop]; }}return cloneTarget;
} else {
returntarget; }}Copy the code
Deep copy
What is deep copy?
A shallow copy simply creates a new object and copies the values of the original object’s primitive type, while a reference data type copies only one layer of properties. Deep copy is different. For complex reference data types, a full memory address is created in the heap and the original object is copied over.
A complete copy of an object from memory to the target object, and a new space in the heap memory to store the new object, and the modification of the new object does not change the original object, the two achieve true separation.
Implementation method
Method 1: json.stringify
Json.stringify () is the simplest deep-copy method currently in development. It serializes an object into a JSON string, converts the contents of the object into a string, and then generates a new object from the JSON string using json.parse ().
let obj1 = { a:1.b: [1.2.3]}let str = JSONStringify (obj1);let obj2 = JSONThe parse (STR);console.log(obj2); / / {a: 1, b: [1, 2, 3]}
obj1.a = 2; obj1.b.push(4);
console.log(obj1); / / {2, a: b: [1, 2, 3, 4]}
console.log(obj2); / / {a: 1, b: [1, 2, 3]}
Copy the code
But there are a few things worth noting about using json.stringify for deep copy
- If the value of the copied object has a function,
undefined
,symbol
These types, afterJSON.stringify
The key/value pair disappears in the serialized string; - Copying the Date reference type becomes a string;
- Unable to copy non-enumerable properties;
- Unable to copy object’s prototype chain
- Object containing
NaN
,Infinity
As well as-Infinity
.JSON
The result of serialization will becomenull
;
To address these issues, try the following code to see what happens to such a complex object if it is deep-copied with json.stringify.
function Obj() {
this.func = function () { alert(1)};this.obj = {a:1};
this.arr = [1.2.3];
this.und = undefined;
this.reg = / 123 /;
this.date = new Date(0);
this.NaN = NaN;
this.infinity = Infinity;
this.sym = Symbol(1);
}
let obj1 = new Obj();
Object.defineProperty(obj1,'innumerable', {enumerable:false.value:'innumerable'
});
console.log('obj1',obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2',obj2);
Copy the code
Method 2: Basic version (handwritten recursive implementation)
The following is an example of a deepClone function encapsulation: for in iterates over the attribute value of an passed parameter, recursively calling the function again if the value is a reference type, or directly copying the underlying data type
let obj1 = {
a: {b:1}}function deepClone(obj) {
let cloneObj = {}
for(let key in obj) { / / traverse
if(typeof obj[key] ==='object') {
cloneObj[key] = deepClone(obj[key]) // Call this function recursively if it is an object
} else {
cloneObj[key] = obj[key] // Copy values directly for basic types}}return cloneObj
}
let obj2 = deepClone(obj1);
obj1.a.b = 2;
console.log(obj2); // {a:{b:1}}
Copy the code
Although it is possible to implement a deep copy using recursion, as with json.stringify above, there are still some problems that are not completely resolved, such as:
- This deep-copy function does not copy non-enumerable attributes as well
Symbol
Type; - This method simply makes a recursive copy of the values of ordinary reference types, whereas the values of ordinary reference types
Array
,Date
,Error
,Function
Such reference types do not copy correctly; - Object property inside the loop, i.e. circular reference is not resolved.
This basic version is also simpler to write and can handle most applications.
If there’s a flaw, there must be a better one
Method 3: Improved version (improved recursive implementation)
In view of the above problems to be solved, I will first tell you how to do respectively through four relevant theories.
- For non-enumerable properties that can traverse an object as well
Symbol
Type, we can useReflect.ownKeys
Methods; - If the parameter type is Date or RegExp, a new instance is generated and returned.
- Using the Object
getOwnPropertyDescriptors
Method can obtain all the attributes of the Object, as well as the corresponding properties, incidentally combined with the Object create method to create a new Object, and inherited the original Object prototype chain; - Use the WeakMap type as a Hash table, because WeakMap is a weak reference type, which can effectively prevent memory leaks. It is helpful to use WeakMap as a detection loop reference. If there is a loop, the reference directly returns the value that WeakMap stores.
const isComplexDataType = obj= > (typeof obj === 'object' || typeof obj === 'function') && (obj ! = =null)
const deepClone = function (obj, hash = new WeakMap(a)) {
if (obj.constructor === Date)
return new Date(obj) // The date object returns a new date object
if (obj.constructor === RegExp)
return new RegExp(obj) The regular object returns a new regular object directly
// If the loop is referenced, use weakMap
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
// Walks through the properties of all keys of the passed argument
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
// Inherit the prototype chain
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeofobj[key] ! = ='function')? deepClone(obj[key], hash) : obj[key] }return cloneObj
}
Copy the code
Let’s start validating the validation code
let obj = {
num: 0.str: ' '.boolean: true.unf: undefined.nul: null.obj: { name: 'I am an object'.id: 1 },
arr: [0.1.2].func: function () { console.log(I'm a function.)},date: new Date(0),
reg: new RegExp('/ I'm a regular /ig'),Symbol('1')]: 1};Object.defineProperty(obj, 'innumerable', {
enumerable: false.value: 'Non-enumerable properties'}); obj =Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // Set loop to a property referenced in a loop
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)
Copy the code