with
Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.
注意:不建议使用
with
语句,因为它可能是令人困惑的错误和兼容性问题的根源,使优化变得不可能,并且在 严格模式 中被禁止。推荐的替代方法是将要访问其属性的对象分配给临时变量。¥Note: Use of the
with
statement is not recommended, as it may be the source of confusing bugs and compatibility issues, makes optimization impossible, and is forbidden in strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.
with
语句扩展了语句的作用域链。
¥The with
statement extends the scope chain for a statement.
语法
¥Syntax
with (expression)
statement
expression
-
将给定表达式添加到计算语句时使用的作用域链。表达式两边的括号是必需的。
statement
-
任何声明。要执行多个语句,请使用 block 语句 (
{ ... }
) 对这些语句进行分组。
描述
¥Description
有两种类型的标识符:限定标识符和非限定标识符。非限定标识符是指不表明其来源的标识符。
¥There are two types of identifiers: a qualified identifier and an unqualified identifier. An unqualified identifier is one that does not indicate where it comes from.
foo; // unqualified identifier
foo.bar; // bar is a qualified identifier
通常,通过在作用域链中搜索具有该名称的变量来解析非限定标识符,而通过在对象的原型链中搜索具有该名称的属性来解析限定标识符。
¥Normally, an unqualified identifier is resolved by searching the scope chain for a variable with that name, while a qualified identifier is resolved by searching the prototype chain of an object for a property with that name.
const foo = { bar: 1 };
console.log(foo.bar);
// foo is found in the scope chain as a variable;
// bar is found in foo as a property
一个例外是 全局对象,它位于作用域链的顶部,其属性自动成为无需限定符即可引用的全局变量。
¥One exception to this is the global object, which sits on top of the scope chain, and whose properties automatically become global variables that can be referred to without qualifiers.
console.log(globalThis.Math === Math); // true
with
语句在其语句主体求值期间将给定对象添加到此作用域链的头部。每个非限定名称将首先在对象内搜索(通过 in
检查),然后再在上层作用域链中搜索。
¥The with
statement adds the given object to the head of this scope chain during the evaluation of its statement body. Every unqualified name would first be searched within the object (through a in
check) before searching in the upper scope chain.
请注意,如果非限定引用引用对象的方法,则以该对象作为其 this
值来调用该方法。
¥Note that if the unqualified reference refers to a method of the object, the method is called with the object as its this
value.
with ([1, 2, 3]) {
console.log(toString()); // 1,2,3
}
该对象可能有一个 [Symbol.unscopables]
属性,它定义了不应添加到作用域链的属性列表(为了向后兼容)。有关详细信息,请参阅 Symbol.unscopables
文档。
¥The object may have an [Symbol.unscopables]
property, which defines a list of properties that should not be added to the scope chain (for backward compatibility). See the Symbol.unscopables
documentation for more information.
使用 with
语句的原因包括保存一个临时变量并通过避免重复冗长的对象引用来减小文件大小。然而,with
语句不可取的原因还有很多:
¥The reasons to use a with
statement include saving one temporary variable and reducing file size by avoiding repeating a lengthy object reference. However, there are far more reasons why with
statements are not desirable:
- 表现:
with
语句强制在所有名称查找中首先搜索指定对象。因此,所有不是指定对象成员的标识符在with
块中的查找速度会更慢。此外,优化器无法对每个非限定标识符所指的内容做出任何假设,因此每次使用该标识符时,它都必须重复相同的属性查找。 - 可读性:
with
语句使得人类读者或 JavaScript 编译器很难决定是否会在作用域链中找到不合格的名称,如果是的话,在哪个对象中找到。例如:如果只看jsfunction f(x, o) { with (o) { console.log(x); } }
f
的定义,是无法判断with
体内的x
指的是什么。只有调用f
时,才能确定x
是o.x
还是f
的第一个形参。如果你忘记在作为第二个参数传递的对象中定义x
,你不会收到错误 - 相反,你只会得到意外的结果。目前还不清楚此类代码的实际意图是什么。 -
向前兼容性:使用
with
的代码可能不向前兼容,尤其是与普通对象以外的对象一起使用时,将来可能会获得更多属性。考虑这个例子:如果你在 ECMAScript 5 环境中调用jsfunction f(foo, values) { with (foo) { console.log(values); } }
f([1, 2, 3], obj)
,则with
语句内的values
引用将解析为obj
。但是,ECMAScript 2015 在Array.prototype
上引入了values
属性(因此它将在每个数组上可用)。因此,升级环境后,with
语句中的values
引用会解析为[1, 2, 3].values
,并且可能会导致错误。 在此特定示例中,values
被定义为无法通过Array.prototype[Symbol.unscopables]
进行作用域,因此它仍然可以正确解析为values
参数。如果它没有被定义为不可作用域,人们就会发现这将是一个难以调试的问题。
示例
使用 with 语句
¥Using the with statement
以下 with
语句指定 Math
对象是默认对象。with
语句后面的语句引用 PI
属性以及 cos
和 sin
方法,但不指定对象。JavaScript 假定这些引用是 Math
对象。
¥The following with
statement specifies that the Math
object is the default object. The statements following the with
statement refer to the PI
property and the cos
and sin
methods, without specifying an object. JavaScript assumes the Math
object for these references.
let a, x, y;
const r = 10;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
通过将属性解构到当前范围来避免使用 with 语句
¥Avoiding the with statement by destructuring properties into the current scope
你通常可以避免使用 with
到 属性解构。这里我们创建一个额外的块来模仿 with
创建额外作用域的行为 - 但在实际使用中,这个块通常可以被省略。
¥You can usually avoid using with
through property destructuring. Here we create an extra block to mimic the behavior of with
creating an extra scope — but in actual usage, this block can usually be omitted.
let a, x, y;
const r = 10;
{
const { PI, cos, sin } = Math;
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
使用 IIFE 避免使用 with 语句
¥Avoiding the with statement by using an IIFE
如果你生成的表达式必须多次重复使用长名称引用,并且你的目标是消除表达式中的冗长名称,则可以将该表达式封装在 IIFE 中并提供长名称作为参数。
¥If you're producing an expression that must reuse a long-named reference multiple times, and your goal is to eliminate that lengthy name within your expression, you can wrap the expression in an IIFE and provide the long name as an argument.
const objectHavingAnEspeciallyLengthyName = { foo: true, bar: false };
if (((o) => o.foo && !o.bar)(objectHavingAnEspeciallyLengthyName)) {
// This branch runs.
}
使用 with 语句和代理创建动态命名空间
¥Creating dynamic namespaces using the with statement and a proxy
with
将把每个变量查找转换为属性查找,而 代理 允许捕获每个属性查找调用。你可以通过组合它们来创建动态命名空间。
¥with
will transform every variable lookup to a property lookup, while Proxies allow trapping every property lookup call. You can create a dynamic namespace by combining them.
const namespace = new Proxy(
{},
{
has(target, key) {
// Avoid trapping global properties like `console`
if (key in globalThis) {
return false;
}
// Trap all property lookups
return true;
},
get(target, key) {
return key;
},
},
);
with (namespace) {
console.log(a, b, c); // "a" "b" "c"
}
规范
Specification |
---|
ECMAScript Language Specification # sec-with-statement |
浏览器兼容性
BCD tables only load in the browser