跳至主内容

编译器假设

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

默认情况下,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

当展开或迭代类数组对象时,假设其实现了与原生 Array.prototype[Symbol.iterator] 行为相同的 [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

History
VersionChanges
v7.24.0Added noUninitializedPrivateFieldAccess assumption

假设代码永远不会尝试在私有字段初始化前访问它们。例如:

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

History
VersionChanges
v7.21.0Added privateFieldsAsSymbols assumption

假设"软隐私"对私有字段已足够,因此可将它们存储为带符号键的公共属性(而非使用外部 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,
"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,
}
}