JavaScript繼承

原型基本實現(xiàn)

原型鏈

ECMAScript中描述了原型鏈的概念民效,并將原型鏈作為實現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法误褪。簡單回顧一下構造函數(shù)亭螟、原型和實例的關系:每個構造函數(shù)都有一個原型對象,原型對象都包含一個指向構造函數(shù)的指針榆俺,而實例都包含一個指向原型對象的內部指針售躁。那么,假如我們讓原型對象等于另一個類型的實例茴晋,結果會怎么樣呢陪捷?顯然,此時的原型對象將包含一個指向另一個原型的指針晃跺,相應地揩局,另一個原型中也包含著一個指向另一個構造函數(shù)的指針。假如另一個原型又是另一個類型的實例掀虎,那么上述關系依然成立凌盯,如此層層遞進,就構成了實例與原型的鏈條烹玉。這就是所謂原型鏈的基本概念驰怎。

實現(xiàn)原型鏈的基本模式

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}

//繼承了SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
console.log(instance.getSuperValue());      //true

以上代碼定義了兩個類型:SuperType和SubType。每個類型分別有一個屬性和一個方法二打。它們的主要區(qū)別是SubType繼承了SuperType县忌,而繼承是通過創(chuàng)建SuperType的實例,并將該實例賦給SubType.prototype實現(xiàn)的继效。實現(xiàn)的本質是重寫原型對象症杏,代之以一個新類型的實例。換句話說瑞信,原來存在于SuperType的實例中的所有屬性和方法厉颤,現(xiàn)在也存在于SubType.prototype中了。在確立了繼承關系之后凡简,我們給SubType.prototype添加了一個方法逼友,這樣就在繼承了SuperType的屬性和方法的基礎上又添加了一個新方法精肃。這個例子中的實例以及構造函數(shù)和原型之間的關系如圖


基本原型鏈

在上面的代碼中,我們沒有使用SubType默認提供的原型帜乞,而是給它換了一個新原型司抱;這個新原型就是SuperType的實例。于是黎烈,新原型不僅具有作為一個SuperType的實例所擁有的全部屬性和方法习柠,而且其內部還有一個指針,指向了SuperType的原型照棋。最終結果就是這樣的:instance指向SubType的原型津畸,SubType的原型又指向SuperType的原型。getSuperValue()方法仍然還在SuperType.prototype中必怜,但property則位于SubType.prototype中。這是因為property是一個實例屬性后频,而getSuperValue()則是一個原型方法梳庆。既然SubType.prototype現(xiàn)在是SuperType的實例,那么property當然就位于該實例中了卑惜。此外膏执,要注意instance.constructor現(xiàn)在指向的是SuperType,這是因為原來SubType.prototype中的constructor被重寫了的緣故(下注)露久。

注:實際上更米,不是SubType的原型的constructor屬性被重寫了,而是SubType的原型指向了另一個對象——SuperType的原型毫痕,而這個原型對象的constructor屬性指向的是SuperType征峦。

確定原型和實例的關系

可以通過兩種方式來確定原型和實例之間的關系。第一種方式是使用instanceof操作符:

console.log(instance instanceof SuperType) //true
console.log(instance instanceof SubType) //true

由于原型鏈的關系消请,我們可以說instance是Object(所有函數(shù)的默認原型)栏笆、SuperType或SubType中任何一個類型的實例。因此臊泰,測試這三個構造函數(shù)的結果都返回了true蛉加。

第二種方式是使用isPrototypeOf()方法铁孵。同樣魔市,只要是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型浪秘,因此isPrototypeOf()方法也會返回true需频,如下所示丁眼。

console.log(SuperType.prototype.isPrototypeOf(instance))
console.log(SubType.prototype.isPrototypeOf(instance))

謹慎地定義方法

function A(){

}
A.prototype.name = 'hello'
A.prototype.sayhi = function () {
    return this.name + 'A'
}
function B(){

}
B.prototype = new A()
B.prototype.name = 'hi'
B.prototype.sayhi = function() {
    return this.name + 'B'
}

var c = new B();
console.log(c.name);  // hi
console.log(c.sayhi()); //hiB

以上代碼子類會屏蔽父類的同名的屬性和方法,還有一點需要注意贺辰,即在通過原型鏈實現(xiàn)繼承時户盯,不能使用對象字面量創(chuàng)建原型方法嵌施,因為這樣做就會重寫原型鏈。

原型鏈的問題

原型鏈雖然很強大莽鸭,可以用它來實現(xiàn)繼承吗伤,但它也存在一些問題。其中硫眨,最主要的問題來自包含引用類型值的原型足淆。引用類型值的原型屬性會被所有實例共享;而這也正是為什么要在構造函數(shù)中礁阁,而不是在原型對象中定義屬性的原因巧号。在通過原型來實現(xiàn)繼承時,原型實際上會變成另一個類型的實例姥闭。于是丹鸿,原先的實例屬性也就順理成章地變成了現(xiàn)在的原型屬性了。

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){            
}

//繼承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);        //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors);        //"red,blue,green,black"

