Symbol.unscopables

Symbol.unscopables 静态数据属性代表 众所周知的符号 Symbol.unscopableswith 语句在范围对象上查找此符号,以查找包含不应成为 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

¥Value

众所周知的符号 Symbol.unscopables

¥The well-known symbol Symbol.unscopables.

Property attributes of Symbol.unscopables
Writable no
Enumerable no
Configurable no

描述

¥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().

示例

¥Examples

使用语句确定范围

¥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.

js
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.

js
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]:

js
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:

js
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:

js
with (character) {
  console.log(name + ' says: "' + toString() + '"'); // Yoda says: "[object Undefined]"
}

这是因为当查找 character[Symbol.unscopables].toString 时,它返回 Object.prototype.toString(),这是一个真值,从而使 with() 语句中的 toString() 调用引用 globalThis.toString(),并且因为在没有 this 的情况下调用它,所以 thisundefined,使其返回 [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.

js
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.

js
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

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看