跳至正文

编译器假设

默认情况下,Babel 会尝试编译您的代码,使其尽可能地与原生行为相匹配。然而,这有时意味着会生成更多的输出代码,或者更慢的输出代码,仅仅是为了支持一些您不关心的边缘情况。

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

例如

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

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

提示

您是否正在从 @babel/preset-envloosespec 选项迁移到粒度假设?请查看“从 @babel/preset-env"loose""spec" 模式迁移”,了解基于假设的等效配置,可以直接复制粘贴作为起点。

arrayLikeIsIterable

当展开或迭代类数组对象时,假设它实现了一个 [Symbol.iterator] 方法,其行为与原生 Array.prototype[Symbol.iterator] 相同,因此可以直接按索引迭代其元素。

例如,这在旧版浏览器中迭代 DOM 集合时非常有用。

JavaScript
let images = $("img");

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

const copy = [...images];

constantReexports

从模块重新导出绑定时,假设它不会改变,因此可以直接导出它,就像您在执行以下操作一样

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

export const value = val;

*注意:* 这也会影响 transform-modules-umdtransform-modules-amd 插件。

JavaScript
export { value } from "dependency";

constantSuper

类的超类可以通过使用 Object.setPrototypeOf 随时更改,这使得 Babel 无法静态地知道它。启用此选项后,Babel 假设它永远不会更改,因此它始终是类声明中 extends 子句中的值。

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

enumerableModuleMeta

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

JavaScript
export const number = 2;

ignoreFunctionLength

函数具有 .length 属性,该属性反映了直到最后一个非默认参数的参数数量。启用此选项后,假设编译后的代码不依赖于此 .length 属性。

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

ignoreToPrimitiveHint

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

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

iterableIsArray

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

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

JavaScript
let str = tag`a`;

noClassCalls

转换类时,假设它们始终使用 new 实例化,并且从不作为函数调用。

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

noDocumentAll

当使用检查 nullundefined 的运算符时,假设它们从不与特殊值 document.all 一起使用。

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

noIncompleteNsImportDetection

假设在初始化之前没有观察到模块导出对象的任何自有属性。例如,当尝试访问 ns.foo 时,无论是否启用此假设,它都将返回 undefined。区别在于,当 noIncompleteNsImportDetection: true 时,Object.prototype.hasOwnProperty.call(ns, "foo") 将返回 false

JavaScript
export var foo;

noNewArrows

假设代码从不尝试使用 new 实例化箭头函数,这在规范中是不允许的。

*注意:* 此假设默认为 true。从 Babel 8 开始,它将默认为 false

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

noUninitializedPrivateFieldAccess

历史记录
版本更改
v7.24.0添加了 noUninitializedPrivateFieldAccess 假设

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

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

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

objectRestNoSymbols

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

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

privateFieldsAsProperties

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

JavaScript
class Foo {
#method() {}

#field = 2;

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

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

privateFieldsAsSymbols

历史记录
版本更改
v7.21.0添加了 privateFieldsAsSymbols 假设

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

class Foo {
#method() {}

#field = 2;

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

pureGetters

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

JavaScript
let a = obj;

a.b?.();

setClassMethods

声明类时,假设方法不会遮蔽超类原型上的访问器或不可写属性,并且程序不依赖于方法的不可枚举性。因此,可以直接分配方法,而不必使用 Object.defineProperty

JavaScript
class Foo extends Bar {
method() {}

static check() {}
}

setComputedProperties

当使用计算属性时,假设对象不包含覆盖在同一对象中定义的 setter 的属性,因此可以直接分配它们,而不必使用 Object.defineProperty 来定义它们。

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

setPublicClassFields

当使用公共类字段时,假设它们不会遮蔽当前类、其子类或其超类中的任何 getter。因此,可以直接分配它们,而不必使用 Object.defineProperty

JavaScript
class Test {
field = 2;

static staticField = 3;
}

setSpreadProperties

当使用对象展开运算符时,假设展开的属性不会触发目标对象上的 getter,因此可以直接分配它们,而不必使用 Object.defineProperty 来定义它们。

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

skipForOfIteratorClosing

当将 for-of 与迭代器一起使用时,应始终使用 .return() 关闭它,并在出现错误时使用 .throw() 关闭它。启用此选项后,Babel 假设这些方法未定义或为空,并且会避免调用它们。

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

superIsCallableConstructor

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

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

@babel/preset-env"loose""spec" 模式迁移

@babel/preset-envloose 选项等效于以下配置

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

@babel/preset-envspec 选项等效于以下配置

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