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
相关产品推荐
相关产品推荐

