Symbol.unscopables
Symbol.unscopables 靜態資料屬性表示 知名 Symbol Symbol.unscopables。 with 語句會在作用域物件上查詢此 Symbol,以獲取包含一組不應在 with 環境中成為繫結的屬性的集合。
試一試
const object = {
foo: 42,
};
object[Symbol.unscopables] = {
foo: true,
};
with (object) {
console.log(foo);
// Expected output: Error: foo is not defined
}
值
知名 Symbol Symbol.unscopables。
Symbol.unscopables 的屬性特性 | |
|---|---|
| 可寫 | 否 |
| 可列舉 | 否 |
| 可配置 | 否 |
描述
可以透過 Symbol.unscopables 訪問的 [Symbol.unscopables] Symbol 可以定義在任何物件上,以排除屬性名稱作為 with 環境繫結中的詞法變數被公開。請注意,在使用 嚴格模式 時,with 語句不可用,並且此 Symbol 可能不需要。
將 [Symbol.unscopables] 物件的屬性設定為 true(或任何 真值)將使 with 作用域物件的相應屬性成為不可作用域,因此不會引入到 with 主體作用域中。將屬性設定為 false(或任何 假值)將使其可作用域,從而作為詞法作用域變量出現。
在決定 x 是否不可作用域時,會查詢 [Symbol.unscopables] 屬性的整個原型鏈,以查詢名為 x 的屬性。這意味著,如果您將 [Symbol.unscopables] 宣告為一個普通物件,Object.prototype 的屬性(如 toString)也將變得不可作用域,這可能會導致向後不相容,因為舊程式碼可能假設這些屬性通常是作用域內的(請參閱 下面的示例)。建議使您的自定義 [Symbol.unscopables] 屬性的原型為 null,就像 Array.prototype[Symbol.unscopables] 所做的那樣。
DOM API(如 Element.prototype.append())也使用此協議。
示例
with 語句中的作用域
以下程式碼在 ES5 及更早版本中執行正常。但是在 ECMAScript 2015 中,引入了 Array.prototype.values() 方法。這意味著在 with 環境中,“values”現在將是 Array.prototype.values() 方法,而不是 with 語句外的變數。
var values = [];
with (values) {
// If [Symbol.unscopables] did not exist, values would become
// Array.prototype.values starting with ECMAScript 2015.
// And an error would have occurred.
values.push("something");
}
當 Array.prototype.values() 被新增時,包含 with (values) 的程式碼導致 Firefox 中的一些網站出現故障(Firefox Bug 883914)。此外,這暗示任何未來的陣列方法新增都可能具有破壞性,因為它可能隱式更改 with 作用域。因此,引入了 [Symbol.unscopables] Symbol,並將其在 Array 上實現為 Array.prototype[Symbol.unscopables],以防止某些 Array 方法被作用域到 with 語句中。
物件中的不可作用域
您也可以為自己的物件設定 [Symbol.unscopables]。
const obj = {
foo: 1,
bar: 2,
baz: 3,
};
obj[Symbol.unscopables] = {
// Make the object have `null` prototype to prevent
// `Object.prototype` methods from being unscopable
__proto__: null,
// `foo` will be scopable
foo: false,
// `bar` will be unscopable
bar: true,
// `baz` is omitted; because `undefined` is falsy, it is also scopable (default)
};
with (obj) {
console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // 3
}
避免使用非 null 原型物件作為 [Symbol.unscopables]
將 [Symbol.unscopables] 宣告為普通物件而不消除其原型可能會導致細微的錯誤。請考慮以下在 [Symbol.unscopables] 之前正常工作的程式碼。
const character = {
name: "Yoda",
toString: function () {
return "Use with statements, you must not";
},
};
with (character) {
console.log(name + ' says: "' + toString() + '"'); // Yoda says: "Use with statements, you must not"
}
為了保持向後相容性,您決定在向 character 新增更多屬性時新增一個 [Symbol.unscopables] 屬性。您可能天真地這樣做:
const character = {
name: "Yoda",
toString: function () {
return "Use with statements, you must not";
},
student: "Luke",
[Symbol.unscopables]: {
// Make `student` unscopable
student: true,
},
};
但是,上面的程式碼現在會中斷。
with (character) {
console.log(name + ' says: "' + toString() + '"'); // Yoda says: "[object Undefined]"
}
這是因為在查詢 character[Symbol.unscopables].toString 時,它會返回 Object.prototype.toString(),這是一個真值,因此使 with() 語句中的 toString() 呼叫指向 globalThis.toString() —— 因為它沒有 this 呼叫,所以 this 是 undefined,導致它返回 [object Undefined]。
即使方法沒有被 character 覆蓋,將其設為不可作用域也會改變 this 的值。
const proto = {};
const obj = { __proto__: proto };
with (proto) {
console.log(isPrototypeOf(obj)); // true; `isPrototypeOf` is scoped and `this` is `proto`
}
proto[Symbol.unscopables] = {};
with (proto) {
console.log(isPrototypeOf(obj)); // TypeError: Cannot convert undefined or null to object
// `isPrototypeOf` is unscoped and `this` is undefined
}
為了解決這個問題,請始終確保 [Symbol.unscopables] 只包含您希望不可作用域的屬性,而不包含 Object.prototype 屬性。
const character = {
name: "Yoda",
toString: function () {
return "Use with statements, you must not";
},
student: "Luke",
[Symbol.unscopables]: {
// Make the object have `null` prototype to prevent
// `Object.prototype` methods from being unscopable
__proto__: null,
// Make `student` unscopable
student: true,
},
};
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-symbol.unscopables |
瀏覽器相容性
載入中…