为何两个JavaScript对象共用同一数组?原因与解决方法
嘿,这个问题我太熟了!让我一步步给你拆解清楚:
问题原因
你遇到的核心问题是原型链上的引用类型会被所有实例共享,而基本类型看起来正常是因为赋值逻辑不同:
- 首先,
Object.create(OBJ)创建的新对象,会把OBJ作为它的原型对象(也就是内部的[[Prototype]])。当你访问实例的属性时,会先找自己的属性,如果没有才会顺着原型链往上找。 - 对于像
id这种基本类型:当你执行obj1.id = 1时,其实是在obj1自身添加了一个id属性,直接覆盖了原型上的id。后续访问obj1.id会优先取自己的,obj2同理,所以看起来各自独立。 - 但对于
arr这种引用类型(数组是引用类型):当你执行obj1.arr.push(1)时,你并没有给obj1添加自己的arr属性,而是直接修改了原型链上OBJ的arr数组——因为你只是在操作数组的内容,而不是给obj1赋值新的数组。所以obj2访问arr时,还是会去原型上找,自然看到的是被修改后的同一个数组。
正确实现方式
这里给你几种常用的解决办法,按需选择:
方法1:使用构造函数
构造函数会在每次实例化时,给新对象创建独立的属性,包括引用类型:
function MyObj() { this.id = 0; this.arr = [0, 0]; // 每个实例都会得到自己的数组 } let obj1 = new MyObj(); let obj2 = new MyObj(); // 现在操作各自的arr就不会互相影响了 obj1.arr.push(1); console.log(obj1.arr); // [0,0,1] console.log(obj2.arr); // [0,0]
方法2:Object.create后手动添加实例自身的引用类型
如果还是想用Object.create,可以在创建实例后,给每个实例赋值自己的数组:
// 原型上可以保留共享属性,或者去掉引用类型 var OBJ = { id: 0 }; let obj1 = Object.create(OBJ); obj1.arr = [0, 0]; // 给obj1创建自己的arr let obj2 = Object.create(OBJ); obj2.arr = [0, 0]; // 给obj2创建自己的arr
如果原型上需要保留默认的数组值,也可以浅拷贝原型的数组来创建新的:
var OBJ = { id: 0, arr: [0,0] }; let obj1 = Object.create(OBJ); obj1.arr = [...OBJ.arr]; // 浅拷贝原型数组,生成新数组 // 也可以用 obj1.arr = OBJ.arr.slice(); let obj2 = Object.create(OBJ); obj2.arr = [...OBJ.arr];
方法3:ES6类(语法糖,本质和构造函数一致)
如果喜欢更现代的语法,可以用ES6的类:
class MyObj { constructor() { this.id = 0; this.arr = [0, 0]; } } let obj1 = new MyObj(); let obj2 = new MyObj();
简单总结下:要让每个实例拥有独立的引用类型,关键是在实例初始化阶段给每个实例创建属于自己的引用类型实例,而不是依赖原型上的那个共享引用。
内容的提问来源于stack exchange,提问作者Geo
相关产品推荐
相关产品推荐

