You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用Object.assign()与Spread properties仍会修改原始对象?

为什么Object.assign和扩展运算符没解决你的引用问题?

嘿,我太懂你这种困惑了!一开始以为用了ES6的这些方法就能彻底断开对象引用,结果发现还是“牵一发而动全身”,其实核心问题出在浅拷贝 vs 深拷贝上——你忽略了这俩方法只能做浅拷贝,嵌套的对象/数组依然会共享引用。

让我一步步给你拆解:

1. 先搞懂直接赋值的问题

当你写bar = foo[0]时,因为对象是引用类型,你并没有复制foo[0]这个对象本身,只是把bar指向了foo[0]在内存中的同一个地址。所以不管你修改bar还是foo[0],本质上都是在操作同一个对象,自然会互相影响。

2. 为什么Object.assign和扩展运算符没用?

Object.assign({}, foo[0])或者const bar = { ...foo[0] }确实能创建一个新的外层对象,但它们只拷贝了对象的第一层属性。如果foo[0]里有嵌套的对象或数组,那这些内层的元素依然是引用传递的。

举个例子:

const foo = [{
  name: "John",
  details: { age: 30, city: "London" }
}];

// 用扩展运算符做浅拷贝
const bar = { ...foo[0] };

// 修改bar的嵌套属性
bar.details.age = 31;

// 你会发现foo[0]的details也变了!
console.log(foo[0].details.age); // 输出31

看到没?外层的name是拷贝了新值,但内层的details还是和原对象共享同一个引用,所以修改它还是会影响原数组里的元素。

3. 怎么彻底解决?用深拷贝

要完全断开引用,你需要做深拷贝——也就是把对象的每一层都复制一遍,而不只是第一层。这里有几种常用方法:

方法一:JSON序列化/反序列化(简单但有局限)

这是最快捷的方式,适合没有特殊类型的对象:

const bar = JSON.parse(JSON.stringify(foo[0]));

⚠️ 注意:这种方法不能处理函数、Symbol、循环引用,或者Date对象(会转成字符串),如果你的对象里有这些类型,就不能用这个方法。

方法二:手动写递归深拷贝函数

如果需要处理复杂类型,可以自己实现一个深拷贝函数:

function deepClone(obj) {
  // 处理基本类型和null
  if (obj === null || typeof obj !== "object") return obj;
  
  // 处理数组
  if (obj instanceof Array) {
    return obj.map(item => deepClone(item));
  }
  
  // 处理对象
  const clonedObj = {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key]);
    }
  }
  return clonedObj;
}

// 使用方式
const bar = deepClone(foo[0]);

这个函数会递归遍历对象的每一层,把所有嵌套的对象/数组都复制一遍,彻底断开引用。

方法三:用第三方库(可选)

如果你不想自己写函数,可以用成熟的库,比如Lodash的_.cloneDeep方法,它能处理各种复杂的对象类型。

总结一下

你之前忽略的关键点就是:Object.assign和扩展运算符只是浅拷贝,只能处理第一层属性。如果你的数组元素是嵌套对象/数组,必须用深拷贝才能完全切断和原对象的引用关系。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:35:30