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

JS使用展开运算符拷贝对象后splice仍修改原父对象问题

问题原因

{ ...obj } 展开语法仅做浅拷贝:只复制最外层对象的属性,内层嵌套的子对象(Red/Blue/Custom)、子数组(List)都和原始categories对象共享内存引用。
你的代码中通过let myList = obj[key].List拿到数组后,直接调用了splice()方法——splice会原地修改调用它的数组,这个数组本质就是原始categories中对应key下的List,所以原始对象会被同步改动。
哪怕你后续通过展开语法重新给obj赋值,在这之前splice已经完成了对原数组的修改,原始对象的改动已经发生。

修复方案

最直接的解决方式是对原始对象做深拷贝,彻底切断副本和原对象的引用关联,同时避免使用会原地修改数组的方法,所有操作基于新生成的副本执行。
修复后的完整可运行代码:

let categories = {
  Red: {
    Name: "Fire Flame",
    List: [
      ["1","5"],
      ["10","15"]
    ]
  },
  Blue: {
    Name: "Super Light",
    List: [
      ["1","5"],
      ["9","12"]
    ]
  },
  Custom: {
    List: [
      "Star Flash"
    ]
  }
}

const submitData = () => {
  // 深拷贝生成完全独立的副本,现代运行环境原生支持structuredClone
  let obj = structuredClone(categories);
  Object.keys(obj).forEach(key => flattenAndUnique(key));

  function flattenAndUnique(key) {
    const current = obj[key];
    if (key === "Custom") {
      current.List = current.List.flat();
      return;
    }
    const numberList = [];
    current.List.forEach(([start, end]) => {
      const min = Number(start);
      const max = Number(end);
      for (let i = min; i <= max; i++) {
        numberList.push(String(i));
      }
    });
    // 用Set快速去重
    current.List = [...new Set(numberList)];
  }

  console.log("处理后的副本:", obj);
  console.log("原始对象(未改动):", categories);
};

submitData();
改动说明
  • 用原生structuredClone做深拷贝替代浅拷贝,副本和原对象完全独立,修改副本不会对原对象产生任何影响。如果需要兼容IE等老旧环境,可以替换为lodash的_.cloneDeep方法,或自行实现递归深拷贝。
  • 移除了原地修改数组的splice调用,所有数组操作都生成新值后再赋值,从根源上避免引用污染。
  • 简化了冗余的循环赋值逻辑,用Array.flat()做数组拍平、用Set做去重,代码更简洁,同时去掉了无意义的async标记(原逻辑无任何异步操作,该标记不产生实际作用)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:48:21