First of all, we need to understand that JS data types are divided into two types, one is basic data type, the other is reference data type.

There are six basic data types:undefined.null.number.boolean.string.symbol(ES6).

Reference data types:object.Array.FunctionEtc.

The main differences between the two are:

Base data types store values, and reference data types store addresses. When a reference type is created, the computer creates a space in memory for us to store it, and this space has an address. And the values that refer to data types are objects stored in stack memory and heap memory. Stack area memory holds variable identifiers and Pointers to that object in heap memory. When looking for a reference value, the interpreter first looks for the address in the stack. It then finds the entity of heap memory based on the address.

In most real projects, we want two variables (with the same initial value) that do not affect each other. So you use copy (light and dark)

Shallow copy

Copy an object to create a new object, and the new object needs to create a new memory for storage. Both the primitive and String properties in the new object need to create a new space for storage, but if it is a reference type property, the reference type property still points to the reference property memory of the original object. When a change is made to a reference property of a new object or an original object, the values of the reference property types of both parties are changed simultaneously.

There are many ways to shallow copy:object.assing.Extend operator methods.Concat copies arrays.Slice copy arrayAnd so on.

Let’s look at a shallow copy of the extended operator approach

/ * a copy of the object * / let obj = {a: 1, b: {c: 1}} let obj2 = {... obj} obj.a = 2 console.log(obj) //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}} obj.b.c = 2 console.log(obj) //{a:2,b:{c:2}} console.log(obj2); // let arr = [1, 2, 3]; let newArr = [...arr]; // The same effect as arr.slice()Copy the code

The extension operator has the same drawback as object.assign, that is, it performs almost the same function as shallow copies, but it is more convenient to use the extension operator for shallow copies if the attributes are values of primitive types.

Implement a shallow copy manually

Based on the above understanding, how to implement a shallow copy manually.

  • Make a basic copy of the base type;

  • Create a new store for reference types and copy a layer of object properties

See the code below:

Var Obj = {function () {alert(1)}, Obj :{a:1,b:{c:2}}, arr: [1,2], und: undefined, reg: /123/, date: new Date(0), NaN: NaN, infinity: Infinity, sym: Symbol(1) } 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 { return target; } } shallowClone(Obj)Copy the code

It can be seen from the code that a shallow copy of the code is basically realized manually by using the judgment type and iterating through the object attributes assigned to the target object for the reference type object.

Deep copy

Create a new object and copy the values of the properties of the original object. Deep copy copies all objects referenced by the copied object.

Method 1: Beggar’s Edition (JSON.stringfy)

Son.stringfy () serializes an object into a JSON string, converts the contents of the object into a string, and generates a new object from the JSON string using json.parse ().

Let STR = {a:1, b:[1,2,3]} let STR = json.stringify (obj1); let obj2 = JSON.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]} the console. The log (obj2); / / {a: 1, b: [1, 2, 3]}Copy the code

So there’s something to notice here

  • If the value of the copied object is function, undefined, or symbol, the key/value pair will disappear in the string serialized by json. stringify.
  • Copying a RegExp reference type becomes an empty object.
  • Object containing NaN, Infinity, and -infinity, the result of JSON serialization is null;
  • [key] = obj[key] = obj[key] = obj

Method two: Handwritten recursive implementation (basic version)

Let obj1 = {a:{b:1}} function deepClone(obj) {let cloneObj = {} for(let key in obj) {typeof obj[key] ==='object') {cloneObj[key] = deepClone(obj[key]) else {cloneObj[key] = obj[key]} return cloneObj } let obj2 = deepClone(obj1); obj1.a.b = 2; console.log(obj2); // {a:{b:1}}Copy the code

This basic version is also simpler to write and can handle most applications. However, there are some drawbacks: this method only copies the values of ordinary reference types recursively, and does not copy the values of Array, Date, RegExp, Error, and Function correctly. Let’s take a look at the improved version

Method 3: Improved version (improved recursive implementation)

Var obj = {num: 0, STR: ", Boolean: true, unf: undefined, nul: null, obj: {name: 'object ', id: 1, gender: 1}, arr: [0, 1, 2], func: function () {console.log(' function ')}, date: new date (0), reg: New RegExp('/ re /ig'), [Symbol('1')]: 1,}; Object.defineproperty (obj, 'innumerable', {enumerable: false, value: 'unenumerable'}); obj = Object.create(obj, Object. GetOwnPropertyDescriptors (obj) obj. Loop = obj / / set loop into circular references / / judge data type of the attribute function ifType (val) {let type = typeof  val; if (type ! == "object") { return type; } return Object.prototype.toString.call(val).replace(/^\[object (\S+)\]$/, '$1'); } // const deepClone = function (obj, Hash = new WeakMap()) {if (ifType(obj) === 'Date') return new Date(obj) // Date object returns a new Date object if (ifType(obj) === = If (hash.has(obj)) return hash.get(obj) let (weakMap) if (hash.has(obj)) return hash.get(obj) let AllDesc = Object. GetOwnPropertyDescriptors (obj) / / traverse incoming parameters all the key features of the let copyObj = Object. The create (Object. GetPrototypeOf (obj), AllDesc) // Inherit the prototype chain hash.set(obj, copyObj) const isType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj ! == null) for (let key of Reflect.ownKeys(obj)) { copyObj[key] = (isType(obj[key]) && typeof obj[key] ! == 'function') ? deepClone(obj[key], hash) : Obj [key]} return copyObj} // verify let copyObj = deepClone(obj) copyobj.arr. push(4) console.log('obj', obj) console.log('cloneObj', copyObj)Copy the code

Console results:

conclusion

In JavaScript, the depth of the knowledge of copy is more important, which is very helpful for you to understand the underlying principle of JS, for any problem, should use similar methods to analyze each problem in-depth investigation is what, so as to better improve their basic skills.