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 |
浏览器兼容性
加载中…