First, “deep copy” and “shallow copy” difference

For this question, consider the use or origin of deep and shallow copies, which is why this problem arises.

Let’s start with some javascript basics.


[1] javascript variables contain values of two different data types: base and reference.

(1) Basic type values refer to simple data segments, including those newly added in ES6. There are 6 types in total, as follows:

String, Number, Boolean, Null, Undefined, Symbol

② Reference type values refer to objects that may consist of multiple values, as follows:

Object (Object, Array, Function)

When assigning a value to a variable, the parser must determine whether the value is a primitive or referential value. Basic data types are accessed by value because you can manipulate the actual values stored in variables.

The value of a reference type is an object held in memory. Unlike other languages, JavaScript does not allow direct access to locations in memory, meaning you cannot directly manipulate the memory space of an object. When you manipulate an object, you’re actually manipulating a reference to the object rather than the actual object.


[2] storage of javascript variables — stack and heap

Stack: Automatically allocated memory space, automatically released by the system, which holds the basic type of value and reference type address

Heap: Dynamically allocated memory that varies in size and is not automatically freed. It holds the value of the reference type.

[3] javascript value passing and address passing

The biggest difference between base types and reference types is really the difference between value and address

Value passing: Base types use value passing.

    let a = 10; // Define a variable a and assign it to 10
    let b = a;  // Assign a value of 10 to b (a and B are both basic types, passing values)
    b++;  / / b plus
    console.log(a, b) / / 10 and 11Copy the code

Address pass: The reference type is address pass, assigning an address stored in stack memory to the variable received.

    let a = ['a'.'b'.'c']; // Define an array a and assign it to it
    let b = a;   // Array is a reference type, which uses address passing to assign the address of A to B
    b.push('d'); // Add a 'd' element to the b array
    console.log(a) // ['a', 'b', 'c', 'd']
    console.log(b) // ['a', 'b', 'c', 'd']Copy the code

Analysis: Since both a and B are reference types, using address passing, that is, A passes the address to B, then A and B must refer to the same address (the address of the reference type is stored in stack memory), and this address refers to the value of the reference type in heap memory. When B changes this value, it also changes a’s value, because a’s address also points to this value. For example, if A rents a room and gives B the address of the room, and B finds the room by using the address, then any changes B makes to the room (adding some greenery) must also be visible to A.

So how to solve the problem above, is to use shallow copy or deep copy. JS basic types do not have shallow copy or deep copy issues, mainly for reference types

[4] Summary of differences between shallow copy and deep copy

2. What it means:

Shallow copy – The copy level is shallow.

Deep copy: The copy level is deeper.

Specific:

Shallow copy – When copying an object, only the first layer of key-value pairs are independently copied. If there are other objects in the object, only the addresses of nested objects are copied

Deep copy – Deep copy refers to copying an object completely. Even if the objects are nested, they are separated from each other. Modifying the properties of one object does not affect the properties of the other. In fact, as long as the recursion, those properties are still the value of the object into the object again to copy.

Shallow copy case

The shallow copy solution is to set up a new object obj2 and assign the value of obj1 to obj2 by traversing it.

    // A shallow copy of the array
    let arr1 = [1.2.3]
    let arr2 = []
    for (let i in arr1) {
      arr2[i] = arr1[i]
    }
    arr2.push(4)
    console.log(arr1)  / / [1, 2, 3]
    console.log(arr2)  // [1, 2, 3, 4]

    // A shallow copy of the object
    let obj1 = {
      a: '1'.b: '2'.c: '3'
    }
    let obj2 = {}
    for (let i in obj1) {
      obj2[i] = obj1[i]
    }
    obj2.d = '4'
    console.log(obj1) // {a: "1", b: "2", c: "3"}
    console.log(obj2) // {a: "1", b: "2", c: "3", d: "4"}

    // Shallow copy function encapsulation
    function shallowCopy(obj1, obj2) {
      for(var key in obj1) {
        obj2[key] = obj1[key]
      }
    }Copy the code

However, the above code can only achieve a layer of copy, unable to carry out a deep copy, encapsulation function again through the object array nesting test as follows:

    // Shallow copy function encapsulation
    function shallowCopy(obj1, obj2) {
      for(var key in obj1) {
        obj2[key] = obj1[key]
      }
    }

    // A shallow copy of the object
    let obj1 = {
      a: '1'.b: '2'.c: {
        name: 'Demi'}}let obj2 = {}
    shallowCopy(obj1, obj2) // Copy the data of obj1 to obj2
    obj2.c.name = 'dingFY'
    console.log(obj1) // {a: "1", b: "2", c: {name: 'dingFY'}}
    console.log(obj2) // {a: "1", b: "2", c: {name: 'dingFY'}}Copy the code

