为何将数组作为原型赋值与简单对象表现不同?
为什么原型上的数组会被实例共享,而简单属性不会?
你遇到的核心问题是值类型和引用类型在原型链中的行为差异,以及赋值操作对实例属性的影响。先看两段代码的对比:
正常表现的简单属性代码
const Amount = { _amount: 0, set_Amount: function (foo) { this._amount = foo; }, get_Amount: function () { return this._amount; }, }; function func_a() {} function func_b() {} const obj_a = new func_a() const obj_b = new func_b() Object.assign(func_a.prototype, Amount); Object.assign(func_b.prototype, Amount); obj_a.set_Amount(1) obj_b.set_Amount(2) console.log(obj_a.get_Amount()) //1 console.log(obj_b.get_Amount()) //2
出现共享问题的数组代码
const objList = { _list: [], add_item: function (foo) { this._list.push(foo) }, list: function () { return this._list; }, total: function (){ let x = 0; for (let item in this._list) { x = Number(x) + Number(this._list[item]); } return x; } } function type_a() {} function type_b() {} const obj_a = new type_a() const obj_b = new type_b() Object.assign(type_a.prototype, objList); Object.assign(type_b.prototype, objList); obj_a.add_item(1) obj_a.add_item(2) obj_b.add_item(3) obj_b.add_item(4) console.log(obj_a.list()) // [1, 2, 3, 4] console.log(obj_a.total()) // 10 console.log(obj_b.list()) // [1, 2, 3, 4] console.log(obj_b.total()) // 10
原因解释
简单属性的情况:
调用obj_a.set_Amount(1)时,this._amount = foo是直接给obj_a实例自身添加了一个_amount属性。JavaScript属性查找规则是先找实例自身,再找原型,所以后续get_Amount取的是实例自己的属性,和原型上的_amount无关。每个实例都有独立的_amount,自然互不影响。数组(引用类型)的情况:
调用obj_a.add_item(1)时,this._list.push(foo)并没有给obj_a实例添加新的_list属性,而是顺着原型链找到了原型上的数组。数组是引用类型,所有实例指向的都是同一个数组对象的引用,不管哪个实例调用push,都是在修改同一个数组,最终所有实例看到的结果一致。
解决方案
要让每个实例拥有独立的数组,必须在构造函数里给实例初始化自己的_list,而非把数组放在原型上:
function type_a() { // 每个实例创建时生成新的数组 this._list = []; } function type_b() { this._list = []; } // 原型只放方法,复用逻辑 const objListMethods = { add_item: function (foo) { this._list.push(foo) }, list: function () { return this._list; }, total: function (){ let x = 0; for (let item of this._list) { // 用for...of遍历数组更合适 x += Number(item); } return x; } } Object.assign(type_a.prototype, objListMethods); Object.assign(type_b.prototype, objListMethods);
修改后,每个实例都会有自己的_list数组,操作时互不干扰。
补充:原型的设计初衷是复用方法这类无需每个实例单独存储的逻辑,实例的状态属性(尤其是引用类型)应放在实例自身中,避免共享问题。
内容的提问来源于stack exchange,提问作者ctalley5
相关产品推荐
相关产品推荐

