編譯器假設
預設情況下,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
歷史
版本 | 變更 |
---|---|
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
在物件解構中使用 rest 模式時,假設解構的物件沒有符號鍵,或者即使沒有複製,也沒有問題。
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,
}
}