JavaScript clone
2023-1-27 日,第 623 期 JavaScript Weekly 介绍了最新的原生深拷贝函数 structuredClone(),借此总结一下 JavaScript 中 clone 相关的方法。
JavaScript 中的数据类型分为原始类型和引用类型,他们的区别之一,就是在拷贝时的表现不一样。
- 原始类型存储在栈内存中,每次拷贝都会生成一份新的数据,对新数据的任何操作都不会影响原有数据。
- 引用类型存储在堆内存中,拷贝时只会拷贝指向对象的指针,修改新数据会影响原有数据。
浅拷贝 shallow clone
原始类型生成新数据;引用类型生成新的指针,还是指向原有的对象。
Object.assign
1 | let user = { |
Spread 语法(对象解构)
1 | let user = { |
深拷贝 deep clone
JSON.parse(JSON.stringify())
1 | let user = { |
缺点
- 无法处理递归数据解构
- 无法处理部分 JS 内置数据类型:Map, Set, Date, RegExp, ArrayBuffer.
- 无法拷贝对象的方法
1 | const obj = { |
lodash.cloneDeep()
- 使用 Object.prototype.toString.call 来获取详细类型,不同类型分别处理;
- 递归克隆,数组遍历项,对象遍历属性;
- 使用 Array/Map 缓存引用,解决循环引用的问题;
- loadsh 不会拷贝函数,将返回 {}。 因为克隆函数没有实际意义,公用同一个函数也没问题。 而且涉及到 科里化,this,闭包等问题,无法克隆相等价值的函数。
- 非要克隆函数的话,可以使用
new Function('return ' + fn.toString())()
缺点
需要额外装包。
structuredClone(结构化克隆)
2022 年,HTML Living Standard 新增了一个全局函数 structuredClone,可以直接深拷贝引用类型。
1 | const obj = { |
浏览器支持情况(2023 年 1 月 30 日):
缺点
- 原型链丢失,如果在类实例中使用 structuredClone() ,那么将得到一个普通对象;
- structuredClone 同样不会克隆函数;
- 有些值是不能够被结构化的,比如 Error 对象和 DOM 节点,structuredClone 不会克隆这些对象;
参考
- javascript.info
- MDN
- builder.io
- web.dev