Foreword

I first realized that the importance of deep copy is when I use redux (react + redux). The redux mechanism requires that a new object must be returned in the reducer, but not the original object. In fact, at the time I Of course, I won’t take the initiative to make this mistake, but in many cases, I accidentally modify the original object, for example: var newObj = obj; newObj.xxx = xxx In fact, this time newObj and obj two references point to The same object, I modified newObj, in fact, it is equivalent to modifying obj, this is the first encounter between me and the shallow copy.

 

The difference between deep copy and shallow copy

1. Shallow copy: Assign the original object or the original array reference directly to the new object, the new array, the new object / array is just a reference to the original object

2. Deep copy: Create a new object and array, copy the “value” (all elements of the array) of the properties of the original object, which is “value” instead of “reference”

 

Why use deep copy?

We want to change the original array (object) when changing the new array (object)

 

Deep copy requirement level

When using deep copy, we must figure out the requirements of our deep copy: Is it only “deep” copying the first level of object attributes or array elements, or recursively copying all levels of object attributes and array elements?

 

How to verify the success of deep copy

Change the properties/elements in any new object/array without changing the original object/array

 

1, only copy the first level

 

Deep copy array (copy only first-level array elements)

 

Direct traversal
var array = [1, 2, 3, 4];
function copy (array) {
   let newArray = []
   for(let item of array) {
      newArray.push(item);
   }
   return  newArray;
}
var copyArray = copy(array);
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
console.log(copyArray); // [100, 2, 3, 4]

 

This method will not be explained (there is a foot)

 

2. slice()
var array = [1, 2, 3, 4];
var copyArray = array.slice();
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
console.log(copyArray); // [100, 2, 3, 4]

The slice() method returns a new array of fragments of some of the elements taken from the existing array (without changing the original array!)

Usage: array. Slice(start,end) start indicates the subscript of the starting element, and end indicates the subscript of the terminating element.

When slice() takes no arguments, it returns a new array with the same length as the original array by default.

 

3. concat()
var array = [1, 2, 3, 4];
var copyArray = array.concat();
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
console.log(copyArray); // [100, 2, 3, 4]

The concat() method is used to join two or more arrays. (This method does not change the existing array, it just returns a copy of the connected array.)

Usage: array.concat(array1,array2,…,arrayN)

Because we did not take the parameters when calling concat above, var copyArray = array.concat(); is actually equivalent to var copyArray = array.concat([]);

That is, returning the returned array and an empty array and returning

However, of course things are not so simple, my title above is “deep copy array (copy only first-level array elements)”, here means that for first-level array elements are basic type variables (such as number, String, boolean Simple arrays, all of the above three copy methods can succeed, but for the first-level array elements are arrays of reference type variables such as objects or arrays, the above three methods will be invalid, for example:

var array = [
   { number: 1 },
   { number: 2 },
   { number: 3 }
];
var copyArray = array.slice();
copyArray[0].number = 100;
console.log(array); //  [{number: 100}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

 

 

Deep copy object

 

Direct traversal

This method does not explain (hands on the line)

 

2.ES6 Object.assign
var obj = {
  name: 'a',
  job: 'b'
}
var copyObj = Object.assign({}, obj);
copyObj.name = 'c';
console.log(obj);   // {name: "a", job: "b"}
console.log(copyObj);  // {name: "c", job: "b"}

Object.assign: used for the merging of objects, copying all enumerable properties of the source object (source) to the target object (target), and returning the merged target

Usage: Object.assign(target, source1, source2); So copyObj = Object.assign({}, obj); This code will copy the first level attribute in obj to {}, and then return it to the assignment. To copyObj

 

3.ES6 extension operator:

The extension operator (…) is used to fetch all traversable properties of the parameter object and copy it into the current object.

But for multi-level nested objects, unfortunately, the above three methods will fail:

var obj = {
   name: {
      firstName: 'a',
      lastName: 'b'
   },
   job: 'c'
}
 
var copyObj = Object.assign({}, obj)
copyObj.name.lastName = 'd';
console.log(obj.name.lastName); 
console.log(copyObj.name.lastName);

 

2, copy all levels

Is there a more powerful solution? Enable us to

 

1. Not only copy the first level, but also the values ​​of all levels of the array or object.

 

2. Not for arrays or objects alone, but for objects that are common to arrays, objects, and other complex JSON forms

please look below:

The following trick can be described as “a fresh trick, eat all over the sky”

 

1.JSON.parse(JSON.stringify(XXXX))
var array = [
    { number: 1 },
    { number: 2 },
    { number: 3 }
];
var copyArray = JSON.parse(JSON.stringify(array))
copyArray[0].number = 100;
console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

 

 

 

Simple and rude! ! !

Is it easy to use?

Um? What do you say? You said that the above method is too brainless. Do you have to write a recursion before you have the feeling of doing technology? OK to fulfill you!

 

2. Manually write recursion
var array = [
   { number: 1 },
   { number: 2 },
   { number: 3 }
];
function copy (obj) {
        var newobj = obj.constructor === Array ? [] : {};
        if(typeof obj !== 'object'){
            return;
        }
        for(var i in obj){
           newobj[i] = typeof obj[i] === 'object' ?
           copy(obj[i]) : obj[i];
        }
        return newobj
}
var copyArray = copy(array)
copyArray[0].number = 100;
console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

 

[Note]: All the above examples ignore some special cases: special types of copies of functions, regular expressions, etc. in objects/arrays

 

3, there are a lot of deep copy requirements code – immutable provides solutions

In fact, even if we know how to make deep copies in various situations, we still face some problems: Deep copy is actually very performance consuming. (We may just want to change one of the elements in the new array without affecting the original array, but it is forced to copy the entire original array again. Isn’t that a waste?) So, when there are a lot of items in your project When deep copying demand, performance may form a bottleneck of constraints.

 

The role of immutable:

Implemented by a set of APIs introduced by immutable:

 

1. When changing a new array (object), do not change the original array (object)

 

2. Significantly reduce performance consumption in a large number of deep copy operations

Sneak peek:

const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50
Reference materials:

1.MDN javascript array API
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

Orignal link:https://blog.csdn.net/wyp1725726792/article/details/102756183