JavaScript basics – deep and shallow copy
Welcome to star.
You are welcome to correct any mistakes.
Both shallow copy and deep copy are for reference types in JS. Shallow copy only copies references of objects. If the copied objects change, the original objects will also change. Only a deep copy is a true copy of an object.
preface
When it comes to deep copy, we must first mention the data type of JavaScript, the previous article JavaScript basic heart method – data type said very clear, here will not say more.
Just one thing to know: JavaScript data types are divided into basic data types and reference data types.
For copies of primitive data types, there is no difference between shallow copy and deep copy, which are all for reference data types.
Shallow copy
Shallow copy means that only references are copied without the actual values.
const originArray = [1.2.3.4.5];
const originObj = {a:'a'.b:'b'.c: [1.2.3].d: {dd:'dd'}};
const cloneArray = originArray;
const cloneObj = originObj;
console.log(cloneArray); / / [1, 2, 3, 4, 5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
console.log(cloneArray); / / [6]
console.log(originArray); / / [6]
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}Copy the code
The code above is the simplest to implement a shallow copy using the = assignment operator. You can clearly see that originArray and originObj change as cloneArray and cloneObj change.
Deep copy
A deep copy is a full copy of the target, not just a layer of references, but also the values, as in a shallow copy.
As long as the deep copy, they will never communicate, no one will influence each other.
Currently, there are not many ways to implement deep copy. There are two main methods:
- using
JSON
The object of theparse
和stringify
- Objects are recreated and assigned at each level using recursion
JSON. Stringify/parse method
Let’s take a look at these two methods:
The JSON.stringify() method converts a JavaScript value to a JSON string.
Json. stringify converts a JavaScript value to a JSON string.
The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.
Json. parse converts a JSON string to a JavaScript value or object.
It’s a simple conversion between JavaScript values and JSON strings.
Can it implement deep copy? Let’s try it.
const originArray = [1.2.3.4.5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false
const originObj = {a:'a'.b:'b'.c: [1.2.3].d: {dd:'dd'}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1.1.1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); / / {a: 'aa' b: 'b', c:,1,1 [1], d: {dd: 'doubled'}};
console.log(originObj); / / {a: 'a', b: 'b', c: [1, 2, 3], d: {dd: 'dd'}};Copy the code
It is indeed a deep copy, and very convenient. However, this method can only be used in some simple cases. For example, an object like the following does not apply:
const originObj = {
name:'axuebin'.sayHello:function(){
console.log('Hello World'); }}console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}Copy the code
In cloneObj, some attributes are missing… Why is that?
The reason is found in MDN:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in “pure” values like JSON.stringify(function(){}) or JSON.stringify(undefined).
Undefined, function, symbol ignored during conversion…
If an object contains a function (which is common), you cannot use this method for deep copy.
Recursive method
The idea of recursion is very simple, that is, each layer of data to implement a create object -> object assignment operation, simple and simple on the code:
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // Determine whether the target of replication is an array or an object
for(let keys in source){ // Iterate over the target
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object') {// If the value is an object, recurse
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // If not, assign directlytargetObj[keys] = source[keys]; }}}return targetObj;
}Copy the code
Let’s try:
const originObj = {a:'a'.b:'b'.c: [1.2.3].d: {dd:'dd'}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1.1.1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); / / {a: 'aa' b: 'b', c:,1,1 [1], d: {dd: 'doubled'}};
console.log(originObj); / / {a: 'a', b: 'b', c: [1, 2, 3], d: {dd: 'dd'}};Copy the code
You can. Then try one with a function:
const originObj = {
name:'axuebin'.sayHello:function(){
console.log('Hello World'); }}console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = deepClone(originObj);
console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}Copy the code
Can also. Get things done.
Do you think this is the end? Of course not.
Copy methods in JavaScript
We know that in JavaScript, there are two methods for arrays: concat and slice, which make a copy of the original array. Neither method changes the original array, but returns a new modified array.
At the same time, ES6 introduced object.assgn methods and… The expansion operator can also copy objects.
Are they shallow copies or deep copies?
concat
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
This method can concatenate two or more arrays, but instead of modifying an existing array, it returns a new one.
It looks like a deep copy. Let’s try it:
const originArray = [1.2.3.4.5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); / / [6]
console.log(originArray); [1.2.3.4.5];Copy the code
Looks like a deep copy.
Let’s think about what happens if this object is multi-layered.
const originArray = [1[1.2.3] and {a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); / / [1, 1, 2, 3, 4, 2} {a:]Copy the code
An originArray contains arrays [1,2,3] and objects {a:1}. If you modify arrays and objects directly, it does not affect the originArray. However, if you modify arrays [1,2,3] or objects {a:1}, you will find that the originArray changes.
Conclusion:concat
It just makes a deep copy of the first layer of the array.
slice
The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.
Shallow copy (shallow copy
But, no!
const originArray = [1.2.3.4.5];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray.push(6); / / [6]
console.log(originArray); [1.2.3.4.5];Copy the code
Similarly, let’s try a multi-tiered array.
const originArray = [1[1.2.3] and {a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); / / [1, 1, 2, 3, 4, 2} {a:]Copy the code
Sure enough, the results were the same as concat.
Conclusion:slice
It just makes a deep copy of the first layer of the array.
Object.assign()
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
Copy, copy, copy.
Is it a shallow copy or a deep copy?
Try it yourself.
Conclusion:Object.assign()
I’m copying the property value. If the attribute value of the source object is a reference to the object, it copies only that reference value.
. Expansion operator
const originArray = [1.2.3.4.5[6.7.8]].const originObj = {a:1.b: {bb:1}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); / / [1, 2, 3, 4, 5, 6,7,8,9 []]
constcloneObj = {... originObj}; cloneObj.a =2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}Copy the code
Conclusion:.
The implementation is a deep copy of the first layer of the object. What follows is just the copied reference value.
First shallow copy
We know that there is a situation where the first layer of the target object is made a deep copy, followed by a shallow copy, which can be called a “shallow first layer copy”.
We can implement a function like this ourselves:
function shallowClone(source) {
const targetObj = source.constructor === Array ? [] : {}; // Determine whether the target of replication is an array or an object
for (let keys in source) { // Iterate over the target
if(source.hasOwnProperty(keys)) { targetObj[keys] = source[keys]; }}return targetObj;
}Copy the code
Let’s test it out:
const originObj = {a:'a'.b:'b'.c: [1.2.3].d: {dd:'dd'}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a='aa';
cloneObj.c=[1.1.1];
cloneObj.d.dd='surprise';Copy the code
CloneObj {a:’aa’,b:’b’,c:[1,1,1],d:{dd:’surprise’}} If cloneObj === originObj is false, it does not affect originObj.
console.log(cloneObj); / / {a: 'aa' b: 'b', c:,1,1 [1], d: {dd: 'surprise'}}
console.log(originObj); / / {a: 'a', b: 'b', c: [1, 2, 3], d: {dd: 'surprise'}}Copy the code
What happend?
OriginObj does not affect a, c, but an object in D is modified… What about the deep copy? Isn’t the reference address different?
So this is it:
- from
shallowClone
As we can see from the code, we only carried out for the first level of the targetDeep copy
And the second layer starts with a target that we use directly=
Assignment operator to copy. - So, the target after the second layer only copies a reference, that is, a shallow copy.
conclusion
- The assignment operator
=
The implementation is shallow copy, only copy the object reference value; - The native copy method for arrays and objects in JavaScript is “shallow first-layer copy”.
JSON.stringify
Deep copy is implemented, but there are requirements for the target object;- For true deep copy, recurse.