How to Clone
日常开发中会涉及到对象拷贝相关的运用,怎么拷贝是个值得探讨的问题。
浅拷贝
JavaScript
一共有七种基础类型,分别为string
,number
,boolean
,null
,undefined
,symbol
,bigint
,浅拷贝遇到基础类型的值会直接重新拷贝该值,而对于object
类型的值,则会拷贝该值的引用地址。
Object.assign()
我们可以使用Object.assign({}, targetObject)
来进行浅拷贝,如下所示:
1 | const bar = { |
可以观察到对拷贝之后的newBar.bar
进行重新赋值操作,依然可以影响到原来的bar.bar
,这也是浅拷贝的主要问题。
Object Spread Operation
我们也可以使用对象扩展运算符来进行浅拷贝,如下所示:
1 | const bar = { |
总的来说,浅拷贝使用简单明了,但是对于非基础类型值的处理而导致的问题,往往让我们放弃浅拷贝而转用深拷贝。
深拷贝
深拷贝则没有上述浅拷贝带来的问题,对于object
等类型的值的处理是重新生成相同的值,这样新值和旧值不会相互影响,解决了浅拷贝的问题。
JSON.parse(JSON.stringify(targetObj))
使用JSON
相关的方法来对一个对象深拷贝是常用的深拷贝方法之一,优点是调用简单,且速度较快,如下所示:
1 | const bar = { |
缺点也比较多,比如对递归引用的值无法处理,以及对Set
,Map
,Date
等类型的对象无法处理等,但是一般来说后端返回的内容不会涉及到如上数据类型,所以JSON.parse(JSON.stringify(targetObj))
适用于大部分的情况。
Recursive
当然我们也可以使用递归的思想来解决深拷贝的问题,如下所示:
1 | const bar = { |
可以看到deepClone
简单实现了一个深拷贝,当然这里的deepClone
还有很多情况没有考虑,比如对Set
等数据结构的处理,这种情况下我们可以借助第三方库如lodash
实现,感兴趣的可以看一下lodash
关于这块的处理cloneDeep(obj)
structuredClone()
那么有没有比较简单的原生支持的深拷贝,既能避免JSON.parse(JSON.stringify(targetObj))
不能处理Set
,Map
等数据结构的问题,又不需要引入lodash
等三方库呢?答案是有的,那就是structuredClone()。
structuredClone()
目前只有 Firefox 94,Deno 1.14 以及 Node.js 17.0.0 支持,但是实际上相关算法已经在较早之前运用到了 MessageChannel
,Notification
等地方,以MessageChannel
为例:
1 | function structuredClone(originObj) { |
可以看到structuredClone()
可以正常的处理Set
,Date
等数据结构,并且成功进行了深拷贝。
总结
- 日常开发中
JSON.parse(JSON.stringify(targetObj))
基本上可以应对大部分情况,且较为高效 - 涉及到
Map
,Set
等数据结构的情况下,考虑使用structuredClone(targetObj)
- 最后我们仍然可以借用
lodash
等三方库帮我们处理深拷贝问题