跳至主要內容

編譯器假設

預設情況下,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

歷史
版本變更
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

在物件解構中使用 rest 模式時,假設解構的物件沒有符號鍵,或者即使沒有複製,也沒有問題。

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,
}
}