由于特性的快速进化,给开发者们造成了一个糟糕的问题,他们强烈地渴望立即使用新特性,而同时被被现实打脸 —— 他们的网站/app需要支持那些不支持这些特性的老版本浏览器。

在整个行业中ES5的方式似乎已经无力回天了,它典型的思维模式是,代码库等待几乎所有的前ES5环境从它们的支持谱系中除名之后才开始采用ES5。结果呢,许多人最近(在本书写作时)才开始采用strict模式这样的东西,而它早在五年前就在ES5中定稿了。

对于JS生态系统的未来来说,等待和落后于语言规范那么多年被广泛地认为是一种有害的方式。所有负责推动语言演进的人都渴望这样的事情;只要新的特性和模式以规范的形式稳定下来,并且浏览器有机会实现它们,开发者就开始基于这些新的特性和模式进行编码。

那么我们如何解决这个看起来似乎矛盾的问题?答案是工具,特别是一种称为 转译(transpiling) 的技术(转换+编译)。大致上,它的想法是使用一种特殊的工具将你的ES6代码转换为可以在ES5环境中工作的等价物(或近似物!)。

例如,考虑属性定义缩写(见第二章的“对象字面扩展”)。这是ES6的形式:

  1. var foo = [1,2,3];
  2. var obj = {
  3. foo // 意思是 `foo: foo`
  4. };
  5. obj.foo; // [1,2,3]

这(大致)是它如何被转译:

  1. var foo = [1,2,3];
  2. var obj = {
  3. foo: foo
  4. };
  5. obj.foo; // [1,2,3]

这是一个微小但令人高兴的转换,它让我们在一个对象字面声明中将foo: foo缩写为foo,如果名称相同的话。

转译器为你实施这些变形,这个过程通常是构建工作流的一个步骤 —— 与你进行linting,压缩,和其他类似操作相似。

填补(Shims/Polyfills)

不是所有的ES6新特性都需要转译器。填补(也叫shims)是一种模式,在可能的情况下,它为一个新环境的行为定义一个可以在旧环境中运行的等价行为。语法是不能填补的,但是API经常是可以的。

例如,Object.is(..)是一个用来检查两个值严格等价性的新工具,它不带有===对于NaN-0值的那种微妙的例外。Object.is(..)的填补相当简单:

  1. if (!Object.is) {
  2. Object.is = function(v1, v2) {
  3. // 测试 `-0`
  4. if (v1 === 0 && v2 === 0) {
  5. return 1 / v1 === 1 / v2;
  6. }
  7. // 测试 `NaN`
  8. if (v1 !== v1) {
  9. return v2 !== v2;
  10. }
  11. // 其他的一切情况
  12. return v1 === v2;
  13. };
  14. }

提示:注意外部的if语句守护性地包围着填补的内容。这是一个重要的细节,它意味着这个代码段仅仅是为这个API还未定义的老环境而定义的后备行为;你想要覆盖既存API的情况是非常少见的。

有一个被称为“ES6 Shim”( https://github.com/paulmillr/es6-shim/ )的了不起的ES6填补集合,你绝对应该将它采纳为任何新JS项目的标准组成部分!

看起来JS将会继续一往无前的进化下去,同时浏览器也会持续地小步迭代以支持新特性,而不是大块大块地更新。所以跟上时代的最佳策略就是在你的代码库中引入填补,并在你的构建流程中引入一个转译器步骤,现在就开始习惯新的现实。

如果你决定维持现状,等待不支持新特性的所有浏览器都消失才开始使用新特性,那么你将总是落后于时代。你将可悲地错过所有新发明的设计 —— 而它们使编写JavaScript更有效,更高效,而且更健壮。