It turns out that if there are objects in the object, then only the address of the nested object can be copied, and no further copy can be made. When the value of obj2 nested object C is changed, the value of obj1 nested object C also changes

At this point we can use deep copy to complete, the so-called deep copy, is able to achieve the true sense of the array and object copy, we recursively call the shallow copy way to achieve.

Deep copy case

// Deep copy function encapsulation
    function deepCopy(obj) {
      // Determine whether to create an array or object based on the type of obj
      let newObj = Array.isArray(obj)? [] : {};
      // Check that the obj passed exists and is of type object
      if (obj && typeof obj === 'object') {
        for(key in obj) {
          // If the children of obj are objects, the operation is recursive
          if(obj[key] && typeof obj[key] ==='object') {
            newObj[key] = deepCopy(obj[key])
          } else {
          // // If the child element of obj is not an object, it is directly assigned
            newObj[key] = obj[key]
          }
        }
      }
      return newObj // Return a new object
    }

    // Deep copy of the object
    let obj1 = {
      a: '1'.b: '2'.c: {
        name: 'Demi'}}let obj2 = deepCopy(obj1) // Copy the data of obj1 to obj2
    obj2.c.name = 'dingFY'
    console.log(obj1) // {a: "1", b: "2", c: {name: 'Demi'}}
    console.log(obj2) // {a: "1", b: "2", c: {name: 'dingFY'}}Copy the code

It turns out that the above code can achieve deep cloning.

A shallow copy of the array

In the case of arrays, we can take advantage of array methods such as slice and concat to return a new array.

【 1 】 Array concat ()

    let arr = ['one'.'two'.'three'];
    let newArr = arr.concat();
    newArr.push('four')

    console.log(arr)    // ["one", "two", "three"]
    console.log(newArr) // ["one", "two", "three", "four"]Copy the code

【 2 】 Array. Slice ()

    let arr = ['one'.'two'.'three'];
    let newArr = arr.slice();
    newArr.push('four')

    console.log(arr)    // ["one", "two", "three"]
    console.log(newArr) // ["one", "two", "three", "four"]Copy the code

Deep copy of array

Here’s a trick that applies to objects as well as arrays! One problem, however, is that you can’t copy functions

    let arr = {
      a: 'one'.b: 'two'.c: {
        name: 'Demi'}};let newArr = JSON.parse( JSON.stringify(arr) );
    newArr.c.name = 'dingFY'
    console.log(arr);    // {a: "one", b: "two", c: {name: 'Demi'}}
    console.log(newArr); // {a: "one", b: "two", c: {name: 'dingFY'}}


    // Test whether the function can be copied
    let arr = {
      a: 'one'.b: (a)= >{
        console.log('test')}};let newArr = JSON.parse( JSON.stringify(arr) );
    console.log(arr);    // {a: "one", b: ()=>{console.log('test')}}
    console.log(newArr); // {a: "one"} // The function failed to copyCopy the code

Shallow copy of the object

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 () does a shallow copy

    let arr = {
      a: 'one'.b: 'two'.c: 'three'
    };

    let newArr = Object.assign({}, arr)
    newArr.d = 'four'
    console.log(arr);    // {a: "one", b: "two", c: "three"}
    console.log(newArr); // {a: "one", b: "two", c: "three", d: "four"}Copy the code

Shallow copy encapsulation method

How it works: Iterate over an object, then place the property and its value in a new object

   let shallowCopy = function (obj) {
      // Only objects are copied
      if (typeofobj ! = ='object') return;
      // Determine whether to create an array or object based on the type of obj
      let newObj = obj instanceof Array ? [] : {};
      // Iterate over obj and determine if it is obj's property to copy
      for (let key in obj) {
        if(obj.hasOwnProperty(key)) { newObj[key] = obj[key]; }}return newObj;
    }Copy the code

Deep copy encapsulation method

Principle: We determine the type of property value when copying, if it is an object, we recursively call the deep copy function

    let deepCopy = function (obj) {
      // Only objects are copied
      if (typeofobj ! = ='object') return;
      // Determine whether to create an array or object based on the type of obj
      let newObj = obj instanceof Array ? [] : {};
      // Iterate over obj and determine if it is obj's property to copy
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // If obj's child property is an object, the operation is recursive, otherwise the assignment is direct
          newObj[key] = typeof obj[key] === 'object'? deepCopy(obj[key]) : obj[key]; }}return newObj;
    }Copy the code

The article is updated every week. You can search “Front-end highlights” on wechat to read it in the first time, and reply to [Books] to get 200G video materials and 30 PDF books