JavaScript開發者的繁殖速度和它的語言特性同樣迅猛,這是好事,可是也把JS搞得比任何一種其餘語言都更像流行樂,充滿教派和玄學。但編程不是玄學,是科學和工程。這篇文章就用來闡述和探討JavaScript中的一個比較關鍵的概念,雖然在實踐上並不如在理論上那麼意義重大。node
Prototype Inheritance是JavaScript裏的一個標誌性特性。實際上它叫作Inheritance是有一些問題的,JS沒有type系統,instanceof也只是一個沿着原型鏈查找constructor的語法糖,一個對象是誰構造出來的並不說明任何問題,由於它的對象沒有結構上的穩定性承諾,只能靠程序員自覺。程序員
Inheritance是OO近30年的工程實踐裏留下來的重要特性,可是它不是一個好的特性。固然好與很差是相對的,在絕大多數狀況下它都不是太大的問題,尤爲是工程進入尾聲,開發者對問題和模型有充分認識的時候,Class Hierarchy能夠是很合理的設計。npm
那麼,在更General的層面上去問Inheritance設計解決的是什麼問題呢?兩個字,reuse。編程
在Java裏,reuse有兩個語法關鍵字,一個是extends,即inheritance,另外一個是implements,實現interface。函數
那麼爲何把implements也當成reuse呢?由於任何模塊總有兩個方面,使用者和提供者,implements實現了一個interface,因此等價於重用了使用者代碼。this
固然你說extends也達到了一樣的目的呀?並且我還重用了父類的狀態和行爲呢?是的,凡是兩面,有得有失;這正是它倒黴的地方。設計
它倒黴的具體狀況被稱爲fragile base class問題,wiki上有詞條,不贅述。component
由於extends/inheritance是一種長程的關聯,基類的修改對繼承者的影響難以估計和維護,具備ripple effect特性,所以從耦合度的角度說,它大大提升了組件的耦合度,高耦合度不是罪過,但它應對變化的能力變差了。對象
好了,說了這麼多咱們說到了問題的本原。繼承
在Self語言中,也是最先試圖解決這個問題的語言設計者們,給出了Prototypal Inheritance設計,
它的設計初衷有兩個:
抹平Class和Object的差別,讓修改基類變得容易;
若是你修改基類,複製一個基類對象而後修改,新繼承者重新的基類對象開始繼承。其餘繼承者不受影響。
你以爲這個差異很重要嗎?其實在實踐上沒有想象的那麼重要。
JavaScript在設計上還有點不一樣,它的原型對象是共享而非複製的,結果是隻適合把方法裝載到原型上去,偶爾有一些同類對象相同的只讀context也能夠這樣作,其餘每對象私有態還得經過調用父類構造函數作出來,即ES6裏的super關鍵字,若是是ES5,得手動把this bind到父類構造函數上調用。這個特性就語言而言是重要特性,可是和咱們討論的問題沒太大關係。
更重要的問題出在設計上而不是語言層面。
James Gosling在一次研討會上回答問題時,有人問了他這樣一個問題,若是從新設計Java語言,他會有什麼重要的取捨。Gosling的回答可能有點兒令你吃驚:他說若是能夠從新來過,他不會賦予Java語言繼承特性,只用implements。
呃?!
實際上是能夠理解的。
繼承設計看起來在代碼重用上很方便,可是它的fragile base class問題,讓它沒法應對軟件系統的scale問題。這一點不用論證,在整個軟件工業上,繼承這種whitebox reuse不能scale是一個定論,單一程序用繼承書寫代碼不是問題,可是任何有點規模的系統都是靠interface,protocol,或者所謂的component-based工程方法來搭建的,也就是在更高粒度的設計層面只有基於Interface的blackbox reuse。
在任何語言中,都能用blackbox reuse構建複雜系統。在JavaScript中,龐大的npm包系統實現的最終應用,也是blackbox reuse。
JavaScript自己是否functional是洗剪吹們喜歡探討的,嚴肅的工程師不應幹這個事兒;可是整個JavaScript的Community的共識是基於blackbox reuse構建系統,這是好事。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
那麼是否該使用extends呢?
不少時候沒什麼必要性。既然Gosling都說不應去經過inheritance-based hierarchy去組合複雜行爲(犧牲低耦合度),誰還比他更有資格對這個問題發言呢?
一般能夠把幾個邏輯單元糖葫蘆同樣串起來實現一個從外部看來功能特別Powerful的一個對象時,也很容易把每一個獨立單元用decorator,facade之類的pattern串起來,可能會多寫點兒代碼,但不會不少,JavaScript做爲無類型動態語言在書寫pattern時具備顯著的簡潔優點。(在C++/Java裏是相反的,只寫inheritance顯著比寫pattern簡潔;可是問題就是問題你沒法迴避,若是必需要鬆開耦合,還得回到pattern上定義,這也是爲何這些pattern被髮明出來的緣由。)
固然用extends方便的時候也不必去抵觸它,好比node裏的event emitter,stream等等,該用就用唄,只要不去試圖構造framework同樣的hierarchy便可。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
若是有人跟你說JavaScript不是OO語言,請一腳把它踹溝裏去,JS裏除了用Object Literal寫出來的ex nihilo對象以外,(邏輯上)全部對象都是用構造函數構造出來的,這甚至包括全局的Object, Array,Function等等,一切皆對象是JS的最高設計思想。你怎麼能說它不是OO的?