Three concepts: reference assignment, shallow copy, and deep copy

Reference assignment: The process of defining a variable and assigning a value to the variable (reference type) actually creates a space in memory to hold the value of the reference type. The cleared space has a memory address such as 0x0010001, which is then assigned to the defined variable. When used, it follows the memory address to find the actual contents of memory.

As shown in the figure, define a variable obj1 and assign an object to obj1, then define a variable obj2 and assign obj1 to obj2. It creates a space in memory, and then it assigns the address of that space to the variable, and it assigns obj1 to obj2, so it assigns the address of that memory to obj2, and when you change obj2, you follow the memory address to find the real contents of memory and modify it.

We have not modified obj1, but if we print obj1, we will find that the output of obj1 has changed, which is the effect of reference assignment.

Shallow copy: In shallow copy, a new memory space is created in the heap. This new memory space holds exactly the same data containing attributes as the source data, but it literally copies only shallow data attributes.

Define an obj1 object whose value is an object and the value of an attribute in that object is also an object. One space will store the data of the book obj1. If the value of the attribute is of non-reference type, the value will be directly assigned. If the value of the attribute is of reference type, the reference address will be stored, indicating the actual data content (memory address: X010111).

When we copy obj1 to obj2, we create a space in the heap for obj2 to hold the actual data and assign the reference address to obj2. So when obj1.name is modified, obj2 will not be affected. However, if you modify obj1.info.job and print obj2, you will find that the info property of obj2 has also changed

Deep copy: If you understand shallow copy, it is also easy to understand. As the name implies, deep copy is a complete copy of the source data. In addition to the basic type value attributes of the source data, the reference class values of the source data are also stored in a new space in the heap memory.

Comparison of several methods of deep copy

JSON conversion

In a real world project, most of the deep copy will be sufficient using the JSON transformation method of the requirements, and json.stringify () and json.parse () will suffice for most of our project requirements. As an example, the following code arbitrarily changes the property values of obj1 or obj2, and neither will be affected

var obj1 = {
  name:'Zhao Lao Si'.age:20.info: {job:'Front End Engineer'.time:3}}const obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2)
obj1.info.job = 'Switch to Java Development'
obj1.age = 21
console.log(obj1,obj2)
Copy the code

However, JSON conversion is not a perfect deep copy of the source data, when the metadata properties have functions, re, time objects, undefined, it cannot be copied to. It will ignore the attribute value of function, undefined, encounter the attribute value of the time object, it will be resolved as a time string, encounter regular expression will be resolved as an empty object

var obj1 = {
  name:'Zhao Lao Si'.age:20.date:new Date(),
  func:function(){},
  nund:undefined.reg:/\.js/
}
const obj2 = JSON.parse(JSON.stringify(obj1))
Copy the code

We know that JSON conversion works for deep copy, but why does it? We know that the essence of deep copy is to create space in the heap to store the actual data, but does JSON transform create space in the heap?

Json.stringify () and json.parse () are both serialization and deserialization processes. Serialization is used to store and transfer a javascript object through json.stringify () into a JSON string. Parse () then deserializes the JSON string to return a new JS object

Deep copy is implemented recursively

Create a new object obj2, iterate over obj1, and add the properties of the cloned object to the new object obj2 in turn. This implements the most basic copy

var obj1 = {
  name:'Zhao Lao Si'.age:20.info: {job:'Front End Engineer'}}const obj2 = {}
for(var k in obj1){
  obj2[k] = obj1[k]
}
Copy the code

However, as mentioned earlier, this does not copy all the reference type attributes in the source data. In the above example, obj1 has an info attribute that is a reference type value. To make a deeper copy, you need to recursively assign all levels of properties until the level property value is a primitive type. We converted the above code into a recursive function

function recursion(obj){
  if(typeof obj === 'object') {let res = {}
    for(const k in obj){
      res[k] = recursion(obj[k])
    }
    return res
  }else{
    return obj
  }
}
Copy the code

The code above only considers the type of the object whose attribute value is an array. Let’s test what would happen if we copied the source data from it.

var obj1 = {
  name:'Zhao Lao Si'.age:20.arr: [1.2.3].info: {job:'Front End Engineer'}}const obj2 = recursion(obj1)    // copy to generate a new object obj2
console.log(Array.isArray(obj1.arr),Array.isArray(obj2.arr))  // Prints whether obj1 and obj2 are arrays
Copy the code

Open the console can see the output as a result, the arr obj2 attribute is not an array, it is a key value of form, this is because we didn’t in the recursive function values of an array type for processing, we need to do is in the source data of each type to judge, and then decided to receive the different values according to its type variable is what kind

It is optimized as follows: it can traverse both objects and arrays

function recursion(obj){
  if(typeof obj === 'object') {let res = Array.isArray(obj) ? [] : {}
    for(const k in obj){
      res[k] = recursion(obj[k])
    }
    return res
  }else{
    return obj
  }
}
Copy the code