Home > Web Front-end > JS Tutorial > body text

Detailed explanation of the steps for using deep and shallow copying of JS

php中世界最好的语言
Release: 2018-04-12 10:52:34
Original
1411 people have browsed it

This time I will give you a detailed explanation of the steps for using JS's deep and shallow copy. What are the precautions when using JS's deep and shallow copy? Here are practical cases, let's take a look.

When it comes to deep and shallow copying, we must first mention the data type of JavaScript. The previous article JavaScript Basics - Data Types talked about it very much. Now that it’s clear, I won’t say much more here.

What you need to know is one thing: JavaScript data types are divided into basic data types and reference data types.

For the copy of basic data types, there is no difference between dark and shallow copies. What we call dark and shallow copies are all for reference data types.

Shallow copy

Shallow copy means that only the reference is copied, but the real value is not copied.

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); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,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 after login

The above code is the simplest use of = assignment operator to implement a shallow copy. You can clearly see that with the changes of cloneArray and cloneObj, originArray and originObj also changed accordingly.

Deep copy

A deep copy is a complete copy of the target. Unlike a shallow copy, which only copies a layer of references, even the values ​​are copied.

As long as a deep copy is made, they will never interact with each other and no one will affect anyone else.

Currently there are not many methods to implement deep copy, there are mainly two methods:

  1. Utilize parse and stringify in JSON objects

  2. Use recursion to re-create objects at each layer and assign values

JSON.stringify/parse method

Let’s take a look at these two methods first:

The JSON.stringify() method converts a JavaScript value to a JSON string.

JSON.stringify Converts a JavaScript value into 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 into a JavaScript value or object.

It’s easy to understand, it’s the conversion between JavaScript values ​​and JSON strings.

Can it achieve 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 after login
It is indeed a deep copy and very convenient. However, this method can only be applied to some simple situations. For example, the following object is not applicable:

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 after login
It was found that some attributes were missing in

cloneObj . . . Why?

Found the reason on 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 will be ignored during the conversion process. . .

Understand, that is to say, if the object contains a function (very common), you cannot use this method to perform deep copy.

Recursive method

The idea of ​​recursion is very simple. It is to implement the operation of creating an object -> object assignment for each layer of data. The code is simple and crude:

function deepClone(source){
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for(let keys in source){ // 遍历目标
 if(source.hasOwnProperty(keys)){
 if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
 targetObj[keys] = source[keys].constructor === Array ? [] : {};
 targetObj[keys] = deepClone(source[keys]);
 }else{ // 如果不是,就直接赋值
 targetObj[keys] = source[keys];
 }
 } 
 }
 return targetObj;
}
Copy after login
Let’s try it:

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 after login
Can. Then try the one with functions:

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 after login
That's ok too. Done.

Do you think this is the end? ? of course not.

Copy method in JavaScript

我们知道在 JavaScript 中,数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。

同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。

那它们是浅拷贝还是深拷贝呢?

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.

该方法可以连接两个或者更多的数组,但是它不会修改已存在的数组,而是返回一个新数组。

看着这意思,很像是深拷贝啊,我们来试试:

const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
Copy after login

看上去是深拷贝的。

我们来考虑一个问题,如果这个对象是多层的,会怎样。

const originArray = [1,[1,2,3],{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],{a:2}]
Copy after login

originArray 中含有数组 [1,2,3] 和对象 {a:1},如果我们直接修改数组和对象,不会影响 originArray,但是我们修改数组 [1,2,3] 或对象 {a:1} 时,发现 originArray 也发生了变化。

结论:concat 只是对数组的第一层进行深拷贝。

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.

解释中都直接写道是 a shallow copy 了 ~

但是,并不是!

const originArray = [1,2,3,4,5];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
Copy after login

同样地,我们试试多层的数组。

const originArray = [1,[1,2,3],{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],{a:2}]
Copy after login

果然,结果和 concat 是一样的。

结论:slice 只是对数组的第一层进行深拷贝。

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.

复制复制复制。

那到底是浅拷贝还是深拷贝呢?

自己试试吧。。

结论:Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。

... 展开运算符

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]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}
Copy after login

结论:... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。

首层浅拷贝

我们知道了,会有一种情况,就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。

我们可以自己实现一个这样的函数:

function shallowClone(source) {
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for (let keys in source) { // 遍历目标
 if (source.hasOwnProperty(keys)) {
 targetObj[keys] = source[keys];
 }
 }
 return targetObj;
}
Copy after login

我们来测试一下:

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 after login

经过上面的修改,cloneObj 不用说,肯定是 {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}} 了,那 originObj 呢?刚刚我们验证了 cloneObj === originObj 是 false,说明这两个对象引用地址不同啊,那应该就是修改了 cloneObj 并不影响 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 after login

What happend?

originObj 中关于 a、c都没被影响,但是 d 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?

原来是这样:

  1. 从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。

  2. so,第二层后的目标都只是复制了一个引用,也就是浅拷贝。

总结

  1. Assignment operator = implements a shallow copy, which only copies the reference value of the object;

  2. The copy methods of arrays and objects in JavaScript are all "first-level shallow copies" ;

  3. JSON.stringify implements deep copy, but it has requirements for the target object;

  4. If you want a true deep copy, Please recurse.

# I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

Detailed explanation of http request and loading display usage of Vue2.0

node’s process and child_process modules Detailed explanation of usage

The above is the detailed content of Detailed explanation of the steps for using deep and shallow copying of JS. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!