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