对象与原始类型的根本区别之一是,对象是“通过引用”存储和复制的,而原始类型:字符串、数字、布尔值等 —— 总是“作为一个整体”复制。赋值了对象的变量存储的不是对象本身,而是该对象“在内存中的地址” —— 换句话说就是对该对象的“引用”。
接下来我们来看看如何拷贝对象。
浅拷贝
在JavaScript中复制一个对象几乎都是浅层的(只拷贝一层),而不是深层的。这意味着对一个对象深度嵌套的值的做出修改,同时也将修改它的副本上的值。因为它们引用的是同一个对象。
浅拷贝常用的方法
for...in
循环
for...in
语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。可以通过for in实现浅拷贝。
|
|
Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
|
|
嵌套对象的拷贝
|
|
原来的对象也被修改了。
spread 扩展运算符
|
|
深拷贝
深拷贝是指将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
deepClone
前面我们通过for in 写了一个 shallowCopy
,我们将它改名为 deepCopy
。通过属性是否为引用类型,递归调用 deepCopy
,来实现深拷贝。
|
|
对象转换为 JSON 字符串,再转换为对象
|
|
JSON.parse(JSON.stringify(obj))
可比上面 deepClone 函数简洁多了,但不是好的实践,因为它有以下缺点:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象
- 如果 data 里有NaN、Infinity和-Infinity,则序列化的结果会变成null
- 致命缺陷,性能差
structuredClone
structuredClone
是 HTML5 一个新的 API,它可以实现深拷贝。
|
|
该方法还支持把原始值中的 transferable objects (en-US) (可转移对象) 转移到新对象,而不是把属性引用拷贝过去。 可转移对象与原始对象分离并附加到新对象;它们不可以在原始对象中访问被访问到。
目前主流浏览器和js运行时都支持该方法,可放心食用。