preface

Before we talk about deep copy, let’s review the data types in JS: basic types and reference types.

Basic types:

  1. undefined
  2. null
  3. Boolean
  4. String
  5. Number
  6. Symbol
  7. .

Reference type:

  1. Object
  2. Array
  3. Date
  4. function
  5. RegExp
  6. .

Basic type values occupy a fixed size in memory and are stored in stack memory.

The value of the reference type is the object, which is stored in the heap memory, while the stack stores the variable identifier of the object and the location of the object in the heap memory.

Different types of replication are different.

Shallow copy

For a primitive type, copying the value of a primitive type from one variable to another new variable creates a copy of the value and copies it to the new variable.

For reference types, when you copy the value of a reference type from one variable to a new variable, you are actually copying a pointer. In the end, both variables point to the same object (address), and the writes affect each other.

The manual version

var obj = {
    hero: 'vn'.hp: 1000.ad: 200.goods: ['infinite'.'green fork'.'broken']}var target = {}
copy(obj, target)

function copy(src, tar) {
    for(let key in src) {
        tar[key] = src[key]
    }
}

target.goods[0] = A 'genius'
console.log(obj.goods[0])

Copy the code

Merge version

In addition to its own encapsulation, JS also provides apis for merging objects (shallow copies).

var target = Object.assign(obj, {})
target.goods[0] = A 'genius'
console.log(obj.goods[0])
Copy the code

Deconstruction version

Of course, assignment by deconstruction is also possible.

vartarget = { ... obj } target.goods[0] = A 'genius'
console.log(obj.goods[0])
Copy the code

The above three methods have the same effect (all shallow copies) :

Deep copy

Deep copy mainly targets complex data structures such as Object Array. As shown in the figure, reference data after deep copy all point to their respective memory addresses. The two are independent of each other and do not affect each other.

Rough version

One of the simplest and most brutal methods that we’re familiar with

var obj = {
    hero: 'vn'.hp: 1000.ad: 200.goods: ['infinite'.'green fork'.'broken'].say: () = > {
        console.log('The night is over')}}var target = JSON.parse(JSON.stringify(obj))
Copy the code

This method works fine for copying simple data types. If you want to copy functions, reference types, etc., then this method doesn’t work. See the manual version.

The manual version

If you want to make a deep copy of an individual data type, you can do it by, or if you encounter an Object or an Array, you can do it separately.

function copy(src) {
    if(typeof src === 'object' && src) {
        let target = Array.isArray(src) ? [] : {}
        for(const key in src) {
            target[key] = copy(src[key])
        }
        return target
    } else {
        return src
    }
}

Copy the code

In day-to-day development, the above approach is sufficient. You can also use a third repository such as loadAsh.

Optimized version

var obj = {
    hero: 'vn'.hp: 1000.ad: 200.goods: ['infinite'.'green fork'.'broken'].say: () = > {
        console.log('The night is over')
    }
}
obj.son = obj
Copy the code

Sometimes it is inevitable to have such a need, circular reference will cause bugs, if the data is too large will recurse into an infinite loop and lead to stack overflow.

To solve this problem, you need to store the relationship between the current object (SRC) and the copied object (target) (in the form of key-value pairs).

The current object (SRC) is stored as the key and the copied object (target) as the value before the copy starts.

When you need to copy the current object, look in the map to see if the object has been copied. If yes, return directly. If no, continue copying.

You can choose a data structure called Map to solve this problem.

var obj = {
    hero: 'vn'.hp: 1000.ad: 200.goods: ['infinite'.'green fork'.'broken'].say: () = > {
        console.log('The night is over')}}var obj1 = copy(obj)
obj1.obj = obj
function copy(src, map = new Map(a)) {
    if(typeof src === 'object' && src) {
        let target = Array.isArray(src) ? [] : {}
        if(map.get(src)) return map.get(src)
        map.set(src, target)
        for(const key in src) {
            target[key] = copy(src[key])
        }
        return target
    } else {
        return src
    }
}

Copy the code

The last

Feel free to add, and if this post has helped you, please feel free to give it a like.

Picture from: reference article