这篇文章发布于 2025年01月22日,星期三,23:22,归类于 JS API。 阅读 1514 次, 今日 39 次 没有评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11509
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。
一、开门即见山
目前,Web浏览器提供了原生的Object对象深度克隆方法structuredClone()
函数。
使用方法很简单,JS代码如下所示:
// 创建一个具有值和循环引用的对象 const original = { name: "zhangxinxu" }; original.itself = original; // 克隆 const clone = structuredClone(original); // 两者对象是不相等的 console.assert(clone !== original); // 两者的值是相等的 console.assert(clone.name === "zhangxinxu"); // 并且保留了循环引用 console.assert(clone.itself === clone);
语法
语法如下:
structuredClone(value, options)
其中:
- value
- 需要被深拷贝的值
- options
- 可选参数,支持一个名为
transfer
的参数值,其值为一组可转移的对象,它们将被移动而不是克隆到返回的对象中。
关于transfer
可选参数transfer
多用在一些大数据传输中(转移相比克隆可以节约内存开销),这里有个案例供大家参考:
const original = new Uint8Array(1024); const clone = structuredClone(original); console.log(original.byteLength); // 1024 console.log(clone.byteLength); // 1024 original[0] = 1; console.log(clone[0]); // 0 // 转移Uint8Array会引发异常,因为它不是可转移对象 // const transferred = structuredClone(original, {transfer: [original]}); // 我们可以转移Uint8Array.buffer const transferred = structuredClone(original, { transfer: [original.buffer] }); console.log(transferred.byteLength); // 1024 console.log(transferred[0]); // 1 // Uint8Array.buffer转移后就无法使用了 console.log(original.byteLength); // 0
二、兼容性与Polyfill方法
window全局的structuredClone()
方法兼容性还是不错的,目前所有常见浏览器都已经支持了,如下截图所示:
考虑到总会有一些用户手机舍不得或者忘记或者懒得升级,面对偏外部用户的产品,建议还是同时引入Polyfill。
structuredClone Polyfill
JS不同于CSS,要是JS某个方法不支持,然后你去运行他,很可能会导致整个页面白屏,这是Vue和React项目中是常有的事情(也包括各类小程序)。
所以,我们需要引入Polyfill,可以试试这个项目:https://github.com/ungap/structured-clone
使用示意:
import structuredClone from '@ungap/structured-clone'; const cloned = structuredClone({any: 'serializable'});
还是很easy的啦。
其实,上面的Polyfill还有不少其他的功能,就等大家自行去探索啦。
三、JSON等方法有什么问题
之前我深度拷贝一个Object对象会使用JSON.parse(JSON.stringify(obj))
来实现,虽然可以满足绝大多数的场景,但有时候会出问题。
例如,当对象的属性值是Date()
对象的时候,案例示意:
const originObj = { name: "zhangxinxu", date: new Date() }; const cloneObj = JSON.parse(JSON.stringify(originObj)); // 结果是 'object' console.log(typeof originObj.date); // 结果是 'string' console.log(typeof cloneObj.date);
可以看到,本应实时显示当下时间的属性值变成了固定死的字符串值(也可以看截图运行结果),这并不是我们希望看到的。
而浏览器提供的structuredClone()
方法则没有这个问题,使用示意:
const originObj = { name: "zhangxinxu", date: new Date() }; const cloneObj = structuredClone(originObj); // 结果是 'object' console.log(typeof originObj.date); // 结果是 'object' console.log(typeof cloneObj.date);
当然,还包括很多其他类型的对象也是如此,包括:Date, Set, Map, Error, RegExp, ArrayBuffer, Blob, File, ImageData等。
点点点或者Object方法的问题
如果需要复制的对象层级简单,那么我们使用点点点,或者Object.assign()
、Object.create()
方法是没问题的,例如:
const originObj = { name: "zhangxinxu" }; // ok没问题 const cloneObj = { ... originObj } // ok没问题 const cloneObj = Object.assign({}, originObj) // ok没问题 const cloneObj = Object.create(originObj)
可如果对象的属性值也是个对象,那么上面的方法就有问题,例如:
const originObj = { name: "zhangxinxu", books: ['CSS世界'] }; // ok没问题 const cloneObj = { ... originObj } cloneObj.books.push('HTML并不简单'); // 结果原对象的books也一起变化了 console.log(originObj.books);
控制台运行结果不会骗人:
四、structuredClone不能的局限
当然,structuredClone
方法也不是万能的,例如DOM对象是不能参与复制的。
// 会报错 structuredClone({ el: document.body })
函数也不能复制:
// 会报错 structuredClone({ fn: () => { } })
属性描述符、setter和getter
标题这些类型的东西也不会被深度复制,比方说像getter,克隆的会是其值,而不是getter函数本身。
structuredClone({ get foo() { return 'bar' } }) // 结果: { foo: 'bar' }
对象原型
原型链也是不会被复制的,因此,如果克隆MyClass的实例,克隆的对象将不再是该类的实例(但该类的所有有效属性都将被克隆)
class MyClass { foo = 'bar' myMethod() { /* ... */ } } const myClass = new MyClass() const cloned = structuredClone(myClass) // 结果 { foo: 'bar' } cloned instanceof myClass // false
五、蛇年快乐
好,本文的内容就这些,应该是春节前的最后一篇文章了,本来以为内容不多,但写着写着,发现里面可讲的东西还不少。
我明天就请假回老家了,算算,可以连休12天,还真是富裕的假期。
就是过年很多鱼塘不开门,想要出去钓鱼,还有些困难。
唉,再说吧。
话不多说,祝大家蛇年快乐,万事如意。
在家要是无聊,可以看看技术书籍
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11509
(本篇完)
- 简单了解ES6/ES2015 Symbol() 方法 (0.524)
- 小tip: DOM appendHTML实现及insertAdjacentHTML (0.262)
- 从今天开始,请叫我Node文本节点处理大师 (0.262)
- 利用剪切板JS API优化输入框的粘贴体验 (0.196)
- JS复制文字到剪切板的极简实现及扩展 (0.196)
- 瞎折腾,使用JS让中文内容莫名其妙、狗屁不通 (0.196)
- js面向数据编程(DOP)一点分享 (0.131)
- 翻译:ECMAScript 5.1简介 (0.131)
- 基于HTML模板和JSON数据的JavaScript交互 (0.131)
- JS前端创建html或json文件并浏览器导出下载 (0.131)
- jQuery图片文本滚动切换插件jCarousel中文翻译与详解 (RANDOM - 0.018)