Symbol.unscopables
Symbol.unscopables
静态数据属性表示 众所周知的符号 Symbol.unscopables
。 with
语句在作用域对象上查找此符号,以获取包含不应在 with
环境中成为绑定的属性集合的属性。
试一试
值
众所周知的符号 Symbol.unscopables
。
Symbol.unscopables 的属性属性 |
|
---|---|
可写 | 否 |
可枚举 | 否 |
可配置 | 否 |
描述
[Symbol.unscopables]
符号(通过 Symbol.unscopables
访问)可以定义在任何对象上,以排除属性名称在 with
环境绑定中作为词法变量公开。请注意,在使用 严格模式 时,with
语句不可用,并且此符号可能不需要。
将 [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");
}
包含 with (values)
的代码在添加 Array.prototype.values()
时导致某些网站在 Firefox 中出现故障 (Firefox Bug 883914)。此外,这意味着如果任何未来的数组方法添加会隐式更改 with
作用域,则可能会出现问题。因此,引入了 [Symbol.unscopables]
符号并在 Array
上实现为 Array.prototype[Symbol.unscopables]
以防止某些 Array 方法作用域到 with
语句中。
对象中的 Unscopables
您还可以为自己的对象设置 [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
}
避免使用非空原型对象作为 [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 语言规范 # sec-symbol.unscopables |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。