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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:02:51