First, “deep copy” and “shallow copy” difference
For this question, consider the use or origin of deep and shallow copies, which is why this problem arises.
Let’s start with some javascript basics.
[1] javascript variables contain values of two different data types: base and reference.
(1) Basic type values refer to simple data segments, including those newly added in ES6. There are 6 types in total, as follows:
String, Number, Boolean, Null, Undefined, Symbol
② Reference type values refer to objects that may consist of multiple values, as follows:
Object (Object, Array, Function)
When assigning a value to a variable, the parser must determine whether the value is a primitive or referential value. Basic data types are accessed by value because you can manipulate the actual values stored in variables.
The value of a reference type is an object held in memory. Unlike other languages, JavaScript does not allow direct access to locations in memory, meaning you cannot directly manipulate the memory space of an object. When you manipulate an object, you’re actually manipulating a reference to the object rather than the actual object.
[2] storage of javascript variables — stack and heap
Stack: Automatically allocated memory space, automatically released by the system, which holds the basic type of value and reference type address
Heap: Dynamically allocated memory that varies in size and is not automatically freed. It holds the value of the reference type.
[3] javascript value passing and address passing
The biggest difference between base types and reference types is really the difference between value and address
Value passing: Base types use value passing.
let a = 10; // Define a variable a and assign it to 10
let b = a; // Assign a value of 10 to b (a and B are both basic types, passing values)
b++; / / b plus
console.log(a, b) / / 10 and 11Copy the code
Address pass: The reference type is address pass, assigning an address stored in stack memory to the variable received.
let a = ['a'.'b'.'c']; // Define an array a and assign it to it
let b = a; // Array is a reference type, which uses address passing to assign the address of A to B
b.push('d'); // Add a 'd' element to the b array
console.log(a) // ['a', 'b', 'c', 'd']
console.log(b) // ['a', 'b', 'c', 'd']Copy the code
Analysis: Since both a and B are reference types, using address passing, that is, A passes the address to B, then A and B must refer to the same address (the address of the reference type is stored in stack memory), and this address refers to the value of the reference type in heap memory. When B changes this value, it also changes a’s value, because a’s address also points to this value. For example, if A rents a room and gives B the address of the room, and B finds the room by using the address, then any changes B makes to the room (adding some greenery) must also be visible to A.
So how to solve the problem above, is to use shallow copy or deep copy. JS basic types do not have shallow copy or deep copy issues, mainly for reference types
[4] Summary of differences between shallow copy and deep copy
2. What it means:
Shallow copy – The copy level is shallow.
Deep copy: The copy level is deeper.
Specific:
Shallow copy – When copying an object, only the first layer of key-value pairs are independently copied. If there are other objects in the object, only the addresses of nested objects are copied
Deep copy – Deep copy refers to copying an object completely. Even if the objects are nested, they are separated from each other. Modifying the properties of one object does not affect the properties of the other. In fact, as long as the recursion, those properties are still the value of the object into the object again to copy.
Shallow copy case
The shallow copy solution is to set up a new object obj2 and assign the value of obj1 to obj2 by traversing it.
// A shallow copy of the array
let arr1 = [1.2.3]
let arr2 = []
for (let i in arr1) {
arr2[i] = arr1[i]
}
arr2.push(4)
console.log(arr1) / / [1, 2, 3]
console.log(arr2) // [1, 2, 3, 4]
// A shallow copy of the object
let obj1 = {
a: '1'.b: '2'.c: '3'
}
let obj2 = {}
for (let i in obj1) {
obj2[i] = obj1[i]
}
obj2.d = '4'
console.log(obj1) // {a: "1", b: "2", c: "3"}
console.log(obj2) // {a: "1", b: "2", c: "3", d: "4"}
// Shallow copy function encapsulation
function shallowCopy(obj1, obj2) {
for(var key in obj1) {
obj2[key] = obj1[key]
}
}Copy the code
However, the above code can only achieve a layer of copy, unable to carry out a deep copy, encapsulation function again through the object array nesting test as follows:
// Shallow copy function encapsulation
function shallowCopy(obj1, obj2) {
for(var key in obj1) {
obj2[key] = obj1[key]
}
}
// A shallow copy of the object
let obj1 = {
a: '1'.b: '2'.c: {
name: 'Demi'}}let obj2 = {}
shallowCopy(obj1, obj2) // Copy the data of obj1 to obj2
obj2.c.name = 'dingFY'
console.log(obj1) // {a: "1", b: "2", c: {name: 'dingFY'}}
console.log(obj2) // {a: "1", b: "2", c: {name: 'dingFY'}}Copy the code
It turns out that if there are objects in the object, then only the address of the nested object can be copied, and no further copy can be made. When the value of obj2 nested object C is changed, the value of obj1 nested object C also changes
At this point we can use deep copy to complete, the so-called deep copy, is able to achieve the true sense of the array and object copy, we recursively call the shallow copy way to achieve.
Deep copy case
// Deep copy function encapsulation
function deepCopy(obj) {
// Determine whether to create an array or object based on the type of obj
let newObj = Array.isArray(obj)? [] : {};
// Check that the obj passed exists and is of type object
if (obj && typeof obj === 'object') {
for(key in obj) {
// If the children of obj are objects, the operation is recursive
if(obj[key] && typeof obj[key] ==='object') {
newObj[key] = deepCopy(obj[key])
} else {
// // If the child element of obj is not an object, it is directly assigned
newObj[key] = obj[key]
}
}
}
return newObj // Return a new object
}
// Deep copy of the object
let obj1 = {
a: '1'.b: '2'.c: {
name: 'Demi'}}let obj2 = deepCopy(obj1) // Copy the data of obj1 to obj2
obj2.c.name = 'dingFY'
console.log(obj1) // {a: "1", b: "2", c: {name: 'Demi'}}
console.log(obj2) // {a: "1", b: "2", c: {name: 'dingFY'}}Copy the code
It turns out that the above code can achieve deep cloning.
A shallow copy of the array
In the case of arrays, we can take advantage of array methods such as slice and concat to return a new array.
【 1 】 Array concat ()
let arr = ['one'.'two'.'three'];
let newArr = arr.concat();
newArr.push('four')
console.log(arr) // ["one", "two", "three"]
console.log(newArr) // ["one", "two", "three", "four"]Copy the code
【 2 】 Array. Slice ()
let arr = ['one'.'two'.'three'];
let newArr = arr.slice();
newArr.push('four')
console.log(arr) // ["one", "two", "three"]
console.log(newArr) // ["one", "two", "three", "four"]Copy the code
Deep copy of array
Here’s a trick that applies to objects as well as arrays! One problem, however, is that you can’t copy functions
let arr = {
a: 'one'.b: 'two'.c: {
name: 'Demi'}};let newArr = JSON.parse( JSON.stringify(arr) );
newArr.c.name = 'dingFY'
console.log(arr); // {a: "one", b: "two", c: {name: 'Demi'}}
console.log(newArr); // {a: "one", b: "two", c: {name: 'dingFY'}}
// Test whether the function can be copied
let arr = {
a: 'one'.b: (a)= >{
console.log('test')}};let newArr = JSON.parse( JSON.stringify(arr) );
console.log(arr); // {a: "one", b: ()=>{console.log('test')}}
console.log(newArr); // {a: "one"} // The function failed to copyCopy the code
Shallow copy of the object
The object.assign () method copies the enumerable properties of any number of source objects to the target Object and returns the target Object. But object.assign () does a shallow copy
let arr = {
a: 'one'.b: 'two'.c: 'three'
};
let newArr = Object.assign({}, arr)
newArr.d = 'four'
console.log(arr); // {a: "one", b: "two", c: "three"}
console.log(newArr); // {a: "one", b: "two", c: "three", d: "four"}Copy the code
Shallow copy encapsulation method
How it works: Iterate over an object, then place the property and its value in a new object
let shallowCopy = function (obj) {
// Only objects are copied
if (typeofobj ! = ='object') return;
// Determine whether to create an array or object based on the type of obj
let newObj = obj instanceof Array ? [] : {};
// Iterate over obj and determine if it is obj's property to copy
for (let key in obj) {
if(obj.hasOwnProperty(key)) { newObj[key] = obj[key]; }}return newObj;
}Copy the code
Deep copy encapsulation method
Principle: We determine the type of property value when copying, if it is an object, we recursively call the deep copy function
let deepCopy = function (obj) {
// Only objects are copied
if (typeofobj ! = ='object') return;
// Determine whether to create an array or object based on the type of obj
let newObj = obj instanceof Array ? [] : {};
// Iterate over obj and determine if it is obj's property to copy
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// If obj's child property is an object, the operation is recursive, otherwise the assignment is direct
newObj[key] = typeof obj[key] === 'object'? deepCopy(obj[key]) : obj[key]; }}return newObj;
}Copy the code
The article is updated every week. You can search “Front-end highlights” on wechat to read it in the first time, and reply to [Books] to get 200G video materials and 30 PDF books