不妨先来打量一下 JavaScript。JavaScript 是唯一至今主流且基于原型的语言,虽然说支持面向对象,但是无论是封装、继承、多态,实现起来总需要用到一些 tricky 的办法,而且也不甚完美。事实上,为了抢市场,从 1995 年 JavaScript 设计完成到发布,只有短短的 7、8 个月的时间,极度缺乏谨慎的语言特性和规范的评估。而和微软 JScript 的竞争,使得 EcmaScript 标准仓促问世,这些都是 JavaScript 存在诸多缺陷的重要因素。
不止如此,Google 认定,JavaScript 的缺陷难以以改良的方式被修复,必须革命。那么都有哪些缺陷呢?推荐大家去阅读《Using JavaScript as a Real Programming Language》,作者是曾经 SUN 公司的 Tommi Mikkonen 和 Antero Taivalsaari。我挑选互联网上热议的几条观点说说:
- 语法过于松散。JavaScript 对于错误的兼容性很好,不到迫不得已的时刻不随便抛出异常,这有时候会让问题定位变得困难。代码随意性很强,可以实现类和对象的封装效果,也可以随意放置全局变量、全局方法,命名污染、冲突和覆盖问题难以发现。还有像 JSDoc 等第三方组件用于提供额外的契约来帮助提升代码规范和约束性,但这样的契约并非来自语言本身,而是在注释中。另外,语法过于松散也使得性能提升变得较为困难。
- 缺乏模块化能力。有一些框架专门致力于解决这个问题(比如 sea.js),但是语言本身未能从语法语义上提供 import 和 cascade 的依赖能力,也缺乏按需加载的能力(按需加载请参见 Java 的类加载机制)。缺乏模块化能力直接影响到大型项目的构建,我们不得不引入诸多框架和约束来保证大型项目在 JavaScript 部分的顺利进行。
- 核心库的不完备性。这点会在 HTML5 中逐步得到改善。JavaScript 已经逐渐跳出客户端页面元素显示和行为的原本职工作了,现在可以做到更多的事,比如涉及网络、图像处理、声音处理、线程处理等等。不完备表现的另一方面就是常规操作的复杂性,比如对 DOM 的操作我们不得不借由 JQuery 等等 JavaScript 第三方库来帮助简化 DOM 操作和绑定的行为。
- JavaScript 的编程语言范型不明确。它的编程模型和传统的 C 、Java 大相径庭。为何前后端的编程要分离开来,这是原因之一,也是让诸多前端工程师难受过的壁垒之一。
事实上,Google 对于 JavaScript 缺陷是相当有发言权的,Ajax 的兴起,靠的就是 Gmail 等几个 Google 标志性服务的功劳。为了改善 JavaScript 解释的性能,V8 引擎通过统计学对 JavaScript 执行的分析,强化了 JIT 编译能力,发布后 Chrome 就一下在性能上击垮了其它浏览器,把浏览器的纷争从功能上拉到性能上,这才有其它浏览器纷纷跟上的故事,比如 IE 的 Chakan 引擎,苹果的 Safari Nitro 引擎等等。
推荐大家去看看 Google 对 Dart 的宣传视频(YouTube 的链接在此,需要翻墙),Dart 的目标被概括为一句话:
Dart helps developers from all platforms build complex, high performance client apps from the modern web.
这里正好提及了 JavaScript 的三个软肋:跨平台/浏览器表现的一致性,对复杂应用的支持,以及性能。
官方说明中 Dart 的关键特性也包括了一些对 JavaScript 缺陷的修复:
- 语言层面上支持类和接口,帮助封装和重用。
- 支持可选类型,用户可以像 JavaScript 一样写弱类型的定义,也可以确定类型。你可以写出动态语言风格的代码,也可以写出类似于传统静态风格的代码。
- 对库的良好支持。
- 开发工具上的增强,尤其对于 Dart 虚拟机下运行的场景,开发工具可以做出更多更好的支持。
可是,如果你仅仅把 Dart 当做 JavaScript 修复缺陷的替代品,那你就太小看 Google 的野心了(关于 Google 在 Dart 上的的野心,请参见这篇文章)。Google 一向对那些传统和主流的东西有敢于挑战的勇气,虽然不见得总是能够成功。不妨看看 Go 语言,它的诞生就是要挑战传统的 C 语言,在保持甚至增强 C 语言的性能优势的基础上,填补了 C 语言的一些大坑,比如类型不安全、对并发和通信支持较弱等等。可是 Go 的目标并不仅仅在 C 当前的领域,它虽然不想完全替代 C,但也不想仅限于替代 C,Go 语言的网站就是用 Go 写成,可见它被寄希望的应用领域要广阔得多。
类似地,Dart 的价值不仅仅在于语言层面,否则它就是第二个 CoffieScript;面对纷繁复杂的浏览器,它更希望在虚拟机层面做到统一,就像 Java 做到的那样。因此,Dart 能够转化成 JavaScript 仅仅是一个长期战略过程中让变化显得不那么突兀的步骤之一(参见当年 Office 要在中国干掉 WPS,类似的做法,先兼容 WPS 文件,等到条件成熟,再放弃对 WPS 文件的兼容),毕竟程序员在 JavaScript 和 Dart 的选择上,拥有主导权。
不过需要看到的是,Dart2JS 做得还远不够好,一段 hello world 的代码生成的 JavaScript 未压缩代码可以有几千行,这方面 Dart 的团队正在优化。例如引入 tree shaking 技术,简言之就是遍历代码后,寻找那些 JavaScript 中没有被使用的方法,并删除之。
另一方面,Dart 还希望做到服务端和客户端的统一。事实上,只有 Node.js 或者 GWT 等等少数情况下能够做到这一点,而 Dart 本身就支持在浏览器或者命令行下运行,Dart 虚拟机可以帮助你屏蔽掉这些差异。
在计划实施上,除了明确的 “跳蛙战略”,Dart 提供了完备的环境和组件,官方称之为 “batteries included”,包括库、VM、IDE、浏览器集成和 DartJS 的编译器等等。
在此多了解一下 Dart VM。Dart VM 并非像 JVM 一样基于字节码的,而是没有中间代码,直接基于 Dart 语言本身的。基于字节码的好处在于开发者可以自行选择喜好的语言,最终编译成统一的字节码。除了 Java,Scala、Groovy、Clojure 等等都是在基于字节码的虚拟机上跑的,可是看看 JVM,似乎就是为了 Java 而生的,其它语言要在 JVM 层面上做额外的优化就很困难(这也是一些人提出 “Java 快死了,但是 JVM 还活着” 这样的观点时,我对于 JVM 部分的担忧)。如果是语言本身级别的虚拟机,就没有这个问题。
虚拟机常常存在启动缓慢的问题,一方面是 VM 本身需要启动时间,另一方面 VM 对于加载的代码需要经过预处理、解析、校验、初始化等等过程,为了缓解这一问题,Dart VM 提供了堆快照功能,在某个时刻下,遍历应用程序堆并将所有的对象写入文件,而在以后的 Dart Vm 启动时,直接把这个文件 dump 到内存中以提高启动速度。实际上,Dart 实例运行时和 JavaScript 类似,都是单线程的,因此它在当前执行环境的保存上,有了协程(coroutine)处理的经验,变得比较容易。而且堆快照看起来不算什么特别大的技术创新,本身也是从 Smalltalk 的映像中学来的,另外 V8 引擎也早就引入了快照功能。
很难说 Dart 挑战 JavaScript 的故事谁能获得胜利,但是可以看到的是,Google 在和传统技术的大战中,表现出来的野心,还有对标准的争夺。但是 JavaScript 天生的缺陷,注定它要在不久后的某一天,被某个替代者逐渐蚕食,无论这个替代者是不是 Dart。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》
×Scan to share with WeChat