这篇文章发布于 2025年01月22日,星期三,23:22,归类于 JS API。 阅读 10740 次, 今日 15 次 一条评论
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.523)
- 小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)
- 利用HTML5 Web Audio API给网页JS交互增加声音 (RANDOM - 0.019)

感觉限制还是挺多的