Javascript 繼承機(jī)制的實(shí)現(xiàn)
選定基類(lèi)后,就可以創(chuàng)建它的子類(lèi)了。是否使用基類(lèi)完全由你決定。有時(shí),你可能想創(chuàng)建一個(gè)不能直接使用的基類(lèi),它只是用于給子類(lèi)提供通用的函數(shù)。在這種情況下,基類(lèi)被看作抽象類(lèi)。
盡管ECMAScript并沒(méi)有像其他語(yǔ)言那樣嚴(yán)格地定義抽象類(lèi),但有時(shí)它的確會(huì)創(chuàng)建一些不允許使用的類(lèi)。通常,我們稱(chēng)這種類(lèi)為抽象類(lèi)。
創(chuàng)建的子類(lèi)將繼承超類(lèi)的所有屬性和方法,包括構(gòu)造函數(shù)及方法的實(shí)現(xiàn)。記住,所有屬性和方法都是公用的,因此子類(lèi)可直接訪問(wèn)這些方法。子類(lèi)還可添加超類(lèi)中沒(méi)有的新屬性和方法,也可以覆蓋超類(lèi)中的屬性和方法。
4.2.1 繼承的方式
和其他功能一樣,ECMAScript中實(shí)現(xiàn)繼承的方式不止一種。這是因?yàn)镴avaScript中的繼承機(jī)制并不是明確規(guī)定的,而是通過(guò)模仿實(shí)現(xiàn)的。這意味著所有的繼承細(xì)節(jié)并非由解釋程序處理。作為開(kāi)發(fā)者,你有權(quán)決定最適用的繼承方式。
1. 對(duì)象冒充
構(gòu)想原始的ECMAScript時(shí),根本沒(méi)打算設(shè)計(jì)對(duì)象冒充(object masquerading)。它是在開(kāi)發(fā)者開(kāi)始理解函數(shù)的工作方式,尤其是如何在函數(shù)環(huán)境中使用this關(guān)鍵字后才發(fā)展出來(lái)的。
其原理如下:構(gòu)造函數(shù)使用this關(guān)鍵字給所有屬性和方法賦值(即采用類(lèi)聲明的構(gòu)造函數(shù)方式)。因?yàn)闃?gòu)造函數(shù)只是一個(gè)函數(shù),所以可使ClassA的構(gòu)造函數(shù)成為ClassB的方法,然后調(diào)用它。ClassB就會(huì)收到ClassA的構(gòu)造函數(shù)中定義的屬性和方法。例如,用下面的方式定義ClassA和ClassB:

還記得嗎?關(guān)鍵字this引用的是構(gòu)造函數(shù)當(dāng)前創(chuàng)建的對(duì)象。不過(guò)在這個(gè)方法中,this指向的是所屬的對(duì)象。這個(gè)原理是把ClassA作為常規(guī)函數(shù)來(lái)建立繼承機(jī)制,而不是作為構(gòu)造函數(shù)。如下使用構(gòu)造函數(shù)ClassB可以實(shí)現(xiàn)繼承機(jī)制:

在這段代碼中,為ClassA賦予了方法newMethod(記住,函數(shù)名只是指向它的指針)。然后調(diào)用該方法,傳遞給它的是ClassB構(gòu)造函數(shù)的參數(shù)sColor。最后一行代碼刪除了對(duì)ClassA的引用,這樣以后就不能再調(diào)用它。
所有的新屬性和新方法都必須在刪除了新方法的代碼行后定義。否則,可能會(huì)覆蓋超類(lèi)的相關(guān)屬性和方法:

為證明前面的代碼有效,可以運(yùn)行下面的例子:

有趣的是,對(duì)象冒充可以支持多重繼承。也就是說(shuō),一個(gè)類(lèi)可以繼承多個(gè)超類(lèi)。用UML表示的多繼承機(jī)制如圖4-2所示。

圖 4-2
例如,如果存在兩個(gè)類(lèi)ClassX和ClassY,ClassZ想繼承這兩個(gè)類(lèi),可以使用下面的代碼:
![]()

![]()
這里存在一個(gè)弊端,如果ClassX和ClassY具有同名的屬性或方法,ClassY具有高優(yōu)先級(jí),因?yàn)槔^承的是最后的類(lèi)。除這點(diǎn)小問(wèn)題之外,用對(duì)象冒充實(shí)現(xiàn)多繼承機(jī)制輕而易舉。
由于這種繼承方法的流行,ECMAScript的第三版為Function對(duì)象加入了兩個(gè)新方法,即call()和apply()。
2. call()方法
call()方法是與經(jīng)典的對(duì)象冒充方法最相似的方法。它的第一個(gè)參數(shù)用作this的對(duì)象。其他參數(shù)都直接傳遞給函數(shù)自身。例如:

