再回味 David Linden 的原著“The Accidental Mind”
要想弄懂 CMAScript 标准中那些晦涩难懂的内容,就必须学一大堆东西——不光要学 JavaScript 的未来,还要(为了完全理解编辑的动机及其所受限制)学它的历史。我就是这样过来的,随着我将 JavaScript 的知识碎片慢慢地拼缀到一起,事情也就渐渐变得明朗了,这是一种有着很多“包袱”的语言——有着各种不宜见光的秘密。
CMAScript 2015 候选发布版 1
占领浏览器
本文不是为 S6 的卖点做宣传——有很多相当好的资源可以了解那些即将面世的酷炫功能和扩展——不管怎么说,我认为我们对于 JavaScript 已经基本上统治了浏览器市场这个事实的认识应该是一致的。当今的浏览器中并没有太多的客户端语言,这是有原因的:VBScript 已经从 I 11 中卸掉了,虽然谷歌仍试图将 Dart 硬塞进 web 中(嗯,大多是指 Chrome)——但并不很成功。
突然,一个狂野的 JavaScript 出现了随着集纳越来越多开发者的智慧分享,JavaScript 变得越来越普遍,开始出现在一些陌生的、出乎意料的地方:起初,这些地方只是 web 的天涯海角,纵使 JavaScript 广为流行,也仍然没有被触及到。随后不久,JavaScript 就无处不在了。本质上,IT 领域有两条(左右)广为人知的定律,为 JavaScript 统治世界做出了贡献,甚至预言了这一点。
摩尔定律的解释
摩尔定律使移动 web 变得可行——同时,JavaScript 突进到了服务端、设备及移动操作系统领域,最终将渗入极微小的微控制器中。
Netscape 最初的客户与服务端 JavaScript 架构
提供你的脚本,并吃掉它但并没有坚持下去。
DOM 支配 JavaScript 世界I 的 dHTML(摘自《Developing XML Solutions》一书)
DOM 中的基本模式——James Padolsey 提供图片
最终,除了 DOM 之外,又出现了几个其他的功能及扩展,有些进入了标准化程序,被接受为跨浏览器的解决方案,有些仍然局限于单一的浏览器厂商或环境。直至今天,仍然在持续不断地努力工作(其中一个合作成果是 Service Worker 规范)。作为扩展 web 的手段,以使其跟上技术发展的速度,这种推陈出新的努力是受欢迎的。
进入node.js哇,到目前为止,关于 node,我是鼓吹得太多了(但我无意推销给任何人),但愿我能公正地回答下面这个问题:
为什么 node.js 如此重要?它正好在正确的时间出现在正确的地点。浏览器大战其间,JavaScript 的速度提升
浏览器大战是激烈的,JavaScript 的执行速度已经 10 倍于我们此前见过的任何东西——而且由于激烈的竞争,其执行速度仍然在稳步地提升……然而,node.js 的创立者 Ryan Dahl 甚至都没有试过将 JavaScript 塞入服务器环境中。
相反,应当承认,他只是在研究事件驱动的服务器技术时,碰巧撞上了强大的 V8 引擎——JavaScript 正好适合于事件驱动的、非阻塞I/O的、基于单线程循环的环境。
异步即便异步、非阻塞/事件驱动的 I/O 这样的想法根本不算新鲜,事件驱动的 web 服务器却也并不多。事件驱动的服务器要溯源到 1999 年,新生的 Flash(不,不是那个 flash)服务器就使用的这种技术——但其他使用这种模式的服务端解决方案很快就消失了。事实上,(如果我错了,请纠正我),比起我知道的唯一一个相关的解决方案——Tornado web 服务器(用 Python 写成),node.js 在时间上至少领先半年。
Alexandre 在评论里指出——Nginx 服务器也是事件驱动的(这实际上在 Ryan 最初的演示中就提到了,用来与 Apache的线程系统作对比),不过,我不认为这是一个公正的比较,因为 Nginx 事件驱动的功能太弱,也不太灵活。
也承几位读者指出,Twisted Framework(也是用 Python 写成)是一个事件驱动的异步网络框架,确实早于 node 的发布。Twisted 是不是适合上面的讨论,这一点可以争论,但无疑很有说服力——谢谢参与!
(对于上面提到的 Tornado web 服务器,Twisted 也具有丰富的互操作功能,但我认为 Twisted 更适合与 node 实现互操作,不过这只是个人看法。)
2015年初 IO.js 的到来进一步扩大了 node 社区,并为社区注入了新的生命力
分支作者选择的开放管理模式开始发挥魔力,贡献者们蜂拥而至:短短几个月,活跃的贡献者数量就超过了早期的 node.js,而且本地化团队将社区推进到了新的疆界。
“好,好——node.js 给服务端编程注入了新的生命,它是我们的救世主,穿着闪亮盔甲的无畏勇士,是那啥和那啥——明白了,我的老天呐,烦死了……”——好吧,但它远不止这些!
扩展命令链易于使用,可扩展,可用于所有相关平台,Grunt 和 Gulp 不仅围绕 JavaScript 和 web 来扩展工具,而是任何能想到的平台。(你知道连 Photoshop 都有自己内置的、可通过脚本访问的 node.js 实例吗?)
Jaxer 没能做成的(带有 DOM 的嵌入式浏览器),对 node 来说相当自然——首先借助 PhantomJS,其后不久,利用 JSDOM 中加入的 JavaScript 原生一类对象支持,在 node.js 中即可访问 DOM——而在一个类似的程序包中,重新实现了对 web 内容的自动化客户端测试(请查看 Domenic Denicola 关于 JSDOM 及其动机的精彩演讲!)你猜怎么着,继征服浏览器、服务器以及命令行之后,甚至还不满足——node 瞄准了鱼缸中的那条更大的鱼:桌面应用。
Atom ditor 界面(全部以纯粹的 HTML5 写成),可以与任何当代原生代码编辑器相媲美
Node-webkit 激发出了一整套全新的桌面项目。在 Cloud9 ID 成功整合 node.js 和 web 技术从而成为最先进的集成开发环境之后,像 Brackets 和 ATOM 这样的项目都将 web 体验带入了桌面(同时,两个世界各自最棒的部分——跨平台互操作性和扩展性——保持不变)。
万物皆用 JavaScript
万物皆用 JavaScript有人可能会说,“物联网”的观念目前十分流行。智能手表,智能照明,智能取暖,智能房屋,联网的冰箱、洗衣机以及电热水壶——应有尽有。
这些都是小型的、低功耗的设备,小型的芯片加上有限的内存,再加上低功耗——像 JavaScript 这样的资源独占、耗电量大的语言,可能驱动不了这些设备,是这样么?
嗯,再想一想!万物皆用 JavaScript 的时代可能比你想象的来得要快。
火狐救援几年以前,人们认为低功耗、成本敏感的设备根本不可能使用 web 技术——但 Firefox OS 证明了这不仅可行,而且最近两年里在大约 30 个国家发布了 15 种不同的基于 Firefox OS 的设备。
所有这些设备都是成熟的联网、触屏的智能手机——售价低到只有 33 美金,而且其他类型的装置(如松下的 FirefoxOS 驱动的电视)也证明了,JavaScript 和 web 技术仍然远未触顶。
实际上离所说的界限还远着呢,这不,三星决定将其用于它自己的运行 Tizen 操作系统的智能手表平台上——而同时,还与 cma 国际的 TC39 工作组进行接触,看能不能标准化一种用于移动的更加节俭的语言子集,以便用在更小尺寸的设备上。
我们目前还不知道三星的提议有什么结果——嵌入式 JavaScript 本身就是一个非常活跃的话题。三星最初的 TC39 宣传材料上引用了 Technical Machine 公司的 Tessel 以及 spruino,这是两种使用 JS 的微控制器平台。
下面我们来看看如何为这样资源高度受限的硬件创建一个 JavaScript 的解决方案。在进入细节之前,我想先介绍一下第二个定律,与大家分享:
“任何能用 JavaScript 写的应用,最后都将会用 JavaScript 来写。”——由 Jeff Atwood 发明的 Atwood 定律
我就不用过度解释这个定律了(其实定律本身已经自我解释得很好了),但我会在下面展示其应用。
小世界的脚本spruino 的下一代,Pico——计划在 2015 年 5 月份发
第一代 Tessel(取自 Technical Machine 的 Instagram)
第一代 Tessel由于以上所述的这些原因,Technical Machine 公司开发 Tessel 的高手们选择了一条不同的路径。Tessel 有丰裕的内存(32 MB)由 JavaScript 解释器支配——然而驱动整个机器的 Cortex M3 微控制器才有大约 200KB 的内存,致使在上面运行任何高级 JavaScript 解释器都非常困难。
Firefox OS 怪异的双胞胎兄弟:Jan OSJan OS 是一头怪兽,由疯狂的荷兰人,阿姆斯特丹的科学怪人 Jan Jongboom 所发明——它既非一个手机平台(不再是),也非微控制器平台(还不是)——而是两者皆有那么一点。
所有这些传感器和电路要装在一张信用卡大小的 IC 板上!
其背后原理,主要是运行了一个精简的、为移动优化过的 Linux 内核(Firefox OS 的内核,Gonk,是基于 AOSP 内核的),带 Mozilla 的 Gecko 引擎(包括内置的 SpiderMonkey JavaScript 引擎——全优化的,具备所有移动或桌面浏览器上所有花哨功能的——哈喽呜~~~,Jaxer!),但要有额外的 WebAPI,才能访问移动芯片组、wifi、FM 广播(!)、发送短信、打电话或捕捉陀螺仪数据!
Tessel 2——裸机原型
两个 Tessel 连接到一个 Tessel 2 的 USB 端口上
“分形”式的概念图
超越于时代:未来的 JavaScript 机器人
ASM.js 并不是新技术。——它甚至都称不上是一种技术,只是一串迭代优化,碰巧最后很容易被优化,而且运行确实比较快——如果你愿意,也可以说它更像一种制导的演进(guided evolution)。某些优化(像 AST)已经在前面提过了,这些优化有利于 JavaScript 的性能提升。AST 用于提升解释器的速度,从而使得运行时的执行更快——但没有人认为这是执行 JavaScript 的唯一方式。
这里的关键词是:编译。
自从早期的解释语言以来,即时(JIT)编译已广为人知,且广泛应用——JavaScript 也不例外(自然,对于 JavaScript 这样的动态类型语言来说,JIT有着一些怪异的行为和陷阱)。
Firefox 中 SpiderMonkey JIT 结构
优化热点代码(像长循环或频繁调用的函数),将其直接编译为机器指令会使执行性能大幅提升。ASM.js 试图要做的就是,通过定义一种中间形式的 JavaScript 子集语法,在执行前将 JS 源代码进行 AOT(事先)编译。编译生成类型安全的、可直接执行的机器代码,消除了托管内存和垃圾回收,因而性能就变得可以预测了。
即将加入 JavaScript——对原生 SIMD 指令的支持
对 ASM.js 来说,还有一个第二位的,不那么重要的目标——将 JavaScript 建立为一种快速的目标语言。编译为 JS 的工具,像 mscripten 或 GWT,在 ASM.js 出现之前已经有一段时间了,由于将底层语言编译为 JavaScript 的习惯使然,对于这些编译器生成的源代码,引擎开始做优化。ASM.js 自然延续了这种做法(正像你注意到的,我努力不去过度使用“演进”这个词),定义一种通用的“语言”(一种标准语法格式),使得这些工具输出的源代码可以进行比较,易于优化,或许还有那么点儿可读性(当然,不可能照顾到每个人的习惯)。