Node.js 支持 structuredClone API。这是一个全局可用的函数,用于执行深拷贝,自 Node.js 17.0.0 版本开始提供原生支持。
🚀 如何在 Node.js 中使用
structuredClone 是一个全局函数,你可以在任何地方直接调用,无需引入任何模块。
基本语法:
const clonedObject = structuredClone(originalObject);
它还可以接受一个可选的 options 参数,用于转移可转移对象(如 ArrayBuffer),而不是克隆它们。
structuredClone(value, { transfer: [transferableObjects] });
📝 使用示例
1. 基础深拷贝 这是最常用的方式,用于创建一个与原对象完全独立的副本。
const original = {
name: "Node.js",
types: ["JavaScript", "C++"],
details: { stable: true }
};
// 使用 structuredClone 进行深拷贝
const cloned = structuredClone(original);
// 修改克隆对象的属性
cloned.types.push("Rust");
cloned.details.stable = false;
console.log(original.types); // 输出: ['JavaScript', 'C++'] (原数组未改变)
console.log(original.details.stable); // 输出: true (原对象未改变)
console.log(cloned.types); // 输出: ['JavaScript', 'C++', 'Rust']
2. 处理复杂数据类型
structuredClone 的强大之处在于它能正确处理许多 JSON.stringify 无法处理的类型。
const complexOriginal = {
date: new Date(),
regex: /hello/gi,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
circular: {}
};
complexOriginal.circular.self = complexOriginal; // 循环引用
const complexClone = structuredClone(complexOriginal);
console.log(complexClone.date instanceof Date); // 输出: true (而 JSON 方法会将其转为字符串)
console.log(complexClone.regex instanceof RegExp); // 输出: true (而 JSON 方法会将其转为空对象 {})
console.log(complexClone.map instanceof Map); // 输出: true
console.log(complexClone.set instanceof Set); // 输出: true
console.log(complexClone.circular.self === complexClone); // 输出: true (循环引用被保留)
3. 转移 ArrayBuffer
当处理大型二进制数据时,可以使用 transfer 选项将数据的所有权从原对象转移到克隆对象,这是一种零拷贝操作,性能更好。
const buffer = new ArrayBuffer(16);
const int32View = new Int32Array(buffer);
int32View[0] = 42;
const transferred = structuredClone({ buf: buffer }, { transfer: [buffer] });
console.log(transferred.buf.byteLength); // 输出: 16 (克隆对象中的 buffer 仍可用)
// 尝试访问原 buffer 会导致错误,因为它已被转移
// console.log(buffer.byteLength); // 抛出 TypeError: Cannot perform operation on a detached ArrayBuffer
⚠️ 重要限制
尽管功能强大,structuredClone 无法克隆所有内容。以下类型会导致抛出 DataCloneError 异常或被忽略:
- 函数(
Function) - DOM 节点(在 Node.js 中不适用,但在浏览器环境中需要注意)
- 属性描述符、setter 和 getter
- 原型链(克隆后的对象不再继承自原对象的原型)
- Symbol
- WeakMap 和 WeakSet
🔧 兼容旧版本 Node.js
如果你的 Node.js 版本低于 17,直接使用会报错。可以编写一个回退函数来保证代码兼容性:
function deepClone(obj) {
if (typeof structuredClone === 'function') {
return structuredClone(obj);
} else {
// 注意:这是一个功能受限的回退方案
return JSON.parse(JSON.stringify(obj));
}
}
对于更完整的兼容性,也可以考虑使用第三方 polyfill,如 @ungap/structured-clone。
🌐 浏览器支持详情
structuredClone API 在浏览器中也得到了广泛的支持。
所有现代浏览器(包括 Chrome、Firefox、Safari、Edge)都从 2022 年 3 月起,在稳定版本中提供了对该方法的支持。你可以像在 Node.js 中一样,在浏览器的主线程或 Web Worker 中直接使用它。
根据最新的标准文档和浏览器兼容性数据,各主流浏览器的支持情况如下:
| 浏览器 | 支持版本 | 备注 |
|---|---|---|
| Chrome | 98+ | 从 v98 开始支持 |
| Edge | 98+ | 从 v98 开始支持 |
| Firefox | 94+ | 从 v94 开始支持 |
| Safari | 15.4+ | 从 v15.4 开始支持 |
| Opera | 84+ | 基于 Chromium,对应支持 |
| Internet Explorer | 不支持 | 任何版本都不支持 |
这个 API 目前已被广泛使用,你可以在 Can I use 上查看最新的统计数据。
💡 使用方式与限制
在浏览器中使用时,其语法和功能与 Node.js 完全一致:
// 创建一个包含各种类型数据的对象
const original = {
name: '浏览器',
date: new Date(),
map: new Map([['key', 'value']])
};
// 进行深拷贝
const cloned = structuredClone(original);
console.log(cloned.date instanceof Date); // true
主要限制(与 Node.js 环境相同):
- 无法克隆 函数 (
Function) - 无法克隆 DOM 节点
- 不会复制对象的原型链
- 不会复制属性描述符、setter/getter 等元数据
📦 如何处理旧版本浏览器?
如果你的用户群体可能还在使用较老的浏览器版本(如 Safari 15.4 以下),或者需要支持 Internet Explorer,你可以使用 polyfill 来提供降级方案。
-
核心 polyfill 库:推荐使用
core-js提供的稳定 polyfill 。npm install core-js然后在你的代码入口处引入:
import 'core-js/stable/structured-clone'; // 或者 require('core-js/stable/structured-clone'); -
自定义回退函数:你也可以自己编写一个简单的降级逻辑,但需要注意,
JSON.parse(JSON.stringify())这种方式无法处理Date、Map、Set、循环引用等复杂情况。function safeStructuredClone(obj) { if (typeof structuredClone === 'function') { return structuredClone(obj); } else { // 警告:这是一个功能受限的降级方案,仅适用于简单对象 try { return JSON.parse(JSON.stringify(obj)); } catch (e) { console.error('当前环境不支持深拷贝该对象', e); return null; } } }
总的来说,对于绝大多数现代浏览器项目,你可以放心地直接使用 structuredClone。如果你需要支持非常老的浏览器,或者有兼容性方面的顾虑,可以告诉我,我们再一起看看具体的解决方案。