Skip to content

将 ES8 特性转译到 ES5

BabelJS - 将 ES2017 (ES8) 特性转译到 ES5

Section titled “BabelJS - 将 ES2017 (ES8) 特性转译到 ES5”

ECMAScript 2017(通常称为 ES8)为 JavaScript 引入了几个重要的特性。对于需要支持旧环境的项目,BabelJS 是将这些特性转译(Transpiling)到与 ES5 兼容的代码的关键工具。主要使用的工具是 @babel/preset-env,通常与 core-js 结合使用,为新特性提供 Polyfill(垫片)。

ES2017 的主要特性包括:

  • 异步函数(Async Functions) (async/await)
  • Object.values() / Object.entries()
  • 字符串填充(String padding) (padStart() / padEnd())
  • Object.getOwnPropertyDescriptors()
  • 函数参数列表和调用中的尾随逗号(Trailing commas)

我们将重点介绍其中的一些特性以及 Babel 如何处理它们。假设你已经使用 @babel/preset-env 和 core-js 设置了 Babel 以提供 Polyfill。

对于那些是新方法或对象(而不仅仅是语法)的特性,需要 Polyfill。更新你的 babel.config.json 文件:

{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}

并确保已安装 core-js:

npm install --save core-js

1. 异步函数(Async Functions) (async/await)

Section titled “1. 异步函数(Async Functions) (async/await)”

异步函数(Async functions)提供了一种更简洁的方式来处理基于 Promise 的异步操作,使异步代码看起来和行为更接近同步代码。

ES2017 代码(示例:async_example.js)

Section titled “ES2017 代码(示例:async_example.js)”
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result); // 预期输出: "resolved"
}
asyncCall();

@babel/preset-env(它包含了 @babel/plugin-transform-async-to-generator 插件)将异步函数转译为 generator 函数(generator functions)。这也需要 regenerator-runtime(通过上面配置的 core-js 或 @babel/plugin-transform-runtime 插件来处理)。

npx babel async_example.js --out-file async_example_es5.js

转译后的 ES5 代码(简化版,包含辅助函数)

Section titled “转译后的 ES5 代码(简化版,包含辅助函数)”
// 顶部可能包含 core-js/regenerator-runtime 的导入
// import "core-js/modules/es.object.to-string.js";
// import "core-js/modules/es.promise.js";
// import "regenerator-runtime/runtime.js";
"use strict";
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { /* ... 辅助函数 ... */ }
function _asyncToGenerator(fn) { /* ... 辅助函数 ... */ }
function resolveAfter2Seconds() {
return new Promise(function (resolve) {
setTimeout(function () {
resolve('resolved');
}, 2000);
});
}
function asyncCall() {
return _asyncCall.apply(this, arguments);
}
function _asyncCall() {
_asyncCall = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var result;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
console.log('calling');
_context.next = 3;
return resolveAfter2Seconds();
case 3:
result = _context.sent;
console.log(result);
case 5:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _asyncCall.apply(this, arguments);
}
asyncCall();

转译后的代码使用 generator 函数和 regeneratorRuntime(由 core-js 或 @babel/runtime 提供)来模拟 async/await 的行为。如果 Promise 在目标环境中不可用,也需要进行 Polyfill。

2. 字符串填充(String Padding) (padStart() / padEnd())

Section titled “2. 字符串填充(String Padding) (padStart() / padEnd())”

这些方法使用另一个字符串(如果需要会重复)填充当前字符串,直到结果字符串达到指定的长度。填充是从原始字符串的开头 (padStart) 或结尾 (padEnd) 应用。

ES2017 代码(示例:string_padding.js)

Section titled “ES2017 代码(示例:string_padding.js)”
const str = 'abc';
console.log(str.padStart(5, '_')); // Output: "__abc"
console.log(str.padStart(2, '_')); // Output: "abc" (length already >= 2)
console.log(str.padEnd(8, '*_')); // Output: "abc*_*_*"
console.log('test'.padStart(10)); // Output: " test" (pads with spaces by default)

这些是 String.prototype 上的新方法。Babel 本身不转译这些方法,但是配合 "useBuiltIns": "usage" 和 core-js,如果你的目标环境需要它们,Babel 会确保包含 String.prototype.padStart 和 String.prototype.padEnd 的 Polyfill。

npx babel string_padding.js --out-file string_padding_es5.js

转译后的 ES5 代码 (string_padding_es5.js)

Section titled “转译后的 ES5 代码 (string_padding_es5.js)”
// 顶部可能包含 core-js 字符串填充模块的导入
// e.g., import "core-js/modules/es.string.pad-start.js";
// e.g., import "core-js/modules/es.string.pad-end.js";
"use strict";
var str = 'abc';
console.log(str.padStart(5, '_'));
console.log(str.padStart(2, '_'));
console.log(str.padEnd(8, '*_'));
console.log('test'.padStart(10));

调用的语法保持不变。功能性由 Babel 导入的 Polyfill 提供(因为使用了 useBuiltIns: 'usage' 选项)。

要在浏览器中测试这一点,尤其是旧浏览器,你需要打包 string_padding_es5.js(如果由支持打包器的 Babel 设置生成,它现在包含 core-js 的导入,或者你需要确保 Polyfill 已全局加载)。

创建 test.html 文件:

<!DOCTYPE html>
<html>
<head>
<title>BabelJS ES2017 String Padding Test</title>
</head>
<body>
<p>Check the console for output.</p>
<!-- 假设 string_padding_es5.js 已打包或 polyfill 已全局可用 -->
<script src="path/to/your/bundled/string_padding_es5.js"></script>
</body>
</html>

在任何浏览器(现代浏览器或带有 Polyfill 的旧浏览器)中打开时,控制台应显示:

__abc
abc
abc*_*_*
test

Object.values() 返回一个给定对象自身可枚举属性值的数组。Object.entries() 返回一个给定对象自身可枚举字符串键属性的 [key, value] 对数组。

ES2017 代码(示例:object_methods.js)

Section titled “ES2017 代码(示例:object_methods.js)”
const obj = { a: 1, b: 'hello', c: true };
console.log(Object.values(obj)); // Output: [1, "hello", true]
console.log(Object.entries(obj)); // Output: [["a", 1], ["b", "hello"], ["c", true]]

与字符串填充类似,这些是 Object 上的新的静态方法。Babel 配合 core-js 将为它们提供 Polyfill。

转译后的 ES5 代码 (object_methods_es5.js)

Section titled “转译后的 ES5 代码 (object_methods_es5.js)”
// 顶部可能包含 core-js Object.values/entries 模块的导入
// e.g., import "core-js/modules/es.object.values.js";
// e.g., import "core-js/modules/es.object.entries.js";
"use strict";
var obj = {
a: 1,
b: 'hello',
c: true
};
console.log(Object.values(obj));
console.log(Object.entries(obj));

由于 core-js 提供的 Polyfill,对 Object.values() 和 Object.entries() 的调用可以在旧环境中工作。

Babel 的强大之处在于其通过预设(presets)和插件(plugins)实现的扩展性,并结合像 core-js 这样的 Polyfill 解决方案。这使得开发者可以自信地使用 ES2017 等现代 JavaScript 特性,同时知道他们的代码可以被转换,以便在广泛的目标环境中可靠运行。