- JavaScript高效圖形編程
- (美)Raffaele Cecco
- 1258字
- 2021-04-09 20:45:10
第1章 代碼重用和優化
JavaScript受到了許多不公平的評價。許多人說JavaScript在面向對象編程上存在局限,甚至有人認為JavaScript不能歸為面向對象編程(OOP)語言。盡管JavaScript和C++、Java有許多相似之處,但它沒有等價Class的聲明,也沒有顯而易見的方式去實現流行的OOP技術,如繼承(代碼復用)和封裝。JavaScript的類型非常松散,也沒有編譯器,因此在運行出錯前只能提供很少的錯誤或警告。JavaScript是把雙刃劍,一方面給了程序員很大的自由,另一方面也給程序員帶來一些陷阱。
JavaScript中充滿了對傳統編程“過失”的忽略,傳統的程序員可能對此頗為郁悶。比如,在JavaScript中全局函數和變量是默認行為,而忘記加分號是完全可接受的。對JavaScript的工作方式缺乏了解,往往導致程序員無比郁悶。如果首先了解一些基礎事實,將有助于你編寫JavaScript應用:
- JavaScript不是一個基于類的語言;
- 寫好代碼,并不一定需要基于類的面向對象編程語言。
有些編程人員嘗試用JavaScript 寫C++風格的代碼。盡管在某種程度上可以達到目標,但最終結果往往讓人感覺不自然。
沒有任何編程語言是完美的,人們有理由爭論某個編程語言或OOP本身的優越性是否僅僅是皇帝的新衣。根據我的個人經驗,用C++、Java或PHP編寫的軟件生成的bug和問題,并不比用JavaScript編寫的軟件生成的少。我認為JavaScript的靈活性和表達力,可以使你更快地進行項目開發。
幸運的是,大部分JavaScript的缺點都不是無藥可醫。解決之道并不是一味模仿其他語言,而是揚長避短:利用Javascript的靈活性,而小心避開難處理的部分。基于類的其他語言容易引起笨拙的類層次和臃腫的代碼,JavaScript則提供了同樣有效但更輕量級的繼承模式。
JavaScript可以有許多種方法來實現繼承。下面的代碼使用原型繼承來創建一個Pet對象,和一個繼承它的Cat對象。JavaScript教程中常常能見到這種“經典”的繼承模式。

上述代碼可以工作,但不是特別優雅。如果你熟悉其他OOP語言比如C++或Java,new聲明是好理解的。但關鍵字prototype顯得很啰嗦,并且沒有隱私;注意外部代碼將petCat的legs屬性改成了一個不合理的值:7。這種繼承方法沒有提供對外部繼承的保護,在涉及多個程序員的復雜項目中這個缺點也許會影響很大。
另一個選項無需使用prototype或new,而是利用JavaScript的“函數繼承(functional inheritance)”特性來吸收和增強對象實例(object instances):

這里沒有可笑的prototype,而且所有東西都封裝得很漂亮。最重要的是:legs變量是私有的。如果嘗試從cat外部修改不存在的公共legs屬性,僅導致創建一個沒有用過的legs屬性。真正的legs值安全地保存在pet的getDetails()方法創建的閉包(Closure)內部。閉包在函數執行結束后,保持了函數的局部變量。在這個例子中這個函數指的是pet()。
事實上,用JavaScript實現繼承并沒有所謂“正確”的方法。但我個人認為函數繼承方式非常自然。你和你的應用也許傾向其他方法。通過搜索“JavaScript Inheritance”你可以找到許多在線資源。
提示
使用原型繼承的好處之一是內存效率;不管它被繼承多少次,對象的原型屬性和方法只被保存一次。
函數繼承則相反:每個新的實例都會創建重復的屬性和方法。如果你要創建許多(如上千個)大對象的實例,內存消耗可能會成為一個問題。不過這個問題很容易解決:可以將較大的屬性或方法保存在一個對象中,并將其作為參數傳給構建函數。這樣所有實例就可以共同使用一個對象資源,而不是創建自己的版本。