簡體   English   中英

'this'在構造函數和繼承的構造函數中是不一樣的

[英]'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

同樣的事情發生在p2p2.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 === p1p1.dist2 === p1p2.dist1 === p2p2.dist2 === p2 這里發生的是調用Emitter構造函數中的代碼,這是創建的新Person對象。 所以當你在Emitter構造函數中說this.dis1 = thisthis實際上是p1p2

在這個例子中沒有使用原型的行,但它可以做你想要的其他很酷的事情:

  • 在Emitter.prottoype上聲明的方法和屬性將可用於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語言中, 更喜歡組合而不是繼承,並簡單地將發射器設置為人的屬性。

正如已經提到的dis1dis1在原型鏈中被抬起。 如果要為不同的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM