编译器假设
默认情况下,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
当展开或迭代类数组对象时,假设它实现了一个 [Symbol.iterator]
方法,其行为与原生 Array.prototype[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
历史记录
版本 | 更改 |
---|---|
v7.24.0 | 添加了 noUninitializedPrivateFieldAccess 假设 |
假设代码从不尝试在初始化之前访问类上的私有字段。例如
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
历史记录
版本 | 更改 |
---|---|
v7.21.0 | 添加了 privateFieldsAsSymbols 假设 |
假设“软隐私”对于私有字段来说已经足够了,因此它们可以存储为具有符号键的公共属性(而不是使用外部 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,
"noObjectSuper": true,
"noUndeclaredVariablesCheck": 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,
}
}