with
已棄用:此特性不再推薦。雖然某些瀏覽器可能仍然支援它,但它可能已經從相關的網路標準中刪除,可能正在刪除過程中,或者可能僅為相容性目的而保留。請避免使用它,如果可能,請更新現有程式碼;請參閱本頁底部的相容性表格以指導您的決策。請注意,此特性可能隨時停止工作。
注意:不推薦使用 with 語句,因為它可能會導致令人困惑的錯誤和相容性問題,使得最佳化無法進行,並且在嚴格模式下是被禁止的。推薦的替代方法是將要訪問其屬性的物件賦值給一個臨時變數。
with 語句為一條語句擴充套件了作用域鏈。
語法
with (expression)
statement
描述
有兩種型別的識別符號:限定識別符號和非限定識別符號。非限定識別符號是指不指示其來源的識別符號。
foo; // unqualified identifier
foo.bar; // bar is a qualified identifier
通常,非限定識別符號透過在作用域鏈中搜索具有該名稱的變數來解析,而限定識別符號透過在物件的原型鏈中搜索具有該名稱的屬性來解析。
const foo = { bar: 1 };
console.log(foo.bar);
// foo is found in the scope chain as a variable;
// bar is found in foo as a property
一個例外是全域性物件,它位於作用域鏈的頂部,其屬性自動成為全域性變數,無需限定符即可引用。
console.log(globalThis.Math === Math); // true
在評估其語句體期間,with 語句將給定物件新增到作用域鏈的頭部。每個非限定名稱將首先在物件內搜尋(透過 in 檢查),然後才在上層作用域鏈中搜索。
請注意,如果非限定引用指向物件的方法,則該方法會以物件作為其 this 值進行呼叫。
with ([1, 2, 3]) {
console.log(toString()); // 1,2,3
}
物件可能具有 [Symbol.unscopables] 屬性,該屬性定義了不應新增到作用域鏈中的屬性列表(用於向後相容)。有關更多資訊,請參閱 Symbol.unscopables 文件。
使用 with 語句的原因包括節省一個臨時變數並透過避免重複冗長的物件引用來減小檔案大小。然而,with 語句不可取的原因遠不止這些。
-
效能:
with語句強制首先搜尋指定物件以進行所有名稱查詢。因此,所有不是指定物件成員的識別符號在with塊中會更慢地找到。此外,最佳化器無法對每個非限定識別符號所指的內容做出任何假設,因此每次使用識別符號時都必須重複相同的屬性查詢。 -
可讀性:
with語句使得人類讀者或 JavaScript 編譯器很難判斷非限定名稱是否會在作用域鏈中找到,如果找到,會在哪個物件中找到。例如:jsfunction f(x, o) { with (o) { console.log(x); } }如果你只看
f的定義,就無法判斷with主體中的x指的是什麼。只有當f被呼叫時,才能確定x是o.x還是f的第一個形式引數。如果你忘記在作為第二個引數傳遞的物件中定義x,你不會得到錯誤——相反,你會得到意想不到的結果。這種程式碼的實際意圖也尚不明確。 -
前向相容性:使用
with的程式碼可能不具備前向相容性,特別是當它與非普通物件一起使用時,這些物件將來可能會獲得更多屬性。考慮以下示例:jsfunction f(foo, values) { with (foo) { console.log(values); } }如果你在 ECMAScript 5 環境中呼叫
f([1, 2, 3], obj),with語句內的values引用將解析為obj。然而,ECMAScript 2015 在Array.prototype上引入了values屬性(因此它將在每個陣列上可用)。因此,在升級環境後,with語句內的values引用將解析為[1, 2, 3].values,這很可能導致錯誤。在這個特定示例中,
values透過Array.prototype[Symbol.unscopables]定義為不可作用域的,因此它仍然正確地解析為values引數。如果它沒有定義為不可作用域的,人們可以看到這將是一個多麼難以除錯的問題。
示例
使用 with 語句
以下 with 語句指定 Math 物件為預設物件。with 語句後面的語句引用 PI 屬性以及 cos 和 sin 方法,而無需指定物件。JavaScript 為這些引用假定 Math 物件。
let a, x, y;
const r = 10;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
透過解構屬性到當前作用域來避免使用 with 語句
你通常可以透過屬性解構來避免使用 with。這裡我們建立了一個額外的塊來模擬 with 建立額外作用域的行為——但在實際使用中,這個塊通常可以省略。
let a, x, y;
const r = 10;
{
const { PI, cos, sin } = Math;
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
透過使用 IIFE 來避免 with 語句
如果你正在生成一個必須多次重用長命名引用的表示式,並且你的目標是在表示式中消除該冗長的名稱,你可以將表示式包裝在 IIFE 中,並將長名稱作為引數提供。
const objectHavingAnEspeciallyLengthyName = { foo: true, bar: false };
if (((o) => o.foo && !o.bar)(objectHavingAnEspeciallyLengthyName)) {
// This branch runs.
}
使用 with 語句和代理建立動態名稱空間
with 會將每個變數查詢轉換為屬性查詢,而 代理(Proxies)允許捕獲每個屬性查詢呼叫。你可以透過結合它們來建立動態名稱空間。
const namespace = new Proxy(
{},
{
has(target, key) {
// Avoid trapping global properties like `console`
if (key in globalThis) {
return false;
}
// Trap all property lookups
return true;
},
get(target, key) {
return key;
},
},
);
with (namespace) {
console.log(a, b, c); // "a" "b" "c"
}
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-with-statement |
瀏覽器相容性
載入中…