惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
MongoDB | Blog
MongoDB | Blog
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
J
Java Code Geeks
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
IT之家
IT之家
Vercel News
Vercel News
C
Check Point Blog
Google DeepMind News
Google DeepMind News
月光博客
月光博客
D
DataBreaches.Net
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
Y
Y Combinator Blog
Hugging Face - Blog
Hugging Face - Blog

张鑫旭-鑫空间-鑫生活

1分钟速度了解text-align match-parent声明 « 张鑫旭-鑫空间-鑫生活 光标的形状也能设置了,就是CSS caret-shape属性 « 张鑫旭-鑫空间-鑫生活 pretext与文字四面环绕效果的实现 « 张鑫旭-鑫空间-鑫生活 JSON.rawJSON方法的作用是什么? « 张鑫旭-鑫空间-鑫生活 如何使用CSS判断鼠标从哪个方向进入元素? « 张鑫旭-鑫空间-鑫生活 借助mediabunny纯JS实现视频水印、剪裁、合成等功能 « 张鑫旭-鑫空间-鑫生活 务必谨慎使用JS WeakRef弱引用 « 张鑫旭-鑫空间-鑫生活 CSS六边形头像的实现与蜂巢布局 « 张鑫旭-鑫空间-鑫生活 CSS corner-shape与背景底纹技术 « 张鑫旭-鑫空间-鑫生活 浅学WebTransport API:下一代Web双向通信技术 « 张鑫旭-鑫空间-鑫生活 HTML dialog元素新支持了closedBy属性 « 张鑫旭-鑫空间-鑫生活 HTML interestfor属性与悬停popover交互效果 « 张鑫旭-鑫空间-鑫生活 CSS3模拟window7炫酷界面效果展示 « 张鑫旭-鑫空间-鑫生活 图片旋转+剪裁js插件(兼容各浏览器) « 张鑫旭-鑫空间-鑫生活 纯js无图片无外链CSS图片幻灯片播放插件 « 张鑫旭-鑫空间-鑫生活 CSS border三角、圆角图形生成技术简介 « 张鑫旭-鑫空间-鑫生活 CSS “渐进增强”在web制作中常见应用举例 « 张鑫旭-鑫空间-鑫生活 让IE6/IE7/IE8浏览器支持CSS3属性 « 张鑫旭-鑫空间-鑫生活 jQuery – 高亮动态显示页面HTML代码插件 « 张鑫旭-鑫空间-鑫生活 翻译 – 逐渐消失的Flash网站 « 张鑫旭-鑫空间-鑫生活 CSS reset的重新审视 – 避免样式重置 « 张鑫旭-鑫空间-鑫生活 新浪微博插入话题后部分文字选中的js实现 « 张鑫旭-鑫空间-鑫生活 JavaScript实现http地址自动检测并添加URL链接 « 张鑫旭-鑫空间-鑫生活 CSS实现兼容性的渐变背景(gradient)效果 « 张鑫旭-鑫空间-鑫生活 CSS content内容生成技术以及应用 « 张鑫旭-鑫空间-鑫生活 CSS gradient渐变之webkit核心浏览器下的使用 « 张鑫旭-鑫空间-鑫生活 CSS渐变之CSS3 gradient在Firefox3.6下的使用 « 张鑫旭-鑫空间-鑫生活 区分IE8/IE7/IE6及其他浏览器-CSS “\9″hack « 张鑫旭-鑫空间-鑫生活 CSS实现跨浏览器兼容性的盒阴影效果 « 张鑫旭-鑫空间-鑫生活 CSS3&HTML5各浏览器支持情况一览表 « 张鑫旭-鑫空间-鑫生活
JS原生的深拷贝API structuredClone函数简介 « 张鑫旭-鑫空...
张鑫旭,zhangxinxu · 2025-01-22 · via 张鑫旭-鑫空间-鑫生活

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()方法兼容性还是不错的,目前所有常见浏览器都已经支持了,如下截图所示:

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);

可以看到,本应实时显示当下时间的属性值变成了固定死的字符串值(也可以看截图运行结果),这并不是我们希望看到的。

JSON方法变成字符串示意

而浏览器提供的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复制示意

当然,还包括很多其他类型的对象也是如此,包括: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 })

DOM对象不能复制

函数也不能复制:

// 会报错
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天,还真是富裕的假期。

就是过年很多鱼塘不开门,想要出去钓鱼,还有些困难。

唉,再说吧。

话不多说,祝大家蛇年快乐,万事如意。

在家要是无聊,可以看看技术书籍

HTML并不简单书封

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11509

(本篇完)