You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

全局嵌套const经扩展运算符克隆后的变化及防修改方法

解决扩展运算符浅拷贝嵌套对象导致原对象被修改的问题

你观察得非常准确——扩展运算符(...)对嵌套对象只能完成浅拷贝:它会为第一层属性创建新的引用,但嵌套的子对象仍然和原对象共享同一块内存空间,所以修改子属性时会同步影响原对象,就像你代码里展示的那样。

先再明确下你代码里的问题:

const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', };
(() => { const bar1 = { ...foo }; bar1.a.a1 = 'changed'; })();
console.log(foo); // 输出 { a: { a1: 'changed', a2: 'a2' }, b: 'b' }

这里{...foo}只复制了foo的第一层属性ab,其中a是一个对象,所以bar1.afoo.a指向的是同一个对象,修改bar1.a.a1自然会改变原对象的对应值。

下面给你几种实用的解决方案,你可以根据自己的场景选择:

1. 手动逐层拷贝嵌套对象

如果你的对象层级不多、结构简单,手动为每个嵌套对象做浅拷贝是最直观的方式:

const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', };
(() => { 
  const bar1 = { 
    ...foo,
    a: {...foo.a} // 单独拷贝嵌套的a对象
  }; 
  bar1.a.a1 = 'changed'; 
})();
console.log(foo); // 输出 { a: { a1: 'a1', a2: 'a2' }, b: 'b' },原对象不受影响

这种方法的好处是可控性强,不会有额外的性能开销,但如果对象层级很深就会很繁琐。

2. 使用JSON.parse(JSON.stringify())快速深拷贝

这是前端开发中最常用的快速深拷贝技巧,通过把对象序列化为JSON字符串再解析,生成一个完全独立的新对象:

const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', };
(() => { 
  const bar1 = JSON.parse(JSON.stringify(foo));
  bar1.a.a1 = 'changed'; 
})();
console.log(foo); // 原对象保持不变

⚠️ 注意:这种方法有局限性,无法拷贝函数、Symbol、循环引用的对象,日期对象会被转为字符串,正则表达式会变成空对象。如果你的对象里没有这些特殊类型,这个方法非常方便。

3. 使用原生structuredClone()方法

现在主流浏览器和Node.js都支持原生的structuredClone(),它是专门为深拷贝设计的API,能处理大部分类型,包括循环引用、Date、RegExp、Map、Set等,比JSON方法更强大:

const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', };
(() => { 
  const bar1 = structuredClone(foo);
  bar1.a.a1 = 'changed'; 
})();
console.log(foo); // 原对象完全不受影响

这是目前推荐的原生深拷贝方案,无需依赖第三方库,也不用自己写复杂的递归逻辑。

4. 自定义递归深拷贝函数(或使用工具库)

如果需要处理更复杂的场景(比如拷贝函数、自定义类实例),可以自己写一个递归的深拷贝函数,或者直接使用成熟的工具库(比如Lodash的_.cloneDeep())。

这里给一个简单的递归实现示例:

function deepClone(obj) {
  // 非对象类型直接返回
  if (obj === null || typeof obj !== 'object') return obj;
  
  // 处理数组
  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item));
  }
  
  // 处理普通对象
  const clonedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key]);
    }
  }
  return clonedObj;
}

const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', };
(() => { 
  const bar1 = deepClone(foo);
  bar1.a.a1 = 'changed'; 
})();
console.log(foo); // 原对象不受影响

这个基础版本可以处理普通对象和数组,你可以根据需求扩展它对其他类型的支持。


内容的提问来源于stack exchange,提问作者trmaphi

火山引擎 最新活动