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"]
}
提醒

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

¥This is advanced functionality. Please be careful when enabling assumptions, because they are not spec-compliant and may break your code in unexpected ways.

提示

你是否正在从 @babel/preset-envloosespec 选项迁移到精细假设?检查 “从 @babel/preset-env"loose""spec" 模式迁移” 是否有等效的基于假设的配置,准备复制并粘贴作为起点。

¥Are you migrating from @babel/preset-env's loose and spec options to granular assumptions? Check "Migrating from @babel/preset-env's "loose" and "spec" modes" for the equivalent assumptions-based configuration, ready to be copied and pasted as a starting point.

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-umdtransform-modules-amd 插件。

¥NOTE: This also affects the transform-modules-umd and transform-modules-amd plugins.

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..inObject.keys 迭代 module.exportsrequire("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. 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

当使用检查 nullundefined 的运算符时,假设它们从未与特殊值 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。不同的是,Object.prototype.hasOwnProperty.call(ns, "foo") 会在 noIncompleteNsImportDetection: true 时返回 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

¥NOTE: This assumption defaults to true. It will default to false starting from Babel 8.

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

noUninitializedPrivateFieldAccess

History
版本变化
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++;
}
}
提醒

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

¥When using inline Babel helpers, generated string keys are unique per-file and not globally. This could cause conflicts when extending classes from a different fields with private fields with the same name.

privateFieldsAsSymbols

History
版本变化
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-envloose 选项相当于以下配置:

¥@babel/preset-env's loose option is equivalent to the following configuration:

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-envspec 选项相当于以下配置:

¥@babel/preset-env's spec option is equivalent to the following configuration:

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