编译器假设
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
默认情况下,Babel 会尝试编译您的代码以尽可能接近原生行为。然而,这有时意味着生成更多输出代码或更慢的输出代码,仅仅是为了支持您不关心的边缘情况。
自 Babel 7.13.0 起,您可以在配置中指定 assumptions 选项,告知 Babel 可以对代码做出哪些假设,从而更好地优化编译结果。注意:这取代了插件中的各种 loose 选项,转而采用可应用于多个插件的顶层选项(RFC 链接)。
例如:
{
"targets": ">0.5%",
"assumptions": {
"noDocumentAll": true,
"noClassCalls": true
},
"presets": ["@babel/preset-env"]
}
这是高级功能。启用假设时请务必谨慎,因为它们不符合规范,并可能以意外方式破坏您的代码。
您是否正在从 @babel/preset-env 的 loose 和 spec 选项迁移到细粒度假设?请查看"从 @babel/preset-env 的 "loose" 和 "spec" 模式迁移"获取等效的基于假设的配置,可直接复制粘贴作为起点。
arrayLikeIsIterable
当展开或迭代类数组对象时,假设其实现了与原生 Array.prototype[Symbol.iterator] 行为相同的 [Symbol.iterator] 方法,从而可直接按索引迭代元素。
例如,在旧版浏览器中迭代 DOM 集合时,这可能会很有用。
let images = $("img");
for (const img of images) {
console.log(img);
}
const copy = [...images];
constantReexports
当从模块重新导出绑定时,假设其值不会改变,因此可直接导出,就像执行以下操作:
import { value as val } from "dep";
export const value = val;
注意:这也会影响 transform-modules-umd 和 transform-modules-amd 插件。
export { value } from "dependency";
constantSuper
类的超类可通过 Object.setPrototypeOf 随时更改,导致 Babel 无法静态获知。启用此选项后,Babel 假设超类永不更改,始终是类声明中 extends 子句的值。
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 定义为可枚举是安全的。
export const number = 2;
ignoreFunctionLength
函数具有反映参数数量(截至最后一个非默认参数)的 .length 属性。启用此选项后,假设编译后的代码不依赖此 .length 属性。
function fn(a, b = 2, c, d = 3) {
return a + b + c + d;
}
ignoreToPrimitiveHint
当使用可能调用对象 [Symbol.toPrimitive] 方法的语言特性时,假设其行为不会基于 hint 参数改变。
let str = `a${foo}b`;
iterableIsArray
当使用可迭代对象(在数组解构、for-of 或展开中)时,假设其为数组。
const [first, ...rest] = obj;
call(first, ...obj);
let arr = [first, ...obj];
for (const el of obj) {
console.log(el);
}
mutableTemplateObject
不为标签模板字面量创建的模板对象使用 Object.freeze。这实际上意味着使用 taggedTemplateLiteralLoose 辅助函数而非 taggedTemplateLiteral。
let str = tag`a`;
noClassCalls
转换类时,假设它们始终通过 new 实例化,且永远不会作为函数调用。
class Test {
constructor() {
this.x = 2;
}
}
noDocumentAll
当使用检查 null 或 undefined 的运算符时,假设它们永远不会与特殊值 document.all 一起使用。
let score = points ?? 0;
let name = user?.name;
noIncompleteNsImportDetection
假设在初始化前不会观测到模块导出对象的自有属性。
例如,尝试访问 ns.foo 时,无论此假设开启或关闭都会返回 undefined。区别在于当开启 noIncompleteNsImportDetection: true 时,Object.prototype.hasOwnProperty.call(ns, "foo") 将返回 false。
export var foo;
noNewArrows
假设代码永远不会尝试使用 new 实例化箭头函数(根据规范这是不允许的)。
注意:此假设默认为 true。从 Babel 8 开始将默认改为 false。
let getSum = (a, b) => {
return { sum: a + b }
};
noUninitializedPrivateFieldAccess
History
| Version | Changes |
|---|---|
| v7.24.0 | Added noUninitializedPrivateFieldAccess assumption |
假设代码永远不会尝试在私有字段初始化前访问它们。例如:
class Foo {
x = this.#y; // #y is not initialized yet
#y = 2;
}
class MyClass {
static #id = 123;
method() {
return MyClass.#id;
}
}
objectRestNoSymbols
在对象解构中使用剩余模式时,假设解构对象不包含符号键,或者即使未复制这些键也不会造成问题。
let { name, ...attrs } = obj;
privateFieldsAsProperties
假设"软隐私"对私有字段已足够,因此可将它们存储为具有唯一名称的公共不可枚举属性(而非使用外部 WeakMap)。这使编译后的私有字段更易于调试。
class Foo {
#method() {}
#field = 2;
run() {
this.#method();
this.#field++;
}
}
使用内联 Babel 辅助函数时,生成的字符串键是文件内唯一而非全局唯一。当从不同文件继承具有同名私有字段的类时,可能导致冲突。
privateFieldsAsSymbols
History
| Version | Changes |
|---|---|
| v7.21.0 | Added privateFieldsAsSymbols assumption |
假设"软隐私"对私有字段已足够,因此可将它们存储为带符号键的公共属性(而非使用外部 WeakMap)。这使编译后的私有字段更易于调试。
class Foo {
#method() {}
#field = 2;
run() {
this.#method();
this.#field++;
}
}
pureGetters
假设存在的 getter 没有副作用且可被多次访问。
let a = obj;
a.b?.();
setClassMethods
声明类时,假设方法不会遮蔽超类原型上的访问器或不可写属性,且程序不依赖方法的不可枚举性。因此可以安全地直接赋值而非使用 Object.defineProperty。
class Foo extends Bar {
method() {}
static check() {}
}
setComputedProperties
使用计算对象属性时,假设对象不包含覆盖同对象内 setter 定义的属性,因此可以安全地直接赋值而非使用 Object.defineProperty。
let obj = {
set name(value) {},
[key]: val
}
setPublicClassFields
使用公共类字段时,假设它们不会遮蔽当前类、子类或超类中的任何 getter。因此可以安全地直接赋值而非使用 Object.defineProperty。
class Test {
field = 2;
static staticField = 3;
}
setSpreadProperties
使用对象展开时,假设展开的属性不会触发目标对象的 getter,因此可以安全地直接赋值而非使用 Object.defineProperty。
const result = {
set name(value) {},
...obj,
};
skipForOfIteratorClosing
使用带迭代器的 for-of 循环时,应始终通过 .return() 关闭迭代器,出错时还需调用 .throw()。开启此选项后 Babel 假设这些方法未定义或为空,从而避免调用它们。
for (const val of iterable) {
console.log(val);
}
superIsCallableConstructor
扩展类时,假设超类是可调用的。这意味着无法扩展原生类或内置对象,只能扩展编译后的类或 ES5 的 function 构造函数。
class Child extends Parent {
constructor() {
super(42);
}
}
从 @babel/preset-env 的 "loose" 和 "spec" 模式迁移
@babel/preset-env 的 loose 选项等同于以下配置:
{
"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 选项等同于以下配置:
{
"presets": ["@babel/preset-env"],
"assumptions": {
"noNewArrows": false,
}
}