Symbol.unscopables
Symbol.unscopables
静态数据属性代表 众所周知的符号 Symbol.unscopables
。with
语句在范围对象上查找此符号,以查找包含不应成为 with
环境中的绑定的属性集合的属性。
¥The Symbol.unscopables
static data property represents the well-known symbol Symbol.unscopables
. The with
statement looks up this symbol on the scope object for a property containing a collection of properties that should not become bindings within the with
environment.
Try it
值
描述
¥Description
可以在任何对象上定义 [Symbol.unscopables]
符号(通过 Symbol.unscopables
访问),以排除属性名称在 with
环境绑定中作为词法变量公开。请注意,当使用 严格模式 时,with
语句不可用,并且可能不需要此符号。
¥The [Symbol.unscopables]
symbol (accessed via Symbol.unscopables
) can be defined on any object to exclude property names from being exposed as lexical variables in with
environment bindings. Note that when using strict mode, with
statements are not available, and this symbol is likely not needed.
将 [Symbol.unscopables]
对象的属性设置为 true
(或任何 truthy 值)将使 with
范围对象的相应属性变得不可作用,因此不会被引入到 with
主体作用域中。将属性设置为 false
(或任何 falsy 值)将使其具有作用域,从而显示为词法作用域变量。
¥Setting a property of the [Symbol.unscopables]
object to true
(or any truthy value) will make the corresponding property of the with
scope object unscopable and therefore won't be introduced to the with
body scope. Setting a property to false
(or any falsy value) will make it scopable and thus appear as lexical scope variables.
当决定 x
是否不可作用域时,会在 [Symbol.unscopables]
属性的整个原型链中查找名为 x
的属性。这意味着,如果你将 [Symbol.unscopables]
声明为普通对象,那么 Object.prototype
属性(如 toString
)也将变得不可作用域,这可能会导致旧代码向后不兼容(假设这些属性通常具有作用域)(请参阅 下面是一个例子)。建议你将自定义 [Symbol.unscopables]
属性设置为以 null
作为其原型,就像 Array.prototype[Symbol.unscopables]
一样。
¥When deciding whether x
is unscopable, the entire prototype chain of the [Symbol.unscopables]
property is looked up for a property called x
. This means if you declared [Symbol.unscopables]
as a plain object, Object.prototype
properties like toString
would become unscopable as well, which may cause backward incompatibility for legacy code assuming those properties are normally scoped (see an example below). You are advised to make your custom [Symbol.unscopables]
property have null
as its prototype, like Array.prototype[Symbol.unscopables]
does.
该协议也被 DOM API 使用,例如 Element.prototype.append()
。
¥This protocol is also utilized by DOM APIs, such as Element.prototype.append()
.
示例
使用语句确定范围
¥Scoping in with statements
以下代码在 ES5 及以下版本中运行良好。但是,在 ECMAScript 2015 中,引入了 Array.prototype.values()
方法。这意味着在 with
环境中,"values" 现在是 Array.prototype.values()
方法,而不是 with
语句之外的变量。
¥The following code works fine in ES5 and below. However, in ECMAScript 2015, the Array.prototype.values()
method was introduced. That means that inside a with
environment, "values" would now be the Array.prototype.values()
method and not the variable outside the with
statement.
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 错误 883914)。此外,这意味着如果任何未来的数组方法添加隐式更改了 with
范围,则可能会中断。因此,[Symbol.unscopables]
符号在 Array
上被引入并实现为 Array.prototype[Symbol.unscopables]
,以防止某些 Array 方法被限定在 with
语句中。
¥The code containing with (values)
caused some websites to malfunction in Firefox when Array.prototype.values()
was added (Firefox Bug 883914). Furthermore, this implies that any future array method addition may be breaking if it implicitly changes the with
scope. Therefore, the [Symbol.unscopables]
symbol was introduced and implemented on Array
as Array.prototype[Symbol.unscopables]
to prevent some of the Array methods being scoped into the with
statement.
对象中的不可作用域
¥Unscopables in objects
你还可以为自己的对象设置 [Symbol.unscopables]
。
¥You can also set [Symbol.unscopables]
for your own objects.
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]
¥Avoid using a non-null-prototype object as [Symbol.unscopables]
将 [Symbol.unscopables]
声明为普通对象而不消除其原型可能会导致微妙的错误。考虑以下代码在 [Symbol.unscopables]
之前运行:
¥Declaring [Symbol.unscopables]
as a plain object without eliminating its prototype may cause subtle bugs. Consider the following code working before [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]
属性。你可能会天真地这样做:
¥To preserve backward compatibility, you decided to add an [Symbol.unscopables]
property when adding more properties to character
. You may naïvely do it like:
const character = {
name: "Yoda",
toString: function () {
return "Use with statements, you must not";
},
student: "Luke",
[Symbol.unscopables]: {
// Make `student` unscopable
student: true,
},
};
然而,上面的代码现在崩溃了:
¥However, the code above now breaks:
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]
。
¥This is because when looking up character[Symbol.unscopables].toString
, it returns Object.prototype.toString()
, which is a truthy value, thus making the toString()
call in the with()
statement reference globalThis.toString()
instead — and because it's called without a this
, this
is undefined
, making it return [object Undefined]
.
即使该方法没有被 character
覆盖,使其不可作用域也会改变 this
的值。
¥Even when the method is not overridden by character
, making it unscopable will change the value of 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
属性。
¥To fix this, always make sure [Symbol.unscopables]
only contains properties you wish to be unscopable, without Object.prototype
properties.
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,
},
};
规范
Specification |
---|
ECMAScript Language Specification # sec-symbol.unscopables |
浏览器兼容性
BCD tables only load in the browser