我们已经介绍过的JS特性,和将在这个系列的其他部分中讲解的相当一部分特性都是新近增加的,不一定在老版本的浏览器中可用。事实上,语言规范中的一些最新特性甚至在任何稳定的浏览中都没有被实现。

那么,你拿这些新东西怎么办?你只能等上几年或者十几年直到老版本浏览器归于尘土?

这确实是许多人认为的情况,但是它不是JS健康的进步方式。

有两种主要的技术可以将新的JavaScript特性“带到”老版本的浏览器中:填补和转译。

填补

“填补(Polyfilling)”是一个人为发明的词(由Remy Sharp创造)(https://remysharp.com/2010/10/08/what-is-a-polyfill)。它是指拿来一个新特性的定义并制造一段行为等价的代码,但是这段代码可以运行在老版本的JS环境中。

例如,ES6定义了一个称为Number.isNaN(..)的工具,来为检查NaN值提供一种准确无误的方法,同时废弃原来的isNaN(..)工具。这个工具可以很容易填补,因此你可开始在你的代码中使用它,而不管最终用户是否在一个ES6浏览器中。

考虑如下代码:

  1. if (!Number.isNaN) {
  2. Number.isNaN = function isNaN(x) {
  3. return x !== x;
  4. };
  5. }

if语句决定着在这个工具已经存在的ES6环境中不再进行填补。如果它还不存在,我们就定义Number.isNaN(..)

注意: 我们在这里做的检查利用了NaN值的怪异之处,即它们是整个语言中唯一与自己不相等的值。所以NaN是唯一可能使x !== xtrue的值。

并不是所有的新特性都可以完全填补。有时一种特性的大部分行为可以被填补,但是仍然存在一些小的偏差。在实现你自己的填补时你应当非常非常小心,来确保你尽可能严格地遵循语言规范。

或者更好地,使用一组你信任的,经受过检验的填补,比如那些由ES5-Shim(https://github.com/es-shims/es5-shim)和ES6-Shim(https://github.com/es-shims/es6-shim)提供的。

转译

没有任何办法可以填补语言中新增加的语法。在老版本的JS引擎中新的语法将因为不可识别/不合法而抛出一个错误。

所以更好的选择是使用一个工具将你的新版本代码转换为等价的老版本代码。这个处理通常被称为“转译(transpiling)”,表示转换 + 编译。

实质上,你的源代码是使用新的语法形式编写的,但是你向浏览器部署的是转译过的旧语法形式。你一般会将转译器插入到你的构建过程中,与你的代码linter和代码压缩器类似。

你可能想知道为什么要麻烦地使用新语法编写程序又将它转译为老版本代码 —— 为什么不直接编写老版本代码呢?

关于转译你应当注意几个重要的原因:

  • 在语言中新加入的语法是为了使你的代码更具可读性和维护性而设计的。老版本的等价物经常会绕多得多的圈子。你应当首选编写新的和干净的语法,不仅为你自己,也为了开发团队的其他的成员。
  • 如果你仅为老版本浏览器转译,而给最新的浏览器提供新语法,那么你就可以利用浏览器对新语法进行的性能优化。这也让浏览器制造商有更多真实世界的代码来测试它们的实现和优化方法。
  • 提早使用新语法可以允许它在真实世界中被测试得更加健壮,这给JavaScript委员会(TC39)提供了更早的反馈。如果问题被发现的足够早,他们就可以在那些语言设计错误变得无法挽回之前改变/修改它。

这是一个转译的简单例子。ES6增加了一个称为“默认参数值”的新特性。它看起来像是这样:

  1. function foo(a = 2) {
  2. console.log( a );
  3. }
  4. foo(); // 2
  5. foo( 42 ); // 42

简单,对吧?也很有用!但是这种新语法在前ES6引擎中是不合法的。那么转译器将会对这段代码做什么才能使它在老版本环境中运行呢?

  1. function foo() {
  2. var a = arguments[0] !== (void 0) ? arguments[0] : 2;
  3. console.log( a );
  4. }

如你所见,它检查arguments[0]值是否是void 0(也就是undefined),而且如果是,就提供默认值2;否则,它就赋值被传递的任何东西。

除了可以现在就在老版本浏览器中使用更好的语法以外,观察转译后的代码实际上更清晰地解释了意图中的行为。

仅从ES6版本的代码看来,你可能还不理解undefined是唯一不能作为参数默认值的明确传递的值,但是转译后的代码使这一点清楚的多。

关于转译要强调的最后一个细节是,现在它们应当被认为是JS开发的生态系统和过程中的标准部分。JS将继续以比以前快得多的速度进化,所以每几个月就会有新语法和新特性被加入进来。

如果你默认地使用一个转译器,那么你将总是可以在发现新语法有用时,立即开始使用它,而不必为了让今天的浏览器被淘汰而等上好几年。

有好几个了不起的转译器供你选择。这是一些在本书写作时存在的好选择: