Skip to main content

编译器假设

默认情况下,Babel 会尽量编译你的代码,使其尽可能贴近原生行为。不过,这有时意味着会生成更多的输出代码,或者输出的代码更慢,只是为了支持一些你不在意的边缘情况。

🌐 By default Babel tries to compile your code so that it matches the native behavior as closely as possible. However, this sometimes means generating more output code, or slower output code, just to support some edge cases you don't care about.

从 Babel 7.13.0 开始,你可以在配置中指定一个 assumptions 选项,告诉 Babel 它可以对你的代码做哪些假设,从而更好地优化编译结果。注意:这取代了插件中的各种 loose 选项,转而使用可以应用于多个插件的顶层选项(RFC 链接)。

🌐 Since Babel 7.13.0, you can specify an assumptions option in your configuration to tell Babel which assumptions it can make about your code, to better optimize the compilation result. Note: this replaces the various loose options in plugins in favor of top-level options that can apply to multiple plugins (RFC link).

例如:

🌐 For example:

babel.config.json
{
"targets": ">0.5%",
"assumptions": {
"noDocumentAll": true,
"noClassCalls": true
},
"presets": ["@babel/preset-env"]
}
caution

这是高级功能。启用假设时请小心,因为它们不符合规范,可能会以意想不到的方式破坏你的代码。

tip

你是否正在从 @babel/preset-env 的 loose 和 spec 选项迁移到更细化的假设?查看 “从 @babel/preset-env 的 "loose" 和 "spec" 模式迁移”,获取等效的基于假设的配置,可以直接复制粘贴作为起点。

arrayLikeIsIterable​

在展开或迭代类似数组的对象时,假设它实现了一个与本地 Array.prototype[Symbol.iterator] 行为相同的 [Symbol.iterator] 方法,因此可以直接通过索引迭代它的元素。

🌐 When spreading or iterating an array-like object, assume that it implements a [Symbol.iterator] method with the same behavior of the native Array.prototype[Symbol.iterator], and thus directly iterate over its element by index.

这可能很有用,例如,在旧浏览器中迭代 DOM 集合。

🌐 This can be useful, for example, to iterate DOM collections in older browsers.

JavaScript
let images = $("img");

for (const img of images) {
console.log(img);
}

const copy = [...images];

constantReexports​

从模块重新导出绑定时,假设它没有更改,因此直接导出它是安全的,就像你在做

🌐 When re-exporting a binding from a module, assume that it doesn't change and thus it's safe to directly export it, as if you were doing

JavaScript
import { value as val } from "dep";

export const value = val;

注意: 这也会影响 transform-modules-umd 和 transform-modules-amd 插件。

JavaScript
export { value } from "dependency";

constantSuper​

一个类的超类可以随时通过使用 Object.setPrototypeOf 改变,这让 Babel 无法静态地知道它。当启用这个选项时,Babel 默认它从未改变,因此它总是类声明中 extends 子句中指定的值。

🌐 The super class of a class can be changed at any time by using Object.setPrototypeOf, making it impossible for Babel to statically know it. When this option is enabled, Babel assumes that it's never changed and thus it is always the value that was placed in the extends clause in the class declaration.

JavaScript
class Child extends Base {
method() {
super.method(2);
}
}

enumerableModuleMeta​

在将 ESM 编译为 CJS 时,Babel 会在 module.exports 对象上定义一个 __esModule 属性。假设你从不使用 for..in 或 Object.keys 遍历 module.exports 或 require("your-module") 的键,因此定义 __esModule 为可枚举是安全的。

🌐 When compiling ESM to CJS, Babel defines a __esModule property on the module.exports object. Assume that you never iterate over the keys of module.exports or of require("your-module") using for..in or Object.keys, and thus it's safe to define __esModule as enumerable.

JavaScript
export const number = 2;

ignoreFunctionLength​

函数有一个 .length 属性,它反映了直到最后一个非默认参数(且前面没有默认参数)的参数数量。当启用此选项时,假设编译后的代码不依赖这个 .length 属性。

🌐 Functions have a .length property that reflect the number of parameters up to the last non-default parameter that is not preceded by a default parameter. When this option is enabled, assume that the compiled code does not rely on this .length property.

JavaScript
function fn(a, b = 2, c, d = 3) {
return a + b + c + d;
}

ignoreToPrimitiveHint​