這個例子中的SuperType構造函數(shù)定義了一個colors屬性棚品,該屬性包含一個數(shù)組(引用類型值)靠欢。SuperType的每個實例都會有各自包含自己數(shù)組的colors屬性。當SubType通過原型鏈繼承了SuperType之后铜跑,SubType.prototype就變成了SuperType的一個實例门怪,因此它也擁有了一個它自己的colors屬性——就跟專門創(chuàng)建了一個SubType.prototype.colors屬性一樣。但結果是什么呢锅纺?結果是SubType的所有實例都會共享這一個colors屬性掷空。而我們對instance1.colors的修改能夠通過instance2.colors反映出來,就已經(jīng)充分證實了這一點囤锉。

原型鏈的第二個問題是:在創(chuàng)建子類型的實例時坦弟,不能向超類型的構造函數(shù)中傳遞參數(shù)。實際上官地,應該說是沒有辦法在不影響所有對象實例的情況下减拭,給超類型的構造函數(shù)傳遞參數(shù)。有鑒于此区丑,再加上前面剛剛討論過的由于原型中包含引用類型值所帶來的問題拧粪,實踐中很少會單獨使用原型鏈。

借用構造函數(shù)

在解決原型中包含引用類型值所帶來問題的過程中沧侥,開發(fā)人員開始使用一種叫做借用構造函數(shù)(constructor stealing)的技術(有時候也叫做偽造對象或經(jīng)典繼承)可霎。這種技術的基本思想相當簡單,即在子類型構造函數(shù)的內部調用超類型構造函數(shù)宴杀。別忘了癣朗,函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象,因此通過使用apply()和call()方法也可以在(將來)新創(chuàng)建的對象上執(zhí)行構造函數(shù)

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){  
    //繼承了SuperType
    SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors);    //"red,blue,green"
傳遞參數(shù)

相對于原型鏈而言旺罢,借用構造函數(shù)有一個很大的優(yōu)勢旷余,即可以在子類型構造函數(shù)中向超類型構造函數(shù)傳遞參數(shù)绢记。

function SuperType(name){
    this.name = name;
}

function SubType(){  
    //繼承了SuperType,同時還傳遞了參數(shù)
    SuperType.call(this, "Nicholas");

    //實例屬性
    this.age = 29;
}

var instance = new SubType();
alert(instance.name);    //"Nicholas";
alert(instance.age);     //29

以上代碼中的SuperType只接受一個參數(shù)name正卧,該參數(shù)會直接賦給一個屬性蠢熄。在SubType構造函數(shù)內部調用SuperType構造函數(shù)時,實際上是為SubType的實例設置了name屬性炉旷。為了確保SuperType構造函數(shù)不會重寫子類型的屬性签孔,可以在調用超類型構造函數(shù)后,再添加應該在子類型中定義的屬性窘行。

JavaScript高級程序設計(第3版) 讀書筆記

GitHub:JavaScript-Demo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末饥追,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子罐盔,更是在濱河造成了極大的恐慌但绕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惶看,死亡現(xiàn)場離奇詭異壁熄,居然都是意外死亡,警方通過查閱死者的電腦和手機碳竟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狸臣,“玉大人莹桅,你說我怎么就攤上這事≈蛞啵” “怎么了诈泼?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長煤禽。 經(jīng)常有香客問我铐达,道長,這世上最難降的妖魔是什么檬果? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任瓮孙,我火速辦了婚禮,結果婚禮上选脊,老公的妹妹穿的比我還像新娘杭抠。我一直安慰自己,他們只是感情好恳啥,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布偏灿。 她就那樣靜靜地躺著,像睡著了一般钝的。 火紅的嫁衣襯著肌膚如雪翁垂。 梳的紋絲不亂的頭發(fā)上铆遭,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音沿猜,去河邊找鬼枚荣。 笑死,一個胖子當著我的面吹牛邢疙,可吹牛的內容都是我干的棍弄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疟游,長吁一口氣:“原來是場噩夢啊……” “哼呼畸!你這毒婦竟也來了?” 一聲冷哼從身側響起颁虐,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蛮原,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后另绩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儒陨,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年笋籽,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹦漠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡车海,死狀恐怖笛园,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情侍芝,我是刑警寧澤研铆,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站州叠,受9級特大地震影響棵红,放射性物質發(fā)生泄漏。R本人自食惡果不足惜咧栗,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一逆甜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧致板,春花似錦忆绰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春稚茅,著一層夾襖步出監(jiān)牢的瞬間纸淮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工亚享, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咽块,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓欺税,卻偏偏與公主長得像侈沪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子晚凿,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容

  • ??面向對象(Object-Oriented歼秽,OO)的語言有一個標志应役,那就是它們都有類的概念,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,107評論 0 6
  • 原型鏈繼承基本思想就是讓一個原型對象指向另一個類型的實例 function SuperType() {this.p...
    ningluo閱讀 212評論 0 0
  • 繼承是 OO 語言中的一個最為人津津樂道的概念燥筷。許多 OO 語言都支持兩種繼承方式:接口繼承 和 實現(xiàn)繼承箩祥。接口繼...
    threetowns閱讀 445評論 0 0
  • 我是誰,我來自哪,我是誰的誰 想必大家一定在學習或者開發(fā)過程常常被JS獨有的原型繼承撥過不少腦弦吧,為何不迎問題而...
    俗三瘋閱讀 318評論 0 2
  • 寫在前面 推薦大家先看一下這篇文章的姊妹篇JavaScript創(chuàng)建對象的四種常見模式 這個抄書筆記在介紹四個模式時...
    艾倫先生閱讀 506評論 3 2