Home > Article > Web Front-end > Basic JavaScript tips (picture and text tutorials, detailed answers for you)
Preface
When it comes to deep and shallow copying, we must first mention the data types of JavaScript. The previous article JavaScript Basics - Data Types made it very clear. , not much to say 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 deep and shallow copies. What we call deep and shallow copies are 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'}}
The above code is the simplest way to use the = assignment operator to implement a shallow copy. It can be clearly seen that as cloneArray and cloneObj change, originArray and originObj also change.
Deep copy
Deep copy is a complete copy of the target. Unlike shallow copy, which only copies a layer of references, even the value is 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 ways to implement deep copy, there are mainly two:
Use parse and stringify in the JSON object
Use recursion to recreate each layer Object and assign value
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 is to convert a JSON string into a JavaScript value or object.
It’s easy to understand, it’s the conversion of 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'}};
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"}
Found that some attributes are 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, that is, to create 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; }
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'}}; 可以。那再试试带有函数的: 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: ƒ}
Also. Done.
Do you think this is the end? ? of course not.
Copy method in JavaScript
We know that in JavaScript, arrays have two methods, concat and slice, which can copy the original array. Neither of these two methods will modify the original array. array, instead returning a new, modified array.
At the same time, ES6 introduced the Object.assgn method and the... expansion operator to 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.
The The method can concatenate two or more arrays, but it does not modify the existing array, but returns a new array.
Looking at this meaning, 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); // [1,2,3,4,5,6] console.log(originArray); [1,2,3,4,5];
It looks like a deep copy.
Let’s consider a problem, what will happen if this object is multi-layered.
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}]
originArray contains array [1,2,3] and object {a:1}. If we modify the array and object directly, it will not affect originArray, but we modify the array [1,2,3] Or object {a:1}, it is found that originArray has also changed.
Conclusion: concat only 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.
The explanation directly states that it is a shallow copy~
But, it is not!
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];
Similarly, let’s try a multi-level array.
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}]
Sure enough, the result is the same as concat.
Conclusion: slice only 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. .
结论: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}}
结论:... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。
首层浅拷贝
我们知道了,会有一种情况,就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。
我们可以自己实现一个这样的函数:
function shallowClone(source) { const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象 for (let keys in source) { // 遍历目标 if (source.hasOwnProperty(keys)) { targetObj[keys] = source[keys]; } } return targetObj; }
我们来测试一下:
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';
经过上面的修改,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'}}
What happend?
originObj 中关于 a、c都没被影响,但是 d 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?
原来是这样:
从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。
so,第二层后的目标都只是复制了一个引用,也就是浅拷贝。
总结
赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;
JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;
JSON.stringify 实现的是深拷贝,但是对目标对象有要求;
若想真正意义上的深拷贝,请递归。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
用p5.js制作烟花特效的示例代码_javascript技巧
The above is the detailed content of Basic JavaScript tips (picture and text tutorials, detailed answers for you). For more information, please follow other related articles on the PHP Chinese website!