[英]'This' is not the same in constructor and inherited constructor
我有這個簡單的繼承模式:
function Emitter() {
this.dis1 = this;
};
function Person() {
this.dis2 = this;
};
Person.prototype = new Emitter();
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //true - why?
console.log(p1.dis2 === p2.dis2); //false
為什么單獨對象的dis1是相同的? 這有解決方法嗎?
關於new
運算符如何工作的說明:
Foo = function(){};
Foo.prototype;
var bar = new Foo();
在最后一行發生的是創建一個新對象,它繼承自Foo.prototype
,然后執行Foo
函數中的代碼, this
就是新對象。 然后bar被初始化為該新對象。
你的繼承模型有幾個缺陷,你已經發現了其中一個。 所有缺陷都來自這一行代碼:
Person.prototype = new Emitter();
該行表示所有Person實例繼承的對象是Emitter的實例(在那里創建的Emitter實例)。
p1.dist1
查找原型鏈上的dist1屬性。 首先,它在p1
中查看它是否具有dist1
屬性,但它沒有。 接下來它查看p1.[[Prototype]]
,它被初始化為Person.prototype
,它確實找到了dist1
屬性,它的值是Person.prototype
(上面提到的Emitter的一個實例)。
p1.dist
做同樣的事情,但是屬性在p1
找到並且它等於p1
。
同樣的事情發生在p2
, p2.dist1
是Emitter的相同實例,而p2.dist2
等於p2
。
繼承和避免所有這些問題的方法是:
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this); // calls the parent constructor.
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype); // you don't even use this line
var p1 = new Person();
var p2 = new Person();
在這個模型中, p1.dist1 === p1
和p1.dist2 === p1
, p2.dist1 === p2
和p2.dist2 === p2
。 這里發生的是調用Emitter構造函數中的代碼,這是創建的新Person對象。 所以當你在Emitter構造函數中說this.dis1 = this
, this
實際上是p1
或p2
。
在這個例子中沒有使用原型的行,但它可以做你想要的其他很酷的事情:
p1 instanceof Emitter
將是真的。 您可以在MDN上找到此模型。
在原型鏈中查找 dis1
屬性。
因為它不存在於p1或p2中,所以它會在原型中查找,這是使用new Emitter()
創建的對象。 這就是為什么你有相同價值的原因。
JavaScript中的原型模型與Java等其他語言中的繼承模型有很大不同。 小心不要嘗試在JavaScript中模擬Java或C ++設計:這總是導致糟糕的。
現在,假設您希望在Emitter級別擁有一個對象,並且每個Person實例都有不同的對象,那么最簡單的解決方案可能是按需初始化值或使用特定函數(您可以使用約定來命名它initialize
然后覆蓋它)。
按需初始化的示例:
function Emitter() {
}
Emitter.prototype.register = function(thing){
if (!this.listeners) {
this.listeners = [];
}
this.listeners.push(thing);
}
function Person() {
}
Person.prototype = new Emitter();
實際上,這里的最佳解決方案可能是,通常在真正的OOP語言中, 更喜歡組合而不是繼承,並簡單地將發射器設置為人的屬性。
正如已經提到的dis1
, dis1
在原型鏈中被抬起。 如果要為不同的Person
設置不同的dis1
,可以使用以下模式。
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this);
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype);
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //false
console.log(p1.dis2 === p2.dis2); //false
請注意,您必須在Person
構造函數中調用Emitter
,同時將this
作為當前上下文提供。 現在,創建一個新Person將創建一個對象,該對象包含Person
所有(本地)屬性以及平面層次結構中Emitter
所有(本地)屬性:
{
dis1: Person,
dis2: Person
}
您還可以使用Object.create
來實現原型繼承。 這樣做的好處是,您實際上不會調用Emitter
構造函數,因此您不會將像dis1
這樣的“本地”屬性放在原型對象中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.