There are many articles about deep copying on the Internet, but the quality varies, many of them are not well thought out, and the writing methods are relatively crude and unsatisfactory. This article aims to complete a perfect deep copy. If you have any questions after reading it, please feel free to add and improve it.
To evaluate whether a deep copy is complete, please check whether the following questions have been implemented:
Can basic type data
be copied?
Keys and values are both basic typesOrdinary This article will give you a detailed understanding of deep copying in JavaScriptects
Can they be copied?
Symbol
Can the key of an This article will give you a detailed understanding of deep copying in JavaScriptect be copied? Can
Date
and RegExp
This article will give you a detailed understanding of deep copying in JavaScriptect types be copied? Can
Map
and Set
This article will give you a detailed understanding of deep copying in JavaScriptect types be copied?
Function
Can the This article will give you a detailed understanding of deep copying in JavaScriptect type be copied? (We generally don’t use deep copying for functions)
Can the prototype
of an This article will give you a detailed understanding of deep copying in JavaScriptect be copied?
Non-enumerable properties
Can it be copied?
Circular reference
Can it be copied?
how? Is the deep copy you wrote perfect enough?
The final code version is given directly here for the convenience of those who want to know quickly. Of course, if you want to understand step by step, you can continue to view the rest of the article:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
First look at the JS data type diagram ( Except Object
, all others are basic types):
In JavaScript, the copying of basic type values is to directly copy a new and identical data. The two data are mutually exclusive. Independent and not affecting each other. The copying of reference type values (Object type) is to pass the reference of the This article will give you a detailed understanding of deep copying in JavaScriptect (that is, the memory address where the This article will give you a detailed understanding of deep copying in JavaScriptect is located, that is, the pointer to the This article will give you a detailed understanding of deep copying in JavaScriptect), which is equivalent to multiple variables pointing to the same This article will give you a detailed understanding of deep copying in JavaScriptect. Then as long as one of the variables has a reference to the This article will give you a detailed understanding of deep copying in JavaScriptect When modified, the This article will give you a detailed understanding of deep copying in JavaScriptects pointed to by other variables will also be modified (because they point to the same This article will give you a detailed understanding of deep copying in JavaScriptect). As shown below:
Deep and shallow copy mainly targets the Object type, and the value of the basic type itself is copied exactly the same One copy, no distinction between dark and light copies. Here we first give the copy This article will give you a detailed understanding of deep copying in JavaScriptect for testing. You can use this This article will give you a detailed understanding of deep copying in JavaScript
This article will give you a detailed understanding of deep copying in JavaScriptect to test whether the deep copy function you wrote is perfect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
This article will give you a detailed understanding of deep copying in JavaScript
The This article will give you a detailed understanding of deep copying in JavaScriptect is in Result in Chrome:
Shallow copy: Create a new Object to accept the This article will give you a detailed understanding of deep copying in JavaScriptect value that you want to re-copy or reference. If the This article will give you a detailed understanding of deep copying in JavaScriptect attribute is a basic data type, the value of the basic type is copied to the new This article will give you a detailed understanding of deep copying in JavaScriptect; but if the attribute is a reference data type, the address in the memory is copied. If one of the This article will give you a detailed understanding of deep copying in JavaScriptects changes the address pointed to by the memory, Object will definitely affect another This article will give you a detailed understanding of deep copying in JavaScriptect.
First let’s take a look at some shallow copy methods (for details, click on the hyperlinks to the corresponding methods): MethodUse MethodNotes##Object.assign()Object.assign(target, ... sources) 2. The non-enumerable properties of the This article will give you a detailed understanding of deep copying in JavaScriptect will not be copied; Expand syntaxlet This article will give you a detailed understanding of deep copying in JavaScriptClone = { ...This article will give you a detailed understanding of deep copying in JavaScript };Object.assign ()Array.prototype.concat() copy arrayconst new_array = old_array.concat(value1[, value2[, ...[, valueN] ]])Array.prototype.slice() copy arrayarr.slice([begin[, end]])这里只列举了常用的几种方式,除此之外当然还有其他更多的方式。注意,我们直接使用=
赋值不是浅拷贝,因为它是直接指向同一个对象了,并没有返回一个新对象。
手动实现一个浅拷贝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
从上面这段代码可以看出,利用类型判断(查看typeof),针对引用类型的对象进行 for 循环遍历对象属性赋值给目标对象的属性(for...in
语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性,包含原型上的属性。查看for…in),基本就可以手工实现一个浅拷贝的代码了。
深拷贝:创建一个新的对象,将一个对象从内存中完整地拷贝出来一份给该新对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。
看看现存的一些深拷贝的方法:
JSON.stringfy()
其实就是将一个 JavaScript 对象或值转换为 JSON 字符串,最后再用 JSON.parse()
的方法将JSON 字符串生成一个新的对象。(点这了解:JSON.stringfy()、JSON.parse())
使用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
浏览器执行结果:
从以上结果我们可知JSON.stringfy()
存在以下一些问题:
执行会报错:存在BigInt
类型、循环引用。
拷贝Date
引用类型会变成字符串。
键值会消失:对象的值中为Function
、Undefined
、Symbol
这几种类型,。
键值变成空对象:对象的值中为Map
、Set
、RegExp
这几种类型。
无法拷贝:不可枚举属性、对象的原型链。
补充:其他更详细的内容请查看官方文档:JSON.stringify()
由于以上种种限制条件,JSON.stringfy()
方式仅限于深拷贝一些普通的对象,对于更复杂的数据类型,我们需要另寻他路。
手动递归实现深拷贝,我们只需要完成以下2点即可:
对于基础类型,我们只需要简单地赋值即可(使用=
)。
对于引用类型,我们需要创建新的对象,并通过遍历键来赋值对应的值,这个过程中如果遇到 Object 类型还需要再次进行遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
浏览器执行结果:
该基础版本存在许多问题:
不能处理循环引用。
只考虑了Object
对象,而Array
对象、Date
对象、RegExp
对象、Map
对象、Set
对象都变成了Object
对象,且值也不正确。
丢失了属性名为Symbol
类型的属性。
丢失了不可枚举的属性。
原型上的属性也被添加到拷贝的对象中了。
如果存在循环引用的话,以上代码会导致无限递归,从而使得堆栈溢出。如下例子:
1 2 3 4 5 |
|
对象 a 的键 b 指向对象 b,对象 b 的键 a 指向对象 a,查看a
对象,可以看到是无限循环的:
对对象a
执行深拷贝,会出现死循环,从而耗尽内存,进而报错:堆栈溢出
如何避免这种情况呢?一种简单的方式就是把已添加的对象记录下来,这样下次碰到相同的对象引用时,直接指向记录中的对象即可。要实现这个记录功能,我们可以借助 ES6 推出的 WeakMap
对象,该对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。(WeakMap
相关见这:WeakMap)
针对以上基础版深拷贝存在的缺陷,我们进一步去完善,实现一个完美的深拷贝。
对于基础版深拷贝存在的问题,我们一一改进:
Description: Used to assign the values of all enumerable properties from one or more source This article will give you a detailed understanding of deep copying in JavaScriptects to the target This article will give you a detailed understanding of deep copying in JavaScriptect. It will return the target This article will give you a detailed understanding of deep copying in JavaScriptect. 1. The inherited properties of the This article will give you a detailed understanding of deep copying in JavaScriptect will not be copied; | 3. Symbol type properties can be copied. |
|
Defects and | Almost the same, but if the attributes are all basic type values, it will be more convenient to use the spread operator to perform shallow copy.
|
|
Shallow copy, suitable for arrays of basic type values |
||
Shallow copy, suitable for arrays of basic type values |
存在的问题 | 改进方案 |
---|---|
1. 不能处理循环引用 | 使用 WeakMap 作为一个Hash表来进行查询 |
2. 只考虑了Object 对象 | 当参数为 Date 、RegExp 、Function 、Map 、Set ,则直接生成一个新的实例返回 |
3. 属性名为Symbol 的属性4. 丢失了不可枚举的属性 | 针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys()注: Reflect.ownKeys(This article will give you a detailed understanding of deep copying in JavaScript) 相当于[...Object.getOwnPropertyNames(This article will give you a detailed understanding of deep copying in JavaScript), ...Object.getOwnPropertySymbols(This article will give you a detailed understanding of deep copying in JavaScript)] |
4. 原型上的属性 | Object.getOwnPropertyDescriptors()设置属性描述对象,以及Object.create()方式继承原型链 |
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
|
详细的说明见代码中的注释,更多测试希望大家自己动手尝试验证一下以加深印象。
在遍历 Object
类型数据时,我们需要把 Symbol
类型的键名也考虑进来,所以不能通过 Object.keys
获取键名或 for...in
方式遍历,而是通过Reflect.ownKeys()
获取所有自身的键名(getOwnPropertyNames
和 getOwnPropertySymbols
函数将键名组合成数组也行:[...Object.getOwnPropertyNames(This article will give you a detailed understanding of deep copying in JavaScript), ...Object.getOwnPropertySymbols(This article will give you a detailed understanding of deep copying in JavaScript)]
),然后再遍历递归,最终实现拷贝。
浏览器执行结果:
可以发现我们的cloneObj对象
和原来的This article will give you a detailed understanding of deep copying in JavaScript对象
一模一样,并且修改cloneObj对象
的各个属性都不会对This article will give you a detailed understanding of deep copying in JavaScript对象
造成影响。其他的大家再多尝试体会哦!
【相关推荐:javascript视频教程、编程视频】
The above is the detailed content of This article will give you a detailed understanding of deep copying in JavaScript. For more information, please follow other related articles on the PHP Chinese website!