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

JavaScript中如何克隆对象且不修改原对象?展开运算符克隆异常解析

展开运算符克隆对象后原对象同步修改的原因及解决办法

问题原因

展开运算符{...selectedFood}属于浅拷贝:它仅复制对象的第一层属性。对于基本类型(数字、字符串、布尔值等),会直接复制值;但如果属性是引用类型(比如数组、嵌套对象),它复制的是该引用的内存地址,而非整个数据结构。

在你的代码里,selectedFood的foodNutrients是一个包含多个对象的数组。用{...selectedFood}克隆后,editingFood.foodNutrients和原对象的selectedFood.foodNutrients指向同一个数组,数组里的每个对象也共享相同的内存引用。所以当你通过map修改数组中对象的value属性时,原对象的对应数据自然会跟着改变——本质上你修改的是同一个内存地址里的内容。

解决办法:实现深拷贝

要彻底切断克隆对象和原对象的引用关联,需要做深拷贝,也就是逐层复制所有引用类型的属性。针对你的场景,有几种实用方案:

1. 手动逐层拷贝(推荐,结构明确时使用)

手动复制第一层属性,同时对foodNutrients数组里的每个对象也进行浅拷贝:

let editingFood = {
  ...selectedFood,
  // 对数组中的每个营养对象单独拷贝
  foodNutrients: selectedFood.foodNutrients.map(nutrient => ({ ...nutrient }))
};
// 用forEach更合适,map会返回新数组但此处无需保留
editingFood.foodNutrients.forEach(nutrient => nutrient.value = 10);

2. JSON序列化/反序列化(适合普通无特殊类型的对象)

利用JSON.stringify把对象转成字符串,再用JSON.parse转回来,实现深拷贝:

let editingFood = JSON.parse(JSON.stringify(selectedFood));
editingFood.foodNutrients.map(nutrient => nutrient.value = 10);

⚠️ 注意:如果对象里包含函数、undefined、Symbol或者循环引用,这种方法会丢失对应数据或报错。

3. 递归深拷贝函数(适合复杂结构对象)

自己实现一个递归函数,逐层遍历并复制所有属性:

function deepClone(obj) {
  // 基本类型直接返回
  if (obj === null || typeof obj !== 'object') return obj;
  // 数组的话递归复制每个元素
  if (obj instanceof Array) {
    return obj.map(item => deepClone(item));
  }
  // 对象的话递归复制每个属性
  const clonedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key]);
    }
  }
  return clonedObj;
}

let editingFood = deepClone(selectedFood);
editingFood.foodNutrients.map(nutrient => nutrient.value = 10);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:31:01