在使用可能会调用对象的 [Symbol.toPrimitive] 方法的语言特性时,假设它们不会根据 hint 参数改变其行为。

🌐 When using language features that might call the [Symbol.toPrimitive] method of objects, assume that they don't change their behavior based on the hint parameter.

JavaScript
let str = `a${foo}b`;

iterableIsArray​

使用可迭代对象时(在数组解构、for-of 或扩展中),假设它是一个数组。

🌐 When using an iterable object (in array destructuring, for-of or spreads), assume that it is an array.

JavaScript
const [first, ...rest] = obj;

call(first, ...obj);
let arr = [first, ...obj];

for (const el of obj) {
console.log(el);
}

mutableTemplateObject​

不要在为带标签的模板字面量创建的模板对象上使用 Object.freeze。这实际上意味着使用 taggedTemplateLiteralLoose 辅助函数而不是 taggedTemplateLiteral。

🌐 Don't use Object.freeze for the template object created for tagged template literals. This effectively means using the taggedTemplateLiteralLoose helper instead of taggedTemplateLiteral.

JavaScript
let str = tag`a`;

noClassCalls​

在转换类的时候,假设它们总是用 new 实例化,而且从来不会作为函数被调用。

🌐 When transforming classes, assume that they are always instantiate with new and they are never called as functions.

JavaScript
class Test {
constructor() {
this.x = 2;
}
}

noDocumentAll​

在使用检查 null 或 undefined 的操作符时,假设它们从来不会与特殊值 document.all 一起使用。

🌐 When using operators that check for null or undefined, assume that they are never used with the special value document.all.

JavaScript
let score = points ?? 0;
let name = user?.name;

noIncompleteNsImportDetection​

假设在初始化之前,模块导出对象的自身属性不会被观察到。例如,当尝试访问 ns.foo 时,不管这个假设是否启用,它都会返回 undefined。区别在于,当 noIncompleteNsImportDetection: true 时,Object.prototype.hasOwnProperty.call(ns, "foo") 会返回 false。

🌐 Assume that no own property of a module export object is observed before initialization. For example, when trying to access ns.foo, it will return undefined both with this assumption turned on or off. The difference is that Object.prototype.hasOwnProperty.call(ns, "foo") would return false when noIncompleteNsImportDetection: true.

JavaScript
export var foo;

noNewArrows​

假设代码从不尝试使用 new 来实例化箭头函数,因为规范不允许这样做。

🌐 Assume that the code never tries to instantiate arrow functions using new, which is disallowed according to the specification.

注意: 这个假设默认是 true。从 Babel 8 开始,它将默认改为 false。

JavaScript
let getSum = (a, b) => {
return { sum: a + b }
};

noUninitializedPrivateFieldAccess​

历史
版本变更
v7.24.0添加了 noUninitializedPrivateFieldAccess 假设

假设代码从不尝试在类初始化之前访问私有字段。例如:

🌐 Assume that code never attempts to access private fields on classes before they are initialized. For example:

JavaScript
class Foo {
x = this.#y; // #y is not initialized yet
#y = 2;
}
JavaScript
class MyClass {
static #id = 123;

method() {
return MyClass.#id;
}
}

objectRestNoSymbols​

在对象解构中使用剩余模式时,假设解构对象没有符号键,或者如果不复制它们就没有问题。

🌐 When using rest patterns in object destructuring, assume that destructured objects don't have symbol keys or that it's not a problem if they are not copied.

JavaScript
let { name, ...attrs } = obj;

privateFieldsAsProperties​

假设“软隐私”对于私有字段已经足够,因此它们可以作为带有唯一名称的公共不可枚举属性存储(而不是使用外部 WeakMap)。这使得调试已编译的私有字段更容易。

🌐 Assume that "soft privacy" is enough for private fields, and thus they can be stored as public non-enumerable properties with an unique name (rather than using an external WeakMap). This makes debugging compiled private fields easier.

JavaScript
class Foo {
#method() {}

#field = 2;

run() {
this.#method();
this.#field++;
}
}
caution

在使用内联 Babel 辅助时,生成的字符串键在每个文件中都是唯一的,而不是全局唯一的。这可能会在从不同文件扩展类时引起冲突,尤其是当私有字段同名时。

privateFieldsAsSymbols​

历史
版本变更
v7.21.0添加了 privateFieldsAsSymbols 假设

