沈淺
摘要: 概述JavaScript實現(xiàn)繼承的機制,對比基于類的繼承,通過實例說明原型繼承的特點,提出一個改進的“寄生組合模式”,使讀者實現(xiàn)原型繼承變得游刃有余。
關鍵詞:JavaScript;原型繼承;構造函數(shù);原型對象
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2018)03-0091-02
Abstract: Overview the JavaScripts implementation of the mechanism on the inheritance, compare to Based on the class of inheritance, explain the prototype of inheritances characteristics through examples, propose an improved “parasitic combination mode”, and enable the readers to realize the prototype of inheritance become to do a job with skill and ease.
Key words: JavaScript, prototype of inheritance, constructor, prototype of object
1 概述
JavaScript是面向Web的編程語言,其高端、動態(tài)以及面向對象的編程風格,使得JavaScript已經從一門簡單的腳本語言進化成為一門強大的編程語言,其基于原型的繼承風格,使用起來非常靈活、高效,但對于初學者,要準確理解原型對象及其繼承機制還是比較困難,本文就JavaScript原型繼承的方式進行了介紹,并結合實例進行分析。
2 基于類的繼承
首先,面向對象編程語言是基于類的語言,以Java為例,Java是基于類的語言,在基于類的語言中,對象是類的實例,并且一個類可以從另一個類繼承。那么,在這些基于類的語言中,實現(xiàn)繼承的好處:1)提高代碼重用性高。如果我們新創(chuàng)建的類與已有的類有絕大部分相類似,則沒有必要再重新定義這個完整的類。這樣做可以實現(xiàn)代碼的重用,大大減少了軟件開發(fā)的成本。2)繼承可以實現(xiàn)面向對象的“多態(tài)”特性。程序員可以將子類的對象直接賦值給父類的引用,無需再編寫顯式的類型轉化。既減輕了工作量又保證了類型系統(tǒng)在安全上的優(yōu)勢。如例1所示代碼,這是Java中實現(xiàn)繼承的語法,class B從class A繼承,子類B的對象就繼承了父類A的所有非私有化成員。
3 基于原型的繼承
3.1 原型對象
在JavaScript中,類的所有實例對象都從同一個原型對象上繼承屬性。因此,原型對象是其核心。早期使用一種所謂的“工廠模式”,即通過定義函數(shù)返回一個新創(chuàng)建的對象,后者繼承自某個原型對象。如例2所示代碼,函數(shù)inherit( )返回了一個繼承自原型對象p的新對象。此種方法并不常用,JavaScript還可以通過構造函數(shù)來創(chuàng)建并初始化對象,只不過JavaScript沒有類定義和特殊的構造器定義,需要程序員自己定義一個構造函數(shù)來創(chuàng)建屬性,而所有的函數(shù)都可以被用來定義構造函數(shù)。如例3所示代碼,在JavaScript中,只要創(chuàng)建一個新函數(shù),就會根據(jù)一組特定規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,而這個屬性指向函數(shù)的原型對象。在默認情況下,原型對象會自動獲得一個constructor屬性,而這個屬性包含一個指向prototype屬性所在函數(shù)對象的指針。當一個函數(shù)對象被創(chuàng)建時,function構造器產生的函數(shù)對象會運行代碼:“this.prototype = {constructor: this};”。
如圖1所示,展現(xiàn)了構造函數(shù),原型對象以及實例對象之間的關系。實例對象f指向原型對象p,與構造函數(shù)F沒有直接的關系。在代碼中,通過測試也發(fā)現(xiàn)實例對象雖然沒有屬性sayHi,但我們卻可以調用f.sayHi( ),這說明新對象的這些屬性和方法是所有實例共享的。
3.2 原型繼承的問題及改進
基于原型的繼承,功能強大,一般采用原型鏈繼承方式,但也存在弊端。構造函數(shù)的prototype屬性是一個指針,它可以是特定類型的所有實例共享的屬性和方法。如果我們在對象f中對原型對象p的某個屬性進行修改,則會影響到其他實例。這個顯然不行,我們可以通過構造函數(shù),將不想被共享的屬性定義在構造函數(shù)中,而將需要共享的部分放在原型對象中。這種方式稱為“寄生組合模式”。該模式既避免了原型鏈中實例共享屬性的問題,又實現(xiàn)了構造函數(shù)無法實現(xiàn)的繼承模式,是JavaScript中比較常用的方式。如例4所示代碼,F(xiàn)ather構造函數(shù)模擬“父類”,Son構造函數(shù)模擬“子類”,代碼Son.prototype=new Father( );實現(xiàn)原型繼承,父類對象的所有屬性都可被子類所有對象共享。在創(chuàng)建子類實例對象時,程序會先去Son的構造函數(shù)去找對象的屬性,如果找不到,則去子類繼承的原型對象中去查找,如果還是沒有找到,則去原型對象繼承的父類中去查找,直到找到Object對象,如果還是沒有,就報錯。
4 結束語
其實JavaScript并沒有提供所謂的類繼承,但是我們可以通過一種原型方式實現(xiàn),并且實現(xiàn)方法是靈活多樣的,不存在哪種更優(yōu),根據(jù)不同的需求實現(xiàn)不同方式的繼承。深入理解JavaScript中實現(xiàn)繼承的原理,即原型及原型鏈的問題,只要理解了這些,實現(xiàn)繼承就可以變得游刃有余。
參考文獻:
[1] David Flanagan.JavaScript權威指南[M]. 6版.北京:機械工業(yè)出版社,2012,201-217.
[2] 王貫飛.JavaScrip中基于原型的繼承的實現(xiàn)與分析[J].計算機光盤軟件與應用,2014(3):110-111.