JS base data type and deep and shallow copy

First of all, we have to go back to Javascript’s basic data types for deep and shallow copying.

Value types[Deep copy]: Num, Boolean, String, null, undefined.

A primitive value is a simple data segment stored in stack memory. When a primitive value is copied, a new memory space is created and the value is copied to the new memory space. For example, 🌰 :

    var a = 1;
    var b = a;
    a = 2;console.log(a);// output 2;
    console.log(b);// output 1;
Copy the code

Reference types[Shallow copy]: objects, arrays, functions, etc.

A type value is an object stored in the heap, and a variable stores only the address pointing to that memory. When a reference value is copied, only the address pointing to that memory is actually copied. For example 🌰 :

var a = { b: 1 };
var a2 = a;
a2.b = 2;
console.log(a); // output {b: 2}
Copy the code

Several methods to implement deep copy of objects

Arrays are also objects, so we’ll start with a deep copy of the core object.

  1. Json.parse () && json.stringfy () converts the object to its JSON string representation and then parses it back to the object. This feels a little too simple, but it works:

    const obj = / *... * /;
    const copy = JSON.parse(JSON.stringify(obj));
    Copy the code

    The advantage is that you can get the fastest cross-browser cloning performance using this method if there are no looping objects and no built-in types need to be retained. The downside here is that you create a temporary, possibly large string, just to put it back into the parser. Another disadvantage is that this approach does not handle looping objects, which occur frequently. For example, when we build a tree data structure, one node refers to its parent, which in turn refers to its children.

    const x = {};
    const y = { x };
    x.y = y; // Cycle: x.y.x.y.x.y.x.y.x...
    const copy = JSON.parse(JSON.stringify(x)); // throws!
    Copy the code

    In addition, built-in types such as Map, Set, RegExp, Date, ArrayBuffer, and other built-in types are lost in serialization.

  2. The disadvantage of this approach is that it is asynchronous. This is fine, but sometimes you need to use synchronization to deeply copy an object.

    function structuralClone(obj) {
      return new Promise(resolve= > {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev= > resolve(ev.data);
        port1.postMessage(obj);
      });
    }
    
    const obj = / *... * /;
    const clone = await structuralClone(obj);
    Copy the code

Deep copy of array as wellArray.slice()andArray.concat()Are methods deep copy?

I was all confused about this, so I looked up some information on the Internet to clear it up.

For one-dimensional arrays:

  1. arrayObj.slice(start, end)
    var arr1 = ["1"."2"."3"];
    var arr2 = arr1.slice(0);
    arr2[1] = "9";
    console.log("Original value of array:" + arr1); / / 1, 2, 3
    console.log("Array new value:" + arr2); / / 1,9,3
    Copy the code
  2. arrayObj.concat(arr1,arr2 … )
    var arr1 = ["1"."2"."3"];
    var arr2 = arr1.concat();
    arr2[1] = "9";
    console.log("Original value of array:" + arr1); //1,2,3 console.log(" array new value: "+ arr2); / / 1,9,3
    Copy the code

What if an array contains objects? :

var arr1 = [{ name: "weifeng" }, { name: "boy" }]; / / the original array
var arr2 = [].concat(arr1); // Copy the array
arr1[1].name = "girl";
console.log(arr1); // [{"name":"weifeng"},{"name":"girl"}]
console.log(arr2); //[{"name":"weifeng"},{"name":"girl"}]

var a1 = [["1"."2"."3"]."2"."3"],
  a2;
a2 = a1.slice(0);
a1[0] [0] = 0; // Change the first element of a1's first element
console.log(a2[0] [0]); // it affects a2
Copy the code

As can be seen from the above two examples, slice and concat are used to copy the array of objects. The entire copy is shallow, and Pointers to each value of the array still point to the same storage address.

The array.slice () and array.concat () methods only work for deep copies of one-dimensional arrays that do not contain reference objects!

Here’s another question for us to think about 🤔. What if, in a two-dimensional array, we modify the first dimension directly? A1 [0] = 1. Will this change affect A2?

In the ES6Object.assign()Method and object extension operators.

Object. The assign () method

Object.assign() is an ES6 implementation of Object copy. The object.assign () function [here is an article][1] is explained in great detail.

ES6 provides object.assign (), which is used to merge/copy attributes of objects.

Object.assign(target, source_1, ... , source_n)Copy the code

Here’s an example

var o1 = { a: 1.b: 1.c: 1 };
var o2 = { b: 2.c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
Copy the code

Is object.assign () a shallow copy or a deep copy? Consider the following example:

function mutateDeepObject(obj) {
  obj.a.thing = true;
}

const obj = { a: { thing: false}};const copy = Object.assign({}, obj);
mutateDeepObject(copy);
console.log(obj.a.thing); // prints true
Copy the code

Object.assign(target, sources…) Is a simple way to copy objects, belonging to the shallow copy. It takes any number of source objects and its main function is to enumerate all their properties and assign them to target.

Object extension operator.

Using the object extension operator… The object’s own enumerable properties can be copied to the new object.

const obj = { a: 1.b: 2.c: { d: "d"}};constshallowClone = { ... obj }; shallowClone.a ="a";
shallowClone.c.d = "4";
console.log(obj); // a: 1, b: 2, c: {d: "4"}}
console.log(shallowClone); // a: "a", b: 2, c: {d: "4"}}
Copy the code

Other methods that look like deep copies of objects are really shallow copies

const obj = { a: 1.b: 2.c: {d:'d'}}const shallowClone = Object.keys(obj).reduce((acc, key) = > (acc[key] = obj[key], acc), {});
shallowClone.a = 'a';
shallowClone.c.d = '4';
console.log(obj); // a: 1, b: 2, c: {d: "4"}}
console.log(shallowClone); // a: "a", b: 2, c: {d: "4"}}支那` `` * *Copy the code