Understanding of shallow copy and deep copy

1. Data type
Data is divided into basic data types (String, Number,  Boolean , Null, Undefined, Symbol) and object data types.

The characteristics of the basic data type: the data directly stored in the stack (stack)
The characteristics of the reference data type: the stored object is referenced in the stack, and the real data is stored in the heap memory

The reference data type stores a pointer on the stack, which points to the starting address of the entity in the heap. When the interpreter looks for a reference value, it first retrieves its address on the stack, and then gets the entity from the heap after getting the address.

 2. Shallow copy and deep copy
Deep copy and shallow copy are only for reference data types such as Object and Array.
The schematic diagram of deep copy and shallow copy is roughly as follows:

Shallow copy only copies the pointer to an object, not the object itself, and the old and new objects still share the same memory. However, deep copy will create an identical object. The new object does not share memory with the original object, and modifying the new object will not change the original object.
3. The difference between assignment and shallow copy
When we assign an object to a new variable, what is actually assigned is the address of the object on the stack, not the data in the heap. That is, the two objects point to the same storage space. No matter which object changes, it is actually the content of the changed storage space. Therefore, the two objects are linked.
A shallow copy is a bitwise copy of an object, which creates a new object with an exact copy of the original object's property values. If the attribute is a basic type, the value of the basic type is copied; if the attribute is a memory address (reference type), the memory address is copied, so if one of the objects changes this address, it will affect the other object. That is to say, the default copy constructor only performs a shallow copy of the object (copying members one by one), that is, only the object space is copied but not the resource.

Let's look at two examples first. What changes will be brought to the original object by comparing assignment and shallow copy?

// 对象赋值
 
var obj1 = {
    'name': 'zhangsan',
    'age':  '18',
    'language': [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1', obj1)
console.log('obj2', obj2)

 

// 浅拷贝
 
var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};
 
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
 
function shallowCopy(src) {
    var dst = {};
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop];
        }
    }
    
return dst;
}
console.log('obj1', obj1)
console.log('obj3', obj3)

 

 In the above example, obj1 is the original data, obj2 is obtained by assignment operation, and obj3 is obtained by shallow copy. We can clearly see the impact on the original data, see the table below for details

Fourth, the implementation of shallow copy

1、Object.assign()

Object.assign() The method can copy any number of enumerable properties of the source object itself to the target object, and then return the target object. But  Object.assign()what is done is a shallow copy, which copies the references to the properties of the object, not the object itself.

var obj = { a: {a: "kobe", b: 39} };
var initalObj = 
Object.assign({}, obj);
initalObj.a.a = "wade";
console.log(obj.a.a); 
//wade

Meaning: When the object has only one layer, it is a deep copy

 

let obj = {
    username: 'kobe'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj); //{username: "kobe"}

2、Array.prototype.concat()

let arr = [1, 3, {    username: 'kobe'    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

 Modifying the new object will change the original object:

 3、Array.prototype.slice()

let arr = [1, 3, {    username: ' kobe'    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

Similarly, modifying the new object will change to the original object:

 

Supplementary note on Array's slice and concat methods : Array's slice and concat methods do not modify the original array, but only return a new array that shallowly copies the elements in the original array.

The elements of the original array are copied according to the following rules:

  • If the element is an object reference (not an actual object), slice will copy the object reference into the new array. Both object references refer to the same object. If the referenced object changes, this element in the new and original arrays will also change.

  • For strings, numbers, and Boolean values ​​(not String, Number, or Boolean objects), slice copies the values ​​into the new array. Modifying these strings or numbers or boolean values ​​in other arrays will not affect the other array.

This passage may be difficult to understand. Let’s give an example and modify the above example slightly:

let arr = [1, 3, {    username: ' kobe'    }];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);

 

 Five, the implementation of deep copy

 1、JSON.parse(JSON.stringify())

let arr = [1, 3, {    username: ' kobe'}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr, arr4)

 

 

Principle: Use JSON.stringify to convert the object into a JSON string, and then use JSON.parse() to parse the string into an object. After one to one, a new object is generated, and the object will open a new stack to realize deep copy .

Although this method can implement deep copying of arrays or objects, it cannot handle functions.

let arr = [1, 3, { username: ' kobe' },function(){}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr, arr4)

2. Handwritten recursive method

The recursive method implements the principle of deep cloning: traverse objects and arrays until they are all basic data types, and then copy them, which is deep copying.

//定义检测数据类型的功能函数
    
function checkedType(target) {
    return Object.prototype.toString.call(target).slice(8, -1)
}
    
//实现深度克隆---对象/数组
    
function clone(target) {
      
    //判断拷贝的数据类型
      
    //初始化变量result 成为最终克隆的数据
      
    let result, targetType = checkedType(target)
      
    if (targetType === 'object') {
        result = {}
     } else if (targetType === 'Array') {
        result = []
      } else {
        return target
      }
      
    //遍历目标数据
    for (let i in target) {
        //获取遍历数据结构的每一项值。
        let value = target[i]
        //判断目标结构里的每一值是否存在对象/数组
        if (checkedType(value) === 'Object' ||
          checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
          //继续遍历获取到value值
          result[i] = clone(value)
        } else { //获取到value值是基本的数据类型或者是函数。
          result[i] = value;
        }
      }
      return result
}

 

3. Function library lodash

This function library is also provided  _.cloneDeep for Deep Copy.

var _ = require('lodash');
var obj1 = {    a: 1, b: { f: { g: 1 } },    c: [1, 2, 3]};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

Guess you like

Origin blog.csdn.net/qq_34093387/article/details/126749736