簡體   English   中英

javascript原型繼承混淆

[英]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.prototypeSubClass.prototype現在引用相同的對象

JavaScript的語法可能有點令人困惑。 在JS中,沒有類繼承,而是基於實例的繼承。 實例的父級也稱為其原型。

當你編寫instance.somethinginstance['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繼承。

示例MyClass.prototype控制台檢查

但是,正如我之前所說,函數的默認.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);

輸出:

這里, BaseClassSubClass繼承, BaseClass variable is accessable through the SubClass

在此輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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