复制ArrayList后修改原列表为何同步影响副本?技术咨询
options数组的对象时,optionsListDuplicate也会跟着变? 嗨,这个问题其实是JavaScript里引用类型的经典陷阱,我来给你理清楚背后的逻辑~
问题本质:引用类型的浅拷贝
在JavaScript中,对象(包括你创建的{ label:..., value:... }这种字面量对象)属于引用类型。当你把一个对象添加到数组里时,数组存储的并不是这个对象本身,而是这个对象在内存中的地址引用。
如果你的实际代码中是先创建一个对象,再把它同时推入两个数组(比如下面这种写法):
// 错误示例:同一个对象被推入两个数组 for(let i=0;i<data.length;i++){ const item = { label:data[i].name, value:data[i].id }; this.options.push(item); this.optionsListDuplicate.push(item); // 这里推的是同一个对象的引用 }
那options和optionsListDuplicate的对应位置存储的都是同一个对象的内存引用。此时不管你从哪个数组里修改这个对象的属性(比如this.options[0].label = "新名称"),实际修改的是内存中那个唯一的对象,另一个数组自然会同步看到变化。
怎么解决?创建独立的对象副本
要让两个数组里的对象互不影响,你需要确保每个数组里的对象都是独立的副本,而不是同一个引用。针对你这种单层结构的对象(只有label和value两个字符串属性),有几种简单的方法:
- 使用对象展开运算符:
for(let i=0;i<data.length;i++){ const originalItem = { label:data[i].name, value:data[i].id }; this.options.push({...originalItem }); // 创建新对象副本 this.optionsListDuplicate.push({...originalItem }); // 另一个新副本 }
- 使用
Object.assign:
for(let i=0;i<data.length;i++){ const originalItem = { label:data[i].name, value:data[i].id }; this.options.push(Object.assign({}, originalItem)); this.optionsListDuplicate.push(Object.assign({}, originalItem)); }
- 直接在push时创建新对象:
其实你最开始的写法就是这种思路——每次push时都用对象字面量创建全新的对象,只要确保没有复用之前创建的对象,就不会出现引用共享的问题。
如果你的对象是多层嵌套的(比如label本身又是一个对象),那上面的浅拷贝方法就不够了,需要用深拷贝,比如递归实现深拷贝,或者使用JSON.parse(JSON.stringify(originalItem))(注意这种方法无法拷贝函数、Symbol等特殊类型)。
额外提醒:数组本身的复制
如果你是直接把数组赋值给另一个变量(比如this.optionsListDuplicate = this.options),那两个变量会指向同一个数组的引用,不管你修改数组的元素还是数组本身(比如push、pop、splice),另一个变量都会同步变化。这种情况要复制数组,应该用:
- 展开运算符:
this.optionsListDuplicate = [...this.options] slice()方法:this.optionsListDuplicate = this.options.slice()Array.from():this.optionsListDuplicate = Array.from(this.options)
但同样,这些都是浅拷贝,数组里的对象还是引用类型,需要结合上面的对象拷贝方法一起用。
内容的提问来源于stack exchange,提问作者user3660375

