好吧,讓我們玩一個小智力遊戲:
從上面的圖片我們可以看到:
- 當我們創建一個像
function Foo() {}
功能,JavaScript的創建Function
實例。
- 每個
Function
實例(構造函數)都有一個屬性prototype
這是一個指針。構造函數的prototype
屬性指向其原型對象。
- 原型對象有一個屬性
constructor
這也是一個指針。
- 原型對象的
constructor
屬性指向它的構造函數。
- 當我們創建
Foo
的新實例(如new Foo()
)時,JavaScript會創建一個新對象。
- 實例的內部
[[proto]]
屬性指向構造函數的原型。
現在,問題出現了,爲什麼JavaScript沒有將constructor
屬性附加到實例對象而不是原型。試想一下:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var Square = defclass({
constructor: function (side) {
this.side = side;
},
area: function() {
return this.side * this.side;
}
});
var square = new Square(10);
alert(square.area()); // 100
正如你可以看到constructor
屬性爲原型的另一種方法,就像上面的例子area
。 constructor
屬性的特殊之處在於它用於初始化原型的實例。否則,它與原型的其他方法完全相同。
定義的原型constructor
財產由於下列原因是有利的:
- 這是邏輯上是正確的。例如考慮
Object.prototype
。 的constructor
屬性指向Object
。如果在實例上定義constructor
屬性,則Object.prototype.constructor
將爲undefined
,因爲Object.prototype
是null
的實例。
- 它與其他原型方法沒有區別。這使得
new
的工作更容易,因爲它不需要在每個實例上定義constructor
屬性。
- 每個實例共享相同的
constructor
屬性。因此它是高效的。
現在,當我們談論繼承,我們有以下情形:
從上面的圖片我們可以看到:
- 派生構造函數的
prototype
屬性設置爲基礎構造函數的實例。
- 因此派生構造函數實例的內部
[[proto]]
屬性也指向它。
- 因此,派生構造函數實例的
constructor
屬性現在指向基礎構造函數。
至於instanceof
算子,與流行的看法相反,它並不依賴於實例的constructor
屬性。從上面我們可以看出,這會導致錯誤的結果。
instanceof
運算符是一個二元運算符(它有兩個操作數)。它在一個實例對象和一個構造函數上運行。作爲上Mozilla Developer Network解釋,它只是執行以下操作:
function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype) { //object is instanceof constructor
return true;
} else if (typeof object == 'xml') { //workaround for XML objects
return constructor.prototype == XML.prototype;
}
object = object.__proto__; //traverse the prototype chain
}
return false; //object is not instanceof constructor
}
爲了把它從Bar
只是如果Foo
繼承,那麼原型鏈的Foo
實例是:
foo.__proto__ === Foo.prototype
foo.__proto__.__proto__ === Bar.prototype
foo.__proto__.__proto__.__proto__ === Object.prototype
foo.__proto__.__proto__.__proto__.__proto__ === null
正如您所看到的,每個對象都繼承自Object
的構造函數。原型鏈在內部[[proto]]
屬性指向null
時結束。
的instanceof
函數簡單地遍歷實例對象的原型鏈(第一操作數),並且每個對象的內部屬性[[proto]]
比較的構造函數的prototype
屬性(第二個操作數)。如果它們匹配,則返回true
;否則如果原型鏈結束,則返回false
。
你需要更新構造函數的任何原因?如果我假裝財產不存在,我發現我的生活更容易。 – hugomg
我很難把它作爲一個副本來關閉 - 所有其他questinos都是如此冗長...... – hugomg
'c.prototype'是'b()'而'b.prototype'是'a()',因此'c.prototype'是'a()' –