with
已弃用:此特性不再推荐。虽然某些浏览器可能仍然支持它,但它可能已经从相关的网络标准中删除,可能正在删除过程中,或者可能仅为兼容性目的而保留。请避免使用它,如果可能,请更新现有代码;请参阅本页底部的兼容性表格以指导您的决策。请注意,此特性可能随时停止工作。
注意:不推荐使用 with 语句,因为它可能会导致令人困惑的错误和兼容性问题,使得优化无法进行,并且在严格模式下是被禁止的。推荐的替代方法是将要访问其属性的对象赋值给一个临时变量。
with 语句为一条语句扩展了作用域链。
语法
with (expression)
  statement
描述
有两种类型的标识符:限定标识符和非限定标识符。非限定标识符是指不指示其来源的标识符。
foo; // unqualified identifier
foo.bar; // bar is a qualified identifier
通常,非限定标识符通过在作用域链中搜索具有该名称的变量来解析,而限定标识符通过在对象的原型链中搜索具有该名称的属性来解析。
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
一个例外是全局对象,它位于作用域链的顶部,其属性自动成为全局变量,无需限定符即可引用。
console.log(globalThis.Math === Math); // true
在评估其语句体期间,with 语句将给定对象添加到作用域链的头部。每个非限定名称将首先在对象内搜索(通过 in 检查),然后才在上层作用域链中搜索。
请注意,如果非限定引用指向对象的方法,则该方法会以对象作为其 this 值进行调用。
with ([1, 2, 3]) {
  console.log(toString()); // 1,2,3
}
对象可能具有 [Symbol.unscopables] 属性,该属性定义了不应添加到作用域链中的属性列表(用于向后兼容)。有关更多信息,请参阅 Symbol.unscopables 文档。
使用 with 语句的原因包括节省一个临时变量并通过避免重复冗长的对象引用来减小文件大小。然而,with 语句不可取的原因远不止这些。
- 
性能: with语句强制首先搜索指定对象以进行所有名称查找。因此,所有不是指定对象成员的标识符在with块中会更慢地找到。此外,优化器无法对每个非限定标识符所指的内容做出任何假设,因此每次使用标识符时都必须重复相同的属性查找。
- 
可读性: with语句使得人类读者或 JavaScript 编译器很难判断非限定名称是否会在作用域链中找到,如果找到,会在哪个对象中找到。例如:jsfunction f(x, o) { with (o) { console.log(x); } }如果你只看 f的定义,就无法判断with主体中的x指的是什么。只有当f被调用时,才能确定x是o.x还是f的第一个形式参数。如果你忘记在作为第二个参数传递的对象中定义x,你不会得到错误——相反,你会得到意想不到的结果。这种代码的实际意图也尚不明确。
- 
前向兼容性:使用 with的代码可能不具备前向兼容性,特别是当它与非普通对象一起使用时,这些对象将来可能会获得更多属性。考虑以下示例:jsfunction f(foo, values) { with (foo) { console.log(values); } }如果你在 ECMAScript 5 环境中调用 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 语句
以下 with 语句指定 Math 对象为默认对象。with 语句后面的语句引用 PI 属性以及 cos 和 sin 方法,而无需指定对象。JavaScript 为这些引用假定 Math 对象。
let a, x, y;
const r = 10;
with (Math) {
  a = PI * r * r;
  x = r * cos(PI);
  y = r * sin(PI / 2);
}
通过解构属性到当前作用域来避免使用 with 语句
你通常可以通过属性解构来避免使用 with。这里我们创建了一个额外的块来模拟 with 创建额外作用域的行为——但在实际使用中,这个块通常可以省略。
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 语句
如果你正在生成一个必须多次重用长命名引用的表达式,并且你的目标是在表达式中消除该冗长的名称,你可以将表达式包装在 IIFE 中,并将长名称作为参数提供。
const objectHavingAnEspeciallyLengthyName = { foo: true, bar: false };
if (((o) => o.foo && !o.bar)(objectHavingAnEspeciallyLengthyName)) {
  // This branch runs.
}
使用 with 语句和代理创建动态命名空间
with 会将每个变量查找转换为属性查找,而 代理(Proxies)允许捕获每个属性查找调用。你可以通过结合它们来创建动态命名空间。
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"
}
规范
| 规范 | 
|---|
| ECMAScript® 2026 语言规范 # sec-with-statement | 
浏览器兼容性
加载中…