JavaScript:世紀機器語言?
原文</i> http://mp.weixin.qq.com/s?__biz=MzA3NDM0ODQwMw==&mid=206520119&idx=1&sn=2631fea1aedada919e56383584f98070 </span>
在我寫了「顛覆者的游戲 - 程序語言」和「elixir - 靈丹妙藥?or 徒有其名?」后,就有同學就讓我講講javascript。對于這門讓人又愛又恨的語言,真要講,我都不知道怎么開頭了。套用『北京人在紐約』里的臺詞: 「如果你愛TA,請讓TA用Javascript,做為一個程序員,TA此生無憂;如果你恨TA,請讓TA用Javascript,做為一個程序員,TA 基本毀了」。
無所不在的javascript
說Javascript程序員此生無憂有很多理由,最大的理由就是:web無處不在,有web存在的地方,就有javascript存在的地方,所以 javascript程序員是皇上的女兒不愁嫁。一個好的javascript程序員,他的痛苦是:這么多工作機會,到底哪個是Sandberg說的 rocket ship? [1]
javascript在web frontend的統治地位是毋庸置疑的。不像backend那么多千奇百怪的選擇,在frontend,二十年來大浪淘沙,有且只有一門能在任何瀏覽器中運行的語言:javascript。而自 prototype
和 script.aculo.us
開啟了小團隊的web2.0之旅后,javascript的生態圈呈爆炸性的繁榮。
web backend不必多說,nodejs已經火熱了幾年,早年的流行詞LAMP [2] 都換成了MEAN [3] 這樣的full stack js framework。這讓很多前端工程師努努力就變成了全棧,一下子轉扶搖而上,沖到了鄙視鏈的上層。
在mobile frontend上,cordova,ionic等html5 mobile app framework在不斷努力讓生活在WebView下的基于javascript的hybrid mobile app能夠達到接近native app的表現。作為移動領域的頭牌,apple對javascript也是又愛又恨 —— 愛其繁榮的生態圈,快速產品化的能力 [4] ,恨其一次編寫到處運行的優勢,無奈這股浪潮已經勢不可當,于是在iOS 8里,apple終于在面對WebView也提供了早就在safari中提供的Nitro JIT engine。我倒不是說hybrid mobile app完全會取代native app,目前還不具備這個可能,但很多應用場景,hybrid app已經能夠很好勝任。
桌面系統的領地,被node-webkit,atom-shell等一大票基于CEF [5] 的chrome衍生品也攻克了。CEF的理念很簡單:每個app就是一個退化的chrome(可以簡單認為把沙箱的限制取消了),跑開發者定制的 html/css/javascript。由于chrome是跨平臺的,所以這樣的軟件也跨平臺,而且界面和web一樣,可以無比豐富(也可無比貼近本地 應用)。
硬件開發?有的是創業公司,比如tessel,看中了javascript在程序員群體的可達性,嘗試提供基于javascript的SDK,來直接操縱硬件。也許有一天,FPGA的邏輯可以用javascript來描述。
設計拙劣的javascript
作為一門語言,javascript是一俊遮百丑的典范。隨便試幾例: [6] :
如果你的另一半如此捉摸不定,是不是很想抽丫一大耳光,轉身離去?可惜這也就止于意淫 —— 誰讓人家顏值太高,小性子壞脾氣再多,你也得忍著,對么?
javascript是可能現代編程語言中設計周期最短的,據說Brendan Eich只有10天時間來設計它,基本思路是他自己的背景(函數式編程)和網景/Sun的背景(Java和面向對象編程)的大雜燴 [7] :
(1) C的基本語法
(2) Java的數據類型和內存管理
(3) Scheme的函數能力(函數是一等公民)
(4) Self的 prototype 的繼承機制
和其它深思熟慮設計出來的語言(比如說clojure [8] ),javascript就是一個災難。而更大的災難是:原本只是在瀏覽器里跑一跑,對DOM進行簡單操作,響應DOM事件,讓靜止的網頁能夠和用戶有更 多交互性的一門表達能力并不很強的,從來沒想過單個應用會超過上千行代碼(所以javascript連最基本的module都沒提供)的語言,慢慢地成了 世間萬物(web)的主宰!
javascript程序員的撕扯人生
這真是件撕扯的事情。一方面語言有不少弱點,另一方面你又需要用這種語言去完成越來越多的事情。
比如說讓人又愛又恨的 this
。有好幾種不同的 scope
或者 context
會使用 this
,在層層嵌套的函數里,多少人用起來戰戰兢兢?
還有基于prototype的繼承,有多少人真正研究一下prototype的概念,以及這么處理繼承和其他主流面向對象語言的異同?優劣?
我們知道在學習一門新的語言時,大家往往會進行比較嚴格的語言訓練,否則想把代碼寫正確并且寫漂亮很難。但javascript不同,似乎沒有人 特別認真對待它,很多前端工程師或者后端工程師幾乎就是翻一兩個教程,因項目需要,順帶著把自己的語言能力擴展到javascript,根本沒有系統學 習,所以 —— 不得不直面各種各樣的慘淡人生。
要getting things right,你需要了解其good part,避免其缺陷或者容易跑偏的部分。
(這圖寓意深刻啊~)
救星:compile to javascript
擺脫這種困境最直接的方法是靜下心來好好學習javascript,取其精華,去其糟粕。遺憾的是,這并不如想象的那樣簡單。有人看到了其中的機 會:既然javascript不可替換,那何不發明(利用)一門語言,將其編譯成javascript呢?就像C語言被編譯成機器指令,或者java被編 譯成byte code?
(準機器代碼)
(web世界的準機器代碼)
于是,compile to javascript的語言便如雨后春筍般漲了出來。它們大致可以分三類:
1) 新語言,取javascript的子集(good parts)。如asm.js [9] ,coffeescript。coffeescript用更好更簡潔的語法撰寫代碼,編譯出來的javascript比較優美。使用起來毫無壓力,大愛 →
, ?
和其函數式編程的風格。
2) 新語言,在javascript語言基礎上擴展。如typescript。javascript的超集。沒用過。
3) 已有語言的子集,能編譯出javascript的子集。如coffeescript,gopher2js。
點擊「閱讀原文」可以看到完整的compile to javascript的清單。
編譯這事,并不簡單,嚴復說譯事三難:「信,達,雅」。這三點對應于編譯就是:
1) 準確無誤。不能歪曲了程序員的意思。
2) 不拘泥于原文,盡可能優化。在這里就是minify的能力要到位。
3) 編譯結果越小越好。在這里就是dead code elimination。
你看一個標準的gcc編譯器,這三點在不同的 -O
選項下,都能做到 [10] 。而在compile to javascript領域里,做到(好) 2)與3)的很少。minify應該是編譯時的動作,很多語言(比如coffeescript)都忽略它,而是使用各種現成的uglifier在編譯后進行。少了AST [11] 層面的支持,很多minify的事情都做不了。
dead code elimination更是如此,一個jQuery的Lib你可能就用到了dom operation,那各種animation的代碼就是dead code。90%以上的語言在這個領域毫無作為。
這里要專門贊一下clojurescript,這三者它都做得很好,而且很美。clojurescript在設計之初,就把google closure compiler作為其編譯基礎。google closure compiler在優化javascript時的瘋狂,堪比gcc的 -O3
。它在做到「信」的基礎上,最大程度地壓縮你的代碼,不僅刪除所有不在執行路徑上的代碼,還把你的函數各種inline。當然,這是有代價的:你需要滿足 特定的約束,寫能讓closure compiler優化的代碼 —— 這幾乎不是正常人干的事情。所以clojurescript就替你完成這件事:你用clojure(的子集)寫代碼,它幫你生成滿足closure script約束的javascript。
在小型項目上,compile to javascript的優勢并不明顯,但項目越大,系統越復雜,這種優勢就越明顯。atom是coffeescript寫的,lighttable是 clojurescript寫的。我相信,隨著這些compile to javascript的項目本身的逐漸成熟,以及其生態圈的逐漸完善,越來越多的復雜系統,會不再使用手寫的javascript,轉而使用表達能力更強 的語言。
如果您覺得這篇文章不錯,請點贊。多謝!
歡迎訂閱公眾號『程序人生』(搜索微信號 programmer_life)。每篇文章都力求原汁原味,北京時間中午12點左右,美西時間下午8點左右與您相會。
1. If you’re offered a seat on a rocket ship, don’t ask what seat. Just get on.
2. linux, apache, mysql, php
3. mongodb, express, angular, nodejs
4. 意味著單位時間能產生更多的app
5. Chromium Embedded Framework
6. 更多可以參見阮一峰的javascript 10個設計缺陷:http://www.ruanyifeng.com/blog/2011/06/10_design_defects_in_javascript.html
7. 見阮一峰的Javascript誕生記:http://www.ruanyifeng.com/blog/2011/06/birth_of_javascript.html
8. 簡直和javascript是兩個極端
9. 這個有空單獨撰文描述
10. 函數inline,指令優化,刪除無調用函數等
11. Abstract Syntax Tree