![]()
在這個(gè)例子中,函數(shù)sayColor()在對(duì)象外定義,即使它不屬于任何對(duì)象,也可以引用關(guān)鍵字this。對(duì)象obj的color屬性等于"red"。調(diào)用call()方法時(shí),第一個(gè)參數(shù)是obj,說(shuō)明應(yīng)該賦予sayColor()函數(shù)中的this關(guān)鍵字值是obj。第二個(gè)和第三個(gè)參數(shù)是字符串。它們與sayColor()函數(shù)中的參數(shù)prefix和suffix匹配,最后生成的消息"The color is red, a very nice color indeed"將被顯示出來(lái)。
要與繼承機(jī)制的對(duì)象冒充方法一起使用該方法,只需將前三行的賦值、調(diào)用和刪除代碼替換即可:

這里,想讓ClassA中的關(guān)鍵字this等于新創(chuàng)建的ClassB對(duì)象,因此this是第一個(gè)參數(shù)。第二個(gè)參數(shù)sColor對(duì)兩個(gè)類(lèi)來(lái)說(shuō)都是唯一的參數(shù)。
3. apply()方法
apply()方法有兩個(gè)參數(shù),用作this的對(duì)象和要傳遞給函數(shù)的參數(shù)的數(shù)組。例如:

這個(gè)例子與前面的例子相同,只是現(xiàn)在調(diào)用的是apply()方法。調(diào)用apply()方法時(shí),第一個(gè)參數(shù)仍是obj,說(shuō)明應(yīng)該賦予sayColor()中的this關(guān)鍵字值是obj。第二個(gè)參數(shù)是由兩個(gè)字符串構(gòu)成的數(shù)組,與sayColor()的參數(shù)prefix和suffix匹配。生成的消息仍是"The color is red, a very nice color indeed",將被顯示出來(lái)。
該方法也用于替換前三行的賦值、調(diào)用和刪除新方法的代碼:
![]()

同樣的,第一個(gè)參數(shù)仍是this。第二個(gè)參數(shù)是只有一個(gè)值color的數(shù)組??梢园袰lassB的整個(gè)arguments對(duì)象作為第二個(gè)參數(shù)傳遞給apply()方法:

當(dāng)然,只有超類(lèi)中的參數(shù)順序與子類(lèi)中的參數(shù)順序完全一致時(shí)才可以傳遞參數(shù)對(duì)象。如果不是,就必須創(chuàng)建一個(gè)單獨(dú)的數(shù)組,按照正確的順序放置參數(shù)。此外,還可使用call()方法。
4. 原型鏈
繼承這種形式在ECMAScript中原本是用于原型鏈的。上一章介紹了定義類(lèi)的原型方式。原型鏈擴(kuò)展了這種方式,以一種有趣的方式實(shí)現(xiàn)繼承機(jī)制。
在上一章中學(xué)過(guò),prototype對(duì)象是個(gè)模板,要實(shí)例化的對(duì)象都以這個(gè)模板為基礎(chǔ)??偠灾?,prototype對(duì)象的任何屬性和方法都被傳遞給那個(gè)類(lèi)的所有實(shí)例。原型鏈利用這種功能來(lái)實(shí)現(xiàn)繼承機(jī)制。
如果用原型方式重定義前面例子中的類(lèi),它們將變?yōu)橄铝行问剑?/P>

原型鏈的神奇之處在于突出顯示的代碼行。這里,把ClassB的prototype屬性設(shè)置成ClassA的實(shí)例。這很有意義,因?yàn)橄胍狢lassA的所有屬性和方法,但又不想逐個(gè)將它們賦予ClassB的prototype屬性。還有比把ClassA的實(shí)例賦予prototype屬性更好的方法嗎?
注意,調(diào)用ClassA的構(gòu)造函數(shù)時(shí),沒(méi)有給它傳遞參數(shù)。這在原型鏈中是標(biāo)準(zhǔn)做法。要確保構(gòu)造函數(shù)沒(méi)有任何參數(shù)。
與對(duì)象冒充相似,子類(lèi)的所有屬性和方法都必須出現(xiàn)在prototype屬性被賦值后,因?yàn)樵谒百x值的所有方法都會(huì)被刪除。為什么?因?yàn)閜rototype屬性被替換成了新對(duì)象,添加了新方法的原始對(duì)象將被銷(xiāo)毀。所以,為ClassB類(lèi)添加name屬性和sayName()方法的代碼如下:
![]()
![]()
![]()
可通過(guò)運(yùn)行下面的例子測(cè)試這段代碼:

