盡管印刷機已經問世很久了,但出版圖書仍然是一件頗具挑戰性的工作。通常,得有一位或幾位作者勤勉地寫作,還要有一位內容編輯協助作者,將他們的想法轉化成不至於太枯燥且可讀性更好的作品。如果這本書與技術或商業有關,則需要更加謹慎——要邀請有技術背景的審校者和密切關注相關主題的專家,找出稿件中關於技術定義或解釋的嚴重錯誤。當然,文字編輯也不能少,他們是確保文字以及語法正確的最後一道防線。然而,到目前為止,我們僅僅談了最淺層的東西:上面提及的所有角色主要處理書的內容,而一本書的制作過程還包括其他許多方面。比如,要有排版人員負責版式設計,保證圖書有好的印刷效果,盡量避免代碼出現孤行和折行的情況;要有人負責設計封面;還必須有人審核初稿的目錄,然後作者纔能與出版社簽訂合同。此外,還要有人監督印刷之前的整個過程,其通常被稱為“生產流程”。一旦這些書印刷出來,還需要把它們發行出去。最後,圖書上架開始售賣。有人購買它並開始閱讀。圖書產品的購買和交易過程甚至可以寫成一本書。
整個過程復雜得令人難以置信。不過,對參與其中的每個人來說,事情卻不是很復雜。比如作者,僅僅是每天寫幾百字而已。那麼,復雜性去哪兒了呢?對整個過程進行這樣的劃分是有原因的:我們人類不擅長處理高復雜性的事項,對於出版圖書或其他商業項目來說,將其分解為單個職責(比如“寫作”“潤色文字”“審校技術問題”“改正語法錯誤”“排版”,或“圖書發行”),能簡化復雜項目參與者的工作,讓流程變得更簡單。
出版圖書僅僅是一個例子——其實對任何事情都可以這麼處理。從你桌上選一樣物品,隨便選什麼都行,思考它是怎麼來到你桌上的。接著,把目光放遠,想一下:它是怎麼生產出來的?由什麼制成?有多少人參與了它的制作、組裝、打磨,並將它送到商店?你選的這個東西是水果嗎?有多少人參與果樹的種植、滅蟲、修剪枝葉、打包果實,再把它送到商店?
我們身邊充斥著各種復雜性,軟件也不例外。從微觀的層面來看,存在物理常量所框定的局限性,比如光速、比特、硬件、中斷調用、彙編語言等;而從宏觀層面來看,你可能會看到比如一個巨型技術部門,處理從查詢到支付的整個過程。我們開發者和所負責的項目就身處在這些復雜性中。
平時,我們不太可能會停下來思考日常事物和交互中的復雜性,因為這樣做會使人寸步難行。相反,我們把解決方法隱藏在抽像的接口之後,以至於我們的大腦就認為它們是接口。其中一些接口很好地映射到被抽像出來的實現中,讓人覺得好用。而另一些則不然,它們讓人覺得疑惑和沮喪。軟件也是這樣。我們不用考慮整個繫統,實際上我們打交道的那些東西都被封裝在接口裡,而這些接口比其背後的具體實現更易理解和使用。
目標讀者
這本書面向專業開發者、業餘愛好者、掌握一定 JavaScript 和 ES6 知識的人。
這些開發人員以及對易讀、易維護、可擴展性強的模塊化編程(甚至不限於 JavaScript 語言)感興趣的人,都可以從本書中受益。
為什麼要使用模塊化 JavaScript
剛開始我是抱著試著玩的心態接觸 Node.js 的,但不知不覺喜歡上了 JavaScript。與此同時,我發現了開源並且愛上了這種實踐。與 C# 封閉的環境對比,Node.js 的開源生態讓我在怎樣寫出更便於他人使用的健壯代碼方面找到了新的視角和樂趣。在這個背景下,我發現自己喜歡思考怎樣定義接口,誰會使用接口,還有如何讓用戶有更多時間做其他事而不是花時間弄明白我們想讓他做什麼。
本書旨在提供一種友好的方法來幫助人們編寫出好的模塊化代碼。並不是說 JavaScript 模塊特別難寫,而是說我們要遵循合理的設計實踐來恰當地分配簡單性和靈活性,以便用戶在多數情況下可以依賴於簡單且足夠靈活的模塊,同時保證內部復雜性是可控的——這並不是一件簡單的事情。我
曾經在 JavaScript Application Design 一書和 Pony Foo 博客裡零散地寫過一些最佳應用設計的內容。但我一直渴望出版一本全面討論如何分析、設計、編寫模塊化代碼的書籍。
盡管找不到哪本書是從 JavaScript 的角度專門闡述這個主題的,不過我能很容易地找到涉及模塊化代碼主題的書籍,例如 Steve McConnell 所著《代碼大全》(Microsoft Press)或者 Robert C. Martin 所著的《代碼整潔之道》
(Prentice Hall),並在 JavaScript 開發工作中使用這些書中所教授的知識。本書試圖讓你把注意力從別人認為你應該做什麼上移開,讓你能夠自己總結應該做什麼以及為什麼做,而不是強迫你接受一些虛偽的定義“整潔代碼” 的所謂法則。
本書不會直接講怎麼編寫模塊化代碼,而是試圖闡明模塊化體繫結構背後的基本原理和 JavaScript 模塊化的歷史,以便於你更好地理解模塊化編程的意義和益處。
市面上有很多關於應用設計的書籍,但關於模塊化應用設計並沒有太多的參考資料,更不用說模塊化 JavaScript 應用設計了。所以,這本書應運而生。盡管本書中大部分的建議、思想和教導都不是專門針對 JavaScript 的,但是本書在闡述時聚焦於 JavaScript,這意味著你將學習如何編寫模塊化 Web 應用程序,而在此過程中也會記住那些使 Web 成為獨特的平臺以及讓JavaScript 在許多方面都很特別的奇異功能。
本書沒有采用長篇大論深入分析具體例子,而是希望你能夠嘗試應用書中的方法來解決自己項目中的問題,並且通過權衡各種方法的優缺點,最終實現目標。對於軟件來說,不存在一刀切的通用解決辦法,經常需要你自己做出判斷,決定怎麼寫代碼。所有的軟件需要與其場景相適應,如果你做過任何軟件開發或發行的工作,那麼你肯定很清楚把同一個軟件硬塞進不同的執行環境有多難。
正如 Practical Modern JavaScript 一樣,這本書目標在於一點點地建立一個基線。在通過 Practical Modern JavaScript 一書學習最新的語言特性之後,我們借助這本書來學習模塊化設計思想。這種增量的、模塊化的方法在兩本書中無處不在。
本書的結構
第 1 章討論 JavaScript 語境下模塊化編程的演變,從早期直接在 onclick 屬性中嵌入的 JavaScript,到 CommonJS,最後到原生 ECMAScript 模塊。然後,介紹編寫自包含代碼的好處,以及在繫統的每個級別(服務、應用程序、組件、模塊、函數、塊等)都這麼做的好處。
第 2 章涵蓋了模塊化設計的要點,為你打下一個基礎,讓你能在這個基礎上編寫對 API 層面友好的模塊,並且知道這個模塊會被如何(在所有可能的情況下)使用、其職責是什麼,以及哪些部分屬於接口。
第 3 章的大部分內容都是在幫助你理解要解決的各種問題,以及如何在解決那些問題的同時密切關注模塊及其接口的演變,並且在等待清晰的模式出現前盡量不要進行抽像。本章刺破表像,讓你將自己的上下文應用到待解決的問題上,啟發你對文檔、錯誤處理,以及遵循自己的推理等主題的最佳實踐的思考。
第 4 章討論內部復雜性、緊耦合,以及如何權衡框架和約束(convention)的優點。這一章的大部分篇幅都在討論通過重構代碼來降低復雜性的各種方法,然後討論與復雜性相關的狀態的作用,以及如何降低復雜性。數據結構也非常重要,因為在控制復雜性時,選擇正確的數據結構雖然具有挑戰性,卻可以帶來巨大的回報。
第 5 章專門討論 JavaScript,詳細介紹了如何利用現代語言結構來編寫簡潔的程序。這一章還研究了繼承和組合等模式,進而引出如何根據實際用例來做正確的選擇的討論。最後,我們也梳理了經典模式,例如解釋什麼是模式、對像工廠、事件觸發和 JSON 消息傳遞。
第 6 章描述了身經百戰的模塊開發人員是如何思考的,涉及安全問題和依賴關繫管理、構建和集成過程、接口和抽像,以及對模塊設計的建議和最佳實踐。
就算你已經很了解 JavaScript 模塊化歷史,至少也應該瀏覽一下第 1 章中對歷史的回顧。如果你是那種喜歡跳著看書的人,我建議你從頭到尾閱讀完所有章節,因為這本薄書更像是一本故事書,講述了合理的程序應該是什麼樣子的而非給出一堆具體的操作步驟。
致謝
本書最終得以完成要感謝很多人的幫助。首先是 Virginia Wilson,她是本書的主要內容編輯和 O’Reilly 的 Modular JavaScript 繫列負責人。她能洞察那些最重要的事情,並且在我的寫作時間緊張、寫作速度放緩時給予充分的理解,總是保持非常積極的態度。
技術審校者們也非常棒。Mathias Bynens 主要負責檢查我關於 ECMAScript 規範的描述是否標準。Ingvar Stepanyan 似乎總是準備抓住機會對我的書進行技術評審,他總是提供獨到的見解,讓我對內容的描述更清晰、所舉的例子更全面。我非常感激他的工作。Adam Rackis 為該繫列圖書的技術審校提供了很大幫助,他總能對需要修正的地方提出有理有據的意見,使其改後變得更充實或更清晰。
如果沒能提到 2016 年在 Indiegogo 眾籌平臺上支持 Modular JavaScript 繫列圖書的所有人,那將是我的疏忽。在這些書隻是一個想法時,謝謝你們對我的信任,從一開始就激勵我,讓我有了極大的熱情。如果我們有機會見面,一起喝啤酒吧,我來買單。以下所列姓名無特定先後順序:
Aaron Endsley, Aaron Hans, Aaron Olson, Aaron Wells, Adam Rackis, Adi
Purnama Mutiara, Adrian Li, Adrian Rand, Agustin Nicolas Polo, Alan
Chandler, Alasdair Shepherd, Alejandro Nanez, Alexis Mills,