假设“软隐私”对于私有字段来说已经足够,因此它们可以作为带有符号键的公开属性存储(而不是使用外部的 WeakMap)。这让调试编译后的私有字段更容易。

🌐 Assume that "soft privacy" is enough for private fields, and thus they can be stored as public properties with a symbol key (rather than using an external WeakMap). This makes debugging compiled private fields easier.

class Foo {
#method() {}

#field = 2;

run() {
this.#method();
this.#field++;
}
}

pureGetters​

假设 getter(如果存在)没有副作用并且可以多次访问。

🌐 Assume that getters, if present, don't have side-effects and can be accessed multiple times.

JavaScript
let a = obj;

a.b?.();

setClassMethods​

在声明类的时候,假设方法不会覆盖父类原型上的访问器或不可写属性,而且程序不依赖方法不可枚举。因此,直接分秘诀法而不是使用 Object.defineProperty 是安全的。

🌐 When declaring classes, assume that methods don't shadow accessors or non-writable properties on the superclass prototype, and that the program doesn't depend on methods being non-enumerable. Thus, it's safe to assign methods rather than using Object.defineProperty.

JavaScript
class Foo extends Bar {
method() {}

static check() {}
}

setComputedProperties​

在使用计算属性时,假设对象中没有覆盖同一对象中定义的 setter 的属性,因此直接赋值是安全的,而不是使用 Object.defineProperty 来定义它们。

🌐 When using computed object properties, assume that the object doesn't contain properties that overwrite setter defined in the same object, and thus it's safe to assign them rather than defining them using Object.defineProperty.

JavaScript
let obj = {
set name(value) {},
[key]: val
}

setPublicClassFields​

在使用公共类字段时,假设它们不会覆盖当前类、子类或父类中的任何 getter。因此,直接赋值是安全的,而不必使用 Object.defineProperty。

🌐 When using public class fields, assume that they don't shadow any getter in the current class, in its subclasses or in its superclass. Thus, it's safe to assign them rather than using Object.defineProperty.

JavaScript
class Test {
field = 2;

static staticField = 3;
}

setSpreadProperties​

在使用对象展开时,假设展开的属性不会触发目标对象的 getter,因此直接赋值是安全的,而不需要使用 Object.defineProperty 来定义它们。

🌐 When using object spread, assume that spreaded properties don't trigger getters on the target object and thus it's safe to assign them rather than defining them using Object.defineProperty.

JavaScript
const result = {
set name(value) {},
...obj,
};

skipForOfIteratorClosing​

在使用 for-of 和迭代器时,它应该总是用 .return() 关闭,并且在出现错误时用 .throw() 关闭。当调用这个选项时,Babel 假设这些方法未定义或为空,因此会避免调用它们。

🌐 When using for-of with an iterator, it should always be closed with .return() and with .throw() in case of an error. When this option is called Babel assumes that those methods are not defined or empty, and it avoids calling them.

JavaScript
for (const val of iterable) {
console.log(val);
}

superIsCallableConstructor​

在扩展类时,假设父类是可调用的。这意味着无法扩展原生类或内置类,只能扩展编译类或 ES5 的 function 构造函数。

🌐 When extending classes, assume that the super class is callable. This means that it won't be possible to extend native classes or built-ins, but only compiled classes or ES5 function constructors.

JavaScript
class Child extends Parent {
constructor() {
super(42);
}
}

从 @babel/preset-env 的 "loose" 和 "spec" 模式迁移​

🌐 Migrating from @babel/preset-env's "loose" and "spec" modes

@babel/preset-env 的 loose 选项相当于以下配置:

JSON
{
"presets": [
["@babel/preset-env", { "exclude": ["transform-typeof-symbol"] }]
],
"assumptions": {
"arrayLikeIsIterable": true,
"constantReexports": true,
"ignoreFunctionLength": true,
"ignoreToPrimitiveHint": true,
"mutableTemplateObject": true,
"noClassCalls": true,
"noDocumentAll": true,
"objectRestNoSymbols": true,
"privateFieldsAsProperties": true,
"pureGetters": true,
"setClassMethods": true,
"setComputedProperties": true,
"setPublicClassFields": true,
"setSpreadProperties": true,
"skipForOfIteratorClosing": true,
"superIsCallableConstructor": true
}
}

@babel/preset-env 的 spec 选项相当于以下配置:

JSON
{
"presets": ["@babel/preset-env"],
"assumptions": {
"noNewArrows": false,
}
}