此外,在原型鏈中,instanceof運(yùn)算符的運(yùn)行方式也很獨(dú)特。對(duì)ClassB的所有實(shí)例,instanceof為ClassA和ClassB都返回true。例如:
![]()
在ECMAScript的弱類(lèi)型世界中,這是極其有用的工具,不過(guò)使用對(duì)象冒充時(shí)不能使用它。
原型鏈的弊端是不支持多重繼承。記住,原型鏈會(huì)用另一類(lèi)型的對(duì)象重寫(xiě)類(lèi)的prototype屬性。
5. 混合方式
這種繼承方式使用構(gòu)造函數(shù)定義類(lèi),并未使用任何原型。對(duì)象冒充的主要問(wèn)題是必須使用構(gòu)造函數(shù)方式(如上一章中學(xué)到的),這不是最好的選擇。不過(guò)如果使用原型鏈,就無(wú)法使用帶參構(gòu)造函數(shù)了。開(kāi)發(fā)者該如何選擇呢?答案很簡(jiǎn)單,兩者都用。
在前一章,你學(xué)過(guò)創(chuàng)建類(lèi)的最好方式是用構(gòu)造函數(shù)方式定義屬性,用原型方式定義方法。這種方式同樣適用于繼承機(jī)制,用對(duì)象冒充繼承構(gòu)造函數(shù)的屬性,用原型鏈繼承prototype對(duì)象的方法。用這兩種方式重寫(xiě)前面的例子,代碼如下:
![]()


在此例子中,繼承機(jī)制由兩行突出顯示的代碼實(shí)現(xiàn)。在第一行突出顯示的代碼中,在ClassB構(gòu)造函數(shù)中,用對(duì)象冒充繼承ClassA類(lèi)的sColor屬性。在第二行突出顯示的代碼中,用原型鏈繼承ClassA類(lèi)的方法。由于這種混合方式使用了原型鏈,所以instanceof運(yùn)算符仍能正確運(yùn)行。
下面的例子測(cè)試了這段代碼:

相關(guān)文章
js實(shí)現(xiàn)圖片拖動(dòng)改變順序附圖
需要改變多個(gè)元素的位置,可以通過(guò)元素拖動(dòng)來(lái)實(shí)現(xiàn),下面以圖片拖動(dòng)為例,用jQuery來(lái)實(shí)現(xiàn),需要的朋友可以參考下2014-05-05
js統(tǒng)計(jì)錄入文本框中字符的個(gè)數(shù)并加以限制不超過(guò)多少
為了更直觀的體現(xiàn)用戶(hù)在文本框輸入文本時(shí)能看到自己輸入了多少字,并且有些特殊的要求字?jǐn)?shù)不超過(guò)多少,本文給出了具體的實(shí)現(xiàn)2014-05-05
js實(shí)現(xiàn)仿百度汽車(chē)頻道選擇汽車(chē)圖片展示實(shí)例
這篇文章主要介紹了js實(shí)現(xiàn)仿百度汽車(chē)頻道選擇汽車(chē)圖片展示,實(shí)例分析了javascript鼠標(biāo)事件操作css樣式的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-05-05
詳解JavaScript添加給定的標(biāo)簽選項(xiàng)
這篇文章給大家分享了JavaScript實(shí)現(xiàn)添加給定的標(biāo)簽選項(xiàng)的相關(guān)知識(shí)點(diǎn)內(nèi)容以及代碼實(shí)例,有興趣的朋友們參考下。2018-09-09
30個(gè)高逼格代碼的JavaScript高級(jí)單行代碼
這篇文章中列出了一個(gè)系列的 30 個(gè) JavaScript 單行代碼,它們?cè)谑褂?nbsp;vanilla js(≥ ES6)進(jìn)行開(kāi)發(fā)時(shí)非常有用,它們也是使用該語(yǔ)言在最新版本中為我們提供的所有功能來(lái)解決問(wèn)題的優(yōu)雅方式,將它們分為以下5大類(lèi):日期、字符串、數(shù)字、數(shù)組、工具2023-08-08
Javascript&DHTML基礎(chǔ)知識(shí)
首先請(qǐng)下載JScript.chm這本手冊(cè),無(wú)論新手老手,有一本手冊(cè)是免不了的,特別是對(duì)于新手,如果你沒(méi)有空翻犀牛書(shū),那么這本手冊(cè)將是你了解這門(mén)語(yǔ)言的首選。下面所講的大多數(shù),手冊(cè)上可以沒(méi)有提及,或提及很少的內(nèi)容。2008-07-07
js發(fā)送短信倒計(jì)時(shí)的簡(jiǎn)單實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇js發(fā)送短信倒計(jì)時(shí)的簡(jiǎn)單實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
JS+canvas動(dòng)態(tài)繪制餅圖的方法示例
這篇文章主要介紹了JS+canvas動(dòng)態(tài)繪制餅圖的方法,結(jié)合具體實(shí)例形式分析了js+canvas實(shí)現(xiàn)餅狀圖形繪制的相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
JavaScript canvas實(shí)現(xiàn)環(huán)形漸變進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了JavaScript canvas實(shí)現(xiàn)環(huán)形漸變進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06

