我想使用原型继承新的对象实例。
测试用例:
var MyObj = function() {}
MyObj.prototype.objName = {}
// I want this to be a different object for each instance of MyObj
var o1 = new MyObj (),
o2 = new MyObj ();
o1.objName['a'] = 1;
o2.objName['a'] = 2;
alert(o1.objName['a']) // 2
alert(o1.objName === o2.objName) // true
这意味着原型中的对象不会作为其副本继承,而是作为其引用继承。
我知道通常你可以这样做。
var MyObj = function() {
this.objName = {}
}
var o1 = new MyObj(),
o2 = new MyObj();
alert(o1.objName === o2.objName) // false
这样可以正常工作,但就我而言,这不是一个选择。我真的需要在MyObj函数之外定义objName。
我设法用这个
“解决”了这个问题MyObj.prototype.objName = function() {
if ( this._objName === undefined ) {
this._objName = {};
}
return this._objName;
}
var o1 = new MyObj(),
o2 = new MyObj();
o1.objName()['a'] = 1;
o2.objName()['a'] = 2;
alert(o1.objName()['a']) // 1
但这不是很漂亮,而且这段代码的性能要差得多。
有没有办法更优雅地解决这个问题?
答案 0 :(得分:1)
这意味着原型中的对象不会作为其副本继承,而是作为其引用继承。
原型上没有任何内容被复制 - 原型继承的整个概念是属性引用原型对象的共享属性。因此,如果您希望每个实例都有一个属性,则必须将其显式分配给对象,并将 shadow 分配给prototype属性;就像您在代码中使用_objName
属性一样。
但这不是很漂亮,而且这段代码的性能要差得多。
如果你想要它漂亮,将其移动到构造函数(或使构造函数查找类似于init
方法的内容,如果存在则调用,然后你可以在原型上创建该init方法。
为了提高性能,可以将getter函数更改为
MyObj.prototype.getObj = function() {
var obj = {};
this.getObj = function(){ return obj; }; // overwrite itself
return obj;
};
虽然它仍然具有函数调用开销。为了更加优雅,您可以使用getter属性(旧浏览器不支持)在第一次访问时删除自身:
Object.defineProperty(MyObj.prototype, "objName", {
get: function() {
var obj = {};
Object.defineProperty(this, "objName", {
value: obj,
writable: true //?
});
return obj;
},
enumerable: true,
configurable: true
});
现在你可以省略函数调用括号。
答案 1 :(得分:0)
这意味着原型中的对象不会作为其副本继承,而是作为其引用继承。
要清楚。首先,在JavaScript中,所有对象都通过引用传递,而不是通过值传递。只有原语按值传递。
其次,你实际上并没有“复制”或“传递”任何东西。当您设置prototype
时,您正在创建原型链。这意味着在你的情况下:
var MyObj = function() {};
MyObj.prototype.objName = {} ;
var o1 = new MyObj ();
var o2 = new MyObj ();
o1
和o2
都没有任何名为objName
的属性,您可以使用以下方法对其进行测试:
console.log(Object.keys(o1)); // []
当JS看到像o1.objName
这样的代码时,首先检查对象是否具有此属性,如果有,则使用它。如果没有,开始查看原型链,从o1
的原型开始,即MyObj.prototype
:它找到了属性objName
,并返回它。如果没有找到它,那么JS将继续检查MyObj.prototype
的原型,依此类推。所以,重点是:MyObj.prototype
它是一个对象:您在o1
和o2
之间共享了该对象。这就是为什么objName
的实例是相同的。这与以下内容完全相同:
function objName(obj) {
return "objName" in obj ? obj.objName : O.objName;
}
var O = { objName: [] };
var foo = {};
var bar = {};
objName(foo).push(0);
objName(bar).push(1);
因此,您无法在prototype
中放入任何不希望在使用该原型创建的对象之间共享的对象。我会说这样的共享状态也是一种应该避免的不良做法,这就是为什么一般prototype
不应该有这样的属性。
我仍然不清楚为什么你不能修改构造函数,但重点是:当你创建对象的实例时,你必须“设置”它。通常,调用构造函数,但任何函数都可以。当你想支持继承,并调用“超级”构造函数来初始化你的对象时,也会这样做。