数组复制修改异常求助:修改新数组时原数组同步变更
问题分析与解决方案
嘿,这个问题我太熟悉了——你遇到的是JavaScript里引用类型浅拷贝的经典坑!下面一步步拆解你的代码问题,再给你修复方案:
问题根源:浅拷贝导致对象引用共享
你用let items = [...productLines];来复制数组,这确实会创建一个新的数组,但注意:数组里的每个对象只是原数组对象的引用,并不是真正的独立副本。也就是说,items里的对象和productLines里的对象指向的是内存里的同一个地址,所以你后续修改这些对象的属性时,原数组里的对应对象也会被同步修改。
你的代码里具体的问题点:
- 修改
group对象会影响原数组:let group = items.find(...)找到的group是原数组对象的引用,所以执行group.group_id = getNewGroupId()时,原productLines里的那个对象的group_id也会被改掉。 - 修改
items里的子对象会影响原数组:items.filter(...)得到的数组里的对象还是原引用,forEach里修改line.parent_group_id时,原数组里对应的那些对象的parent_group_id也会被修改。
修复方案:对对象进行深拷贝
要解决这个问题,你需要深拷贝数组里的每个对象,彻底断开和原对象的引用关联。下面提供几种原生实现方式:
方法1:使用structuredClone(推荐,现代浏览器支持)
structuredClone是浏览器原生的深拷贝API,能处理大部分数据类型(包括日期、正则、Map/Set等),用法简单:
function cloneProductLine(oldGroupIndex) { // 深拷贝数组中的每个对象,创建完全独立的副本 let items = productLines.map(line => structuredClone(line)); let group = items.find((line, index) => index === parseFloat(oldGroupIndex)); // 筛选需要克隆的子项(现在都是独立副本) const clonedItems = items.filter((line) => line.parent_group_id === group.group_id); clonedItems.forEach((line) => line.parent_group_id = getNewGroupId()); // 修改拷贝后的group的id,不会影响原数组 group.group_id = getNewGroupId(); // 将新的group和子项添加到原数组 productLines = [...productLines, group, ...clonedItems]; renderProductLine(); }
方法2:使用JSON.parse(JSON.stringify(...))(兼容性好,但有局限性)
这个方法兼容性极佳,但不能处理函数、Symbol、循环引用、日期(会被转为字符串)等特殊类型:
function cloneProductLine(oldGroupIndex) { let items = productLines.map(line => JSON.parse(JSON.stringify(line))); let group = items.find((line, index) => index === parseFloat(oldGroupIndex)); const clonedItems = items.filter((line) => line.parent_group_id === group.group_id); clonedItems.forEach((line) => line.parent_group_id = getNewGroupId()); group.group_id = getNewGroupId(); productLines = [...productLines, group, ...clonedItems]; renderProductLine(); }
方法3:手动深拷贝(自定义逻辑,适合复杂对象)
如果你的对象有特殊类型(比如函数),可以自己写一个深拷贝函数,比如:
function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); 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 items = productLines.map(line => deepClone(line));
总结
核心就是:数组的浅拷贝(扩展运算符、Array.from等)只会复制数组结构,不会复制里面的引用类型对象。要修改副本不影响原数组,必须对每个对象进行深拷贝,断开引用关联。
内容的提问来源于stack exchange,提问作者Ferdi Louwerse
相关产品推荐
相关产品推荐

