[英]javascript prototypical inheritance confused
給出這樣實現繼承的標准方法
function BaseClass() {
}
function SubClass() {
BaseClass.call(this);
}
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;
為什么需要這樣做
SubClass.prototype = Object.create(BaseClass.prototype);
並最終得到類似的東西
function F(){}
F.prototype = BaseClass.prototype;
SubClass.prototype = new F();
而不只是做
Subclass.prototype = BaseClass.prototype;
在JavaScript中為事物賦值實際上只是復制引用 (除非使用基本類型)。 所以當你這樣做時:
Subclass.prototype = BaseClass.prototype;
你真正在做的是將SubClass
的原型分配到內存中與BaseClass
原型相同的位置,因此你對SubClass
所做的任何與原型相關的更改也會影響BaseClass
。 這是一個小例子:
function BaseClass() {
}
function SubClass() {
BaseClass.call(this);
}
SubClass.prototype = BaseClass.prototype;
SubClass.prototype.constructor = SubClass;
SubClass.prototype.subClassFunction = function(){
console.log("Added this to SubClass");
}
var baseObj = new BaseClass();
baseObj.subClassFunction(); // => "Added this to SubClass"
這就是你想要使用的原因
SubClass.prototype = Object.create(BaseClass.prototype);
因為它將使用指定的原型創建一個新的 唯一對象。
您可以在此處詳細了解此功能的工作原理。
Say BaseClass
有一個toString
方法:
BaseClass.prototype.toString = function() {
return 'foo'
}
但是你在SubClass
重新定義了toString
方法:
SubClass.prototype.toString = function() {
return 'bar'
}
你會期望:
var b = new BaseClass(), s = new SubClass()
b.toString() //=> 'foo'
s.toString() //=> 'bar'
但是如果你使用賦值創建繼承,你會得到的是:
var b = new BaseClass(), s = new SubClass()
b.toString() //=> 'bar'
s.toString() //=> 'bar'
因為BaseClass.prototype
和SubClass.prototype
現在引用相同的對象
JavaScript的語法可能有點令人困惑。 在JS中,沒有類繼承,而是基於實例的繼承。 實例的父級也稱為其原型。
當你編寫instance.something
或instance['something']
,JS引擎會查看實例以查看它是否有一個名為something
的成員。 如果沒有,那么它會查看實例的原型。 如果該對象沒有該成員的something
,它會一次又一次地查看原型的原型,直到它找到該屬性或者到達一個繼承自null
的實例。 在這種情況下,它將簡單地返回undefined
。
函數有一個叫做prototype
的特殊屬性,這是另一回事。 函數的.prototype
是一個簡單的對象,它有一個constructor
屬性,它返回函數本身。 使用new
關鍵字創建對象時,該對象的原型將設置為函數的.prototype
屬性。 這就是混淆的地方:構造函數的.prototype
屬性可以看作是使用所述構造函數創建的所有實例的默認原型。
因此,當您通過編寫以下內容向類添加方法時:
MyClass.prototype.foo = function() {
alert('foo');
};
...實際上,您正在將函數存儲在所有MyClass實例的原型中。 當JS引擎查看MyClass的實例時,它將查找它找不到的foo
成員。 然后它將查看實例的原型,該原型碰巧被設置為MyClass.prototype
,它將找到foo
成員並獲取它。
重要的是在實例的原型和函數的.prototype
之間做出區分,但是大多數人都沒有意識到這一點。 當他們談到一個類的原型時,他們談論的是MyClass.prototype
。 在許多瀏覽器中,可以通過__proto__
訪問實例原型,但這不是JavaScript的標准功能,不應在代碼中使用。
現在讓我們看看你用來模擬類繼承的代碼。
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;
Object.create(parent)
可以看作是執行此操作的函數:
return {
__proto__ : parent
};
換句話說,它創建一個空白Object,其原型是傳遞的對象。 由於所有SubClass實例都將繼承自Subclass.prototype
,因此將SubClass.prototype
替換為繼承自BaseClass.prototype
的對象可確保所有SubClass實例也從BaseClass繼承。
但是,正如我之前所說,函數的默認.prototype
屬性是一個空對象,其.constructor
設置為函數本身。 因此,通過再次手動設置.constructor
,我們完全模仿默認的原型行為。 如果我們不這樣做,那么instance.constructor
將在原型鏈中返回第一個定義的.constructor
屬性,它將是BaseClass
。 除非我們的代碼實際上依賴於constructor
屬性,否則它在行為方面並沒有真正改變任何東西,但它更安全。
最后一點,就像我之前提到的那樣,我最終可以發布這個答案,你不能只做SubClass.prototype = BaseClass.prototype;
因為那樣你就無法在不將它們添加到BaseClass的情況下向SubClass添加方法。
繼承,__ proto__ :
當一個對象SubClass
繼承自另一個對象BaseClass
,在JavaScript中意味着有一個特殊屬性SubClass.__proto__ = BaseClass
。
代碼:
function BaseClass() {
}
function SubClass() {
}
var BaseClass = new BaseClass();
var SubClass = new SubClass();
BaseClass.a = 5;
SubClass.b = 10;
SubClass.__proto__ = BaseClass;
console.log(SubClass);
輸出:
這里, BaseClass
由SubClass
繼承, BaseClass variable is accessable through the SubClass
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.