Shallow copy and deep copy

Posted by philip@hux.co.za on Thu, 28 Oct 2021 13:48:20 +0200

1, Data type

Before understanding deep and shallow copies, you need to understand the different data types.
Basic data types: string, number, null, undefined, boolean, symbol (newly added in ES6)
Reference data type: function, object, array
Characteristics of basic data types: data directly stored in the stack.
Characteristics of reference data type: the stored object is referenced in the stack, and the real data is stored in heap memory.

The reference data type stores a pointer in the stack that points to the starting address of the entity in the heap. When the interpreter looks for a reference value, it will first retrieve its address in the stack, and then obtain the entity from the heap.

2, Deep copy and shallow copy

Deep copy and shallow copy are for reference data types such as objects and arrays.

Shallow copy

A shallow copy is a bitwise copy of an object that creates a new object that is an exact copy of the original object's attribute values. If the attribute of the original object is a basic type, the copied value is the value of the basic type. If the attribute of the original object is a reference type, the copied value is the memory address. Therefore, if one object changes this address, it will affect the other object.

// Object Assignment 
var obj1 = {
   'label' : 'test1',
   'value' : [1,[2,3]],
};
var obj2 = obj1;
obj2.label= "test2";
obj2.value[1] = ["two","three"];
console.log('obj1',obj1)	// {'name': 'test2', 'value': [1, ['two', 'three']]}
console.log('obj2',obj2)	// {'name': 'test2', 'value': [1, ['two', 'three']]}

As shown above, obj2 only copies the memory address of obj1. Changing the attribute value of obj2 will affect obj1.

Implementation of shallow copy

  1. Object.assign()
    The Object.assign() method is a new method for objects provided by ES6. It is used for object merging and copying all enumerable attributes of the source object to the target object.
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

The first parameter of the Object.assign() method is the target object, and the following parameters are the source object.
There are restrictions on the properties copied by Object.assign() to copy only the source object's own properties (inheritance properties are not copied) and non enumerable properties (enumerable: false).
If the value of an attribute of the source object is an object (reference data type), the target object copies the reference of the object. Other basic types are equivalent to deep copy, and the basic data types of new objects and original objects do not affect each other.

let obj1 = {a: {b: 1},c: 2};
let obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj1.c = 3;
obj2.a.b // 2
obj2.c // 2
  1. Object extension operator, deconstruction assignment
let obj = { x: 1, y: 2, a:{b: 4} };
let {...z } = obj ;
obj.x = 10;
obj.a.b = 55
z // { x: 1, y: 2, a:{b: 55} }

Other methods include: Array.prototype.concat(), Array.prototype.slice()

Deep copy:

Deep copy is to open up a memory address in heap memory for complex data types to store copied objects. The newly created and original objects are independent of each other. They have different addresses and do not affect each other.

Common deep copy methods

  1. JSON.parse(JSON.stringify())
    Use JSON.stringify to convert the object into a JSON string, and then use JSON.parse() to parse the string into a new object. The new object will open up a new stack and realize deep copy.
let arr1 = {a:1,b:2,c:{d: ' test'}};
let arr2 = JSON.parse(JSON.stringify(arr1));
arr2.c.d= 'replace';
arr2  // {a:1,b:2,c:{d: ' replace'}}

Note that in the JSON specification, there is only null type, and there is no undefined type. The deep copy will ignore the undefined type attribute, and the null type will not. Therefore, attribute loss may occur during the conversion process.

  1. Recursive Method
    You can also use recursive methods to copy each attribute bit by bit.
let obj1 = {
	label: "testNm",
    value: {
        age: 18
    }
};
// Judge data type
function checkedType(target) {
	return Object.prototype.toString.call(target).slice(8, -1)
}
function deepClone(obj) {
    var tempObj = {};
    for(var key in obj){
       if(checkedType(obj[key]) ==='Object' || checkedType(obj[key]) ==='Array'){ 
          tempObj[key] = deepClone(obj[key]);
       }else{
           tempObj[key] = obj[key];
       }
    }
    return tempObj;
}
var obj2 = deepClone(obj1);
obj1.value.age = 19;
obj2; //{label: "testNm",value:{age:1}}

Topics: Javascript Front-end ECMAScript