javascript 结构化克隆

Node.js 支持 structuredClone API。这是一个全局可用 的函数,用于执行深拷贝 ,自 Node.js 17.0.0 版本开始提供原生支持。

🚀 如何在 Node.js 中使用

structuredClone 是一个全局函数,你可以在任何地方直接调用,无需引入任何模块。

基本语法:

javascript 复制代码
const clonedObject = structuredClone(originalObject);

它还可以接受一个可选的 options 参数,用于转移 可转移对象(如 ArrayBuffer),而不是克隆它们。

javascript 复制代码
structuredClone(value, { transfer: [transferableObjects] });

📝 使用示例

1. 基础深拷贝 这是最常用的方式,用于创建一个与原对象完全独立的副本。

javascript 复制代码
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 无法处理的类型。

javascript 复制代码
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 选项将数据的所有权从原对象转移到克隆对象,这是一种零拷贝操作,性能更好。

javascript 复制代码
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 中不适用,但在浏览器环境中需要注意)
  • 属性描述符settergetter
  • 原型链(克隆后的对象不再继承自原对象的原型)
  • Symbol
  • WeakMapWeakSet

🔧 兼容旧版本 Node.js

如果你的 Node.js 版本低于 17,直接使用会报错。可以编写一个回退函数来保证代码兼容性:

javascript 复制代码
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 完全一致:

javascript 复制代码
// 创建一个包含各种类型数据的对象
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 来提供降级方案。

  1. 核心 polyfill 库 :推荐使用 core-js 提供的稳定 polyfill 。

    bash 复制代码
    npm install core-js

    然后在你的代码入口处引入:

    javascript 复制代码
    import 'core-js/stable/structured-clone';
    // 或者
    require('core-js/stable/structured-clone');
  2. 自定义回退函数 :你也可以自己编写一个简单的降级逻辑,但需要注意,JSON.parse(JSON.stringify()) 这种方式无法处理 DateMapSet、循环引用等复杂情况。

    javascript 复制代码
    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。如果你需要支持非常老的浏览器,或者有兼容性方面的顾虑,可以告诉我,我们再一起看看具体的解决方案。

相关推荐
龙猫不热2 小时前
从 0 手写 Promise:拆解 Promise 链式调用的实现原理
前端·javascript·面试
wuhen_n4 小时前
TypeScript 强力护航:PropType 与组件事件类型的声明
前端·javascript·vue.js
wuhen_n4 小时前
组件设计原则:如何设计一个高内聚、低耦合的 Vue 组件
前端·javascript·vue.js
小蜜蜂dry18 小时前
nestjs学习 - 控制器、提供者、模块
前端·node.js·nestjs
San3019 小时前
手写 Mini Cursor:基于 Node.js 与 LangChain 的开发实战
langchain·node.js·agent
Lee川19 小时前
深度解构JavaScript:作用域链与闭包的内存全景图
javascript·面试
_Eleven20 小时前
Pinia vs Vuex 深度解析与完整实战指南
前端·javascript·vue.js
技术狂小子20 小时前
# 一个 Binder 通信中的多线程同步问题
javascript·vue.js
进击的尘埃20 小时前
Service Worker + stale-while-revalidate:让页面"假装"秒开的那些事
javascript