Shallow copy

concept

JS data types are divided into primitive types and reference types. When we assign a variable of a reference type to another variable, the following situation usually occurs:

const obj1 = {name: 'wyt'};
const obj2 = obj1;
console.log(obj1.name);  // 'wyt'
obj2.name = 'huluntuntao';
console.log(obj2.name);  // 'huluntuntao'
console.log(obj1.name);  // 'huluntuntao'
Copy the code

Because of the reference type, obj2 will change and obj1 will change, but we don’t want that in many cases, so we have deep and shallow copies.

A shallow copy creates a new object whose property value is of the original type and whose reference address is of the reference type.

To put it simply, a shallow copy can copy the original type of an object property, but copy the reference type of an object property.

In other words, shallow copy creates a new address for the object to be copied, but does nothing about the values of other reference types inside the object.

The following are some common shallow copy methods:

Object.assign()

Object.assign() is one of the shallow copy methods commonly used in JS:

const obj1 = { name: 'wyt', other: { age: 25 } }; const obj2 = Object.assign({}, obj1); // The first argument is to copy to the target object, the second argument is to copy the object; console.log(obj1.name); // 'wyt' obj2.name = 'huluntuntao'; console.log(obj2.name); // 'huluntuntao' console.log(obj1.name); // 'wyt' obj2.other.age = 30; console.log(obj1.other.age) // 30 console.log(obj2.other.age) // 30Copy the code

Shallow copy can only copy the value of the original type of the object. It does not solve the problem completely when the value of the object is a reference type.

Note when using object.assign () :

  • You can copy the Symbol type
  • An object’s inherited properties cannot be copied
  • Cannot copy non-enumerable properties
const obj1 = { a: { b: 1 }, sym: Symbol(1), [Symbol(2)]: 'hahah' }; Object.defineproperty (obj1, 'innumerable', {value: 'This is an unenumerable property ', enumerable: false}); const obj2 = Object.assign({}, obj1); console.log(obj2); // The result is shown belowCopy the code

Extended operators (…)

The extension operator is used as follows, basically the same as object.assign () :

const obj1 = { name: 'wyt', other: { age: 25 } }; const obj2 = {... obj1}; console.log(obj1.name); // 'wyt' obj2.name = 'huluntuntao'; console.log(obj2.name); // 'huluntuntao' console.log(obj1.name); // 'wyt' obj2.other.age = 30; console.log(obj1.other.age) // 30 console.log(obj2.other.age) // 30Copy the code

Concat () and slice ()

Concat () and slice() only make shallow copies of arrays, and both return a new array object without altering the original array.

Implement a shallow copy yourself

function MyAssign(source) { if (typeof source ! == 'object' || source === null) { return source; } const target = array.isarray (source)? [] : {}; // Loop over source for (let key in source) {hasOwnProperty() checks whether the object has a property without the stereotype chain) if (source.hasOwnProperty(key)) { target[key] = source[key]; } } return target; }Copy the code

What is deep copy

concept

Shallow copy Only copies the reference address of the property value when the property value is a reference type.

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. The two objects are independent and unaffected, completely separating them from memory.

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.

JSON.stringify()

Json.stringify () is one of the most common deep-copy methods in development. It serializes the object to string and then deserializes it to a new object using json.parse ().

const obj1 = { name: 'wyt', other: { age: 25 } }; const obj2 = JSON.parse(JSON.stringify(obj1)); obj2.other.age = 30; console.log(obj2.other.age); // 30 console.log(obj1.other.age); / / 25Copy the code

Json.stringify (), while it mostly meets our deep-copy needs in everyday development, has a number of problems:

  • Unable to copy non-enumerable properties
  • For the regular object (RegExp()), return the empty object ({}) after copying;
  • Returns a string after copying the Date object (Date());
  • When the property values are undeniational, Symbol and functions, the key pairs disappear.
  • Unable to copy properties on object prototype chain
  • An error will be reported when copying objects referenced by the loop (obj[key] = obj);
  • Object containing NaN, Infinity, and -infinity returns NULL;
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

const obj1 = {
    name: 'wyt',
    other: {
        age: 30
    }
};
obj1.other.age = obj1;
const obj2 = JSON.stringify(obj1);
Copy the code

Simple version of deep copy implementation

Implement the most basic deep copy:

function easyDeepClone(source) { if (typeof source ! == 'object' || source === null) { return source; } const target = array.isarray (source)? [] : {}; If (typeof source[key] === 'object') {target[key] = easyDeepClone(source[key]); } else { target[key] = source[key]; } } return target; }Copy the code

There are some problems with the above deep-copy functions:

  • Unable to copy non-enumerable properties;
  • This method only copies the values of ordinary reference types recursively. It does not copy the values of Date, RegExp, and Function correctly.
  • Failed to solve the problem of circular references

Advanced version of deep copy implementation

// Use a WeakMap to open up storage space and store the corresponding relationship between the current object and the copied object. Function deepClone(source, weakMap = new WeakMap()) {// Check whether it is a basic type or function. Const sourceType = typeof source; if ((sourceType ! == 'object' && sourceType ! == 'function') || source === null || sourceType === 'function') { return source; Constructor === Date) {return new Date(source); Constructor === RegExp {return new RegExp(source);} // Check whether RegExp is present and return if (source.constructor === RegExp) {return new RegExp(source); } if (weakmap.has(source)) { return weakmap.get(source); } let allDesc = Object.getOwnPropertyDescriptors(source); Const target = array.isarray (source)? Const target = array.isarray (source)? [] : Object.create(Object.getPrototypeOf(source), allDesc); weakmap.set(source, target); // loop over source, OwnKeys () reflect.ownkeys (source).map((key) => {if (typeof source[key] === 'object') {target[key] = deepClone(source[key], weakmap); } else { target[key] = source[key]; } }) return target; }Copy the code

Object. GetOwnPropertyDescriptors () for all its attribute of the specified Object descriptors, if you don’t have any own properties, it returns null objects.

Object.getprototypeof () gets the prototype of the Object, and returns NULL if there are no inherited properties.

Reflect.ownkeys () gets all attributes of the object, including non-traversable attributes, but excluding inherited attributes.

WeakMap makes use of its weak reference characteristics, can not manually clear the memory, effectively prevent memory leaks, optimize the code… It is also used here to solve the problem of circular reference.

Here is the test 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 am a function ')}, date: new date (0), reg: New RegExp('/ I am a re /ig'), [Symbol('1')]: 1,}; Object.defineproperty (obj, 'innumerable', {enumerable: false, value: 'unenumerable'}); Obj. Loop = obj // Set loop to a property referenced in a loopCopy the code

Depth copy points

  • Use of shallow copy object.assign ()
  • Write a shallow copy yourself
  • Usage and limitations of deep copy json.stringify ()
  • Write a final deep copy yourself, paying attention to weakMap and reflect.ownkeys ().

For this deep copy, I still don’t have a deep understanding, which involves a lot of things. The above implementation is only within the scope of my current understanding, but there are still many problems, such as the copy of function… And need a lot of review, to remember and consolidate.