Angular项目中克隆嵌套对象数组时出现的异常关联问题排查
问题根源:浅拷贝导致的嵌套对象引用共享
嘿,这个问题的核心其实很清晰——你在resetItems里用的是浅拷贝,只复制了顶层对象,嵌套的子对象还是和原items共享引用!
看你的resetItems实现:
public resetItems(): Array<any> { const arr = []; items.forEach((item) => { arr.push(Object.assign({}, item)); }); return arr; }
Object.assign({}, item)只能搞定第一层对象的复制:
- 像
France、Italy这种顶层对象,确实会生成新的实例,所以修改它们的selected/expanded时,原数组里的顶层对象不会被影响(这就是你点France时符合预期的原因)。 - 但每个顶层对象的
children数组并没有被复制,只是把原数组的引用直接拿过来了。也就是说,newItems里的France.children和原items里的France.children是同一个数组,数组里的Lyon、Paris这些子对象也都是同一个引用。
所以当你修改newItems里Lyon的属性时,本质上是直接修改了原items里的那个Lyon对象——毕竟它们是同一个东西。而你调用clearSelection时,也是通过这个引用把原对象的属性改回了初始值。
解决办法:换成深拷贝
要彻底切断newItems和原items的关联,你得对整个嵌套数组做深拷贝,也就是把所有层级的对象都复制一遍。这里给你几种适合你场景的实现方式:
方式1:手写递归深拷贝(最可控)
这种方式能处理所有嵌套层级,还能自定义处理特殊类型(如果需要的话):
private deepCopy(obj: any): any { // 基本类型直接返回 if (obj === null || typeof obj !== 'object') { return obj; } // 处理数组 if (Array.isArray(obj)) { return obj.map(item => this.deepCopy(item)); } // 处理普通对象 const copiedObj = {...obj}; for (const key in copiedObj) { if (copiedObj.hasOwnProperty(key)) { copiedObj[key] = this.deepCopy(copiedObj[key]); } } return copiedObj; } // 更新resetItems方法 public resetItems(): Array<any> { return this.deepCopy(items); }
方式2:JSON序列化/反序列化(最简单)
这种方式代码最少,但要注意它不能处理函数、Symbol、循环引用这些特殊类型(你的场景里没有这些,完全能用):
public resetItems(): Array<any> { return JSON.parse(JSON.stringify(items)); }
方式3:用浏览器原生的结构化克隆
支持更多类型(比如Date、RegExp),同样不支持函数,代码也很简洁:
public resetItems(): Array<any> { return structuredClone(items); }
验证效果
把resetItems换成上面任何一种深拷贝实现后,再操作newItems里的任何层级对象,原items数组都不会被影响了。比如点击展开Lyon时,原数组里的Lyon的expanded会保持初始的false,完全符合你的预期。
内容的提问来源于stack exchange,提问作者derstauner
相关产品推荐
相关产品推荐

