深入解析 JavaScript 中 with 关键字的原理与风险

作者:安全边界

时间:2026-10-29

浏览:372

本文深入解析 JavaScript 中的 with 关键字,阐述其通过改变作用域链简化对象属性访问的基本用法。重点分析为何官方强烈不推荐使用:包括导致 JS 引擎无法进行静态分析和代码优化引发的严重性能下降,以及因作用域动态绑定造成的语义歧义和潜在 Bug。文章通过具体代码示例说明变量提升、this 指向及严格模式限制,帮助开发者理解底层机制并避免在实际开发中使用该关键字。

文章目录

JavaScript with 关键字概述

提到 JavaScript 中的 with 关键字,许多开发者的第一反应是它用于修改作用域,且核心结论是不推荐使用。听到这一建议后,不少人便直接忽略 with,认为不碰它即可。然而,在阅读现有代码或应对技术面试时,with 相关的问题仍可能出现,其中包含诸多未接触过的陷阱。因此,有必要对 with 关键字进行详细梳理。

一、基本说明

with 关键字的基本语法与用法

根据《JavaScript 高级程序设计》的描述,with 语句的作用是将代码的作用域设置到一个特定的作用域中。其基本语法结构如下:


with (expression) statement;

使用 with 关键字的主要目的是简化多次访问同一对象属性的操作。例如,在以下场景中:


var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

上述代码均涉及访问 location 对象的属性。若使用 with 关键字,可简化为以下写法:


with (location){
  var qs = search.substring(1);
  var hostName = hostname;
  var url = href;
}

在此代码中,with 语句关联了 location 对象。这意味着在 with 代码块内部,每个变量首先被视为局部变量;若局部变量与 location 对象的某个属性同名,则该局部变量将指向该属性。 注意:在严格模式下禁止使用 with 语句。

with 关键字的主要弊端

二、with 关键字的弊端

从基本说明中可见,with 的作用之一是简化代码。然而,为何官方不推荐使用?下面将探讨 with 的缺点:

1、性能问题 2、语义不明,调试困难

with 导致的性能问题分析

三、性能问题

首先讨论性能问题。关于使用 with 关键字导致的性能损耗,我们先对比两段代码:

第一段代码未使用 with 关键字:


function func() {
  console.time("func");
  var obj = {
    a: [1, 2, 3]
  };
  for (var i = 0; i < 100000; i++) {
    var v = obj.a[0];
  }
  console.timeEnd("func");//0.847ms
}
func();

第二段代码使用了 with 关键字:


function funcWith() {
  console.time("funcWith");
  var obj = {
    a: [1, 2, 3]
  };
  var obj2 = { x: 2 };
  with (obj2) {
    console.log(x);
    for (var i = 0; i < 100000; i++) {
      var v = obj.a[0];
    }
  }
  console.timeEnd("funcWith");//84.808ms
}
funcWith();

使用 with 关键字后,代码性能显著降低。第二段代码的 with 语句作用于 obj2 对象,但 with 块内部访问的却是 obj 对象。有一种观点认为:使用 with 后,在 with 块内访问变量时,JS 引擎首先会在 obj2 上查找名为 obj 的属性,若未找到,再进行后续查找,这一过程导致性能降低。但程序性能真正降低的原因是否如此? 我们修改第二段代码,如下所示:


function funcWith() {
  console.time("funcWith");
  var obj = {
    a: [1, 2, 3]
  };
  with (obj) {
    for (var i = 0; i < 100000; i++) {
      var v = a[0];
    }
  }
  console.timeEnd("funcWith");//88.260ms
}
funcWith();

这段代码将 with 语句作用于 obj 对象,并直接使用 a 访问 obj 的 a 属性。按照前述观点,访问 a 属性时应能在 obj 上一次性找到,为何代码性能依然降低? 真正原因是:使用 with 关键字后,JS 引擎无法对这段代码进行优化。 JS 引擎在代码执行前存在编译阶段。不使用 with 时,JS 引擎知道 a 是 obj 上的属性,可通过静态分析代码来增强标识符解析,从而优化代码,提高执行效率。使用 with 后,JS 引擎无法分辨 a 变量是局部变量还是 obj 的一个属性,因此遇到 with 关键字时,引擎会放弃对该段代码的优化,导致执行效率降低。 使用 with 对性能的影响还体现在 JS 压缩工具上,它无法对包含 with 的代码进行有效压缩,这也是影响性能的因素之一。

语义歧义与调试困难

四、语义不明,难以调试

除性能问题外,with 还存在语义不明、难以调试的缺点,即造成代码不易阅读,且可能引发潜在 Bug。


function foo(obj) {
  with (obj) {
    a = 2;
  }
}

var o1 = {
  a: 3
};
var o2 = {
  b: 3
};

foo(o1);
console.log(o1.a); // 2

foo(o2);
console.log( o2.a ); // undefined
console.log( a ); // 2

这段代码易于理解:在 foo 函数内,使用 with 关键字访问传入的 obj 对象,并修改 a 属性。当传入 o1 对象时,因 o1 存在 a 属性,操作正常。传入 o2 对象时,修改 a 属性时,由于 o2 没有 a 属性,被修改的 a 属性则变成了全局变量。这就造成了潜在 Bug。

复杂场景下的作用域与变量提升分析

五、延伸分析

前文已阐述不推荐使用 with 的原因及潜在问题。下面来看一些更复杂的情况,参考以下代码:


var obj = {
  x: 10,
  foo: function () {
    with (this) {
      var x = 20;
      var y = 30;
      console.log(y);//30
    }
  }
};
obj.foo();
console.log(obj.x);//20
console.log(obj.y);//undefined

这段代码分别输出 30、20、undefined。涉及的知识点较多:with 关键字、this 关键字、变量提升等,我们逐一解释。 1、this 关键字 关于 this 关键字的文章在网络上众多,此处不再赘述。只需记住一点:this 关键字始终指向调用函数的对象。在此处,foo 函数中,this 指向的就是 obj 对象。因此在 with(this) 语句块内,可直接通过 x 变量访问 obj 的 x 属性。 2、变量提升 JS 中的变量提升也是常见问题,可简单理解为:在 JS 中,变量声明会被提升到函数顶部,尽管有时它是在后面声明的。

因此,上述代码可解析为:


var obj = {
  x: 10,
  foo: function () {
    var x;//声明局部变量x
    var y;//声明局部变量y
    with (obj) {
      x = 20;//访问变量x,在obj上找到x,则修改为20
      y = 30;//访问变量y,在bojg上找不到y,则进一步查找,找到局部变量y,修改为30
      console.log(y);//30//直接输出局部变量y,
    }
  }
};
obj.foo();
console.log(obj.x);//20,obj.x已被修改为20
console.log(obj.y);//undefined,obj不存在y属性,则为undefined

上述注释解释了代码的执行过程,相信读者已理解为何输出 30、20、undefined。

有兴趣的读者可参考以下代码:


({
x: 10,
foo: function () {
  function bar() {
    console.log(x);
    console.log(y);
    console.log(this.x);
  }
  with (this) {
    var x = 20;
    var y = 30;
    bar.call(this);
  }
}
}).foo();

这段代码会输出什么?为什么呢?

总结与建议

总结

本文总结了 with 语句的特点和弊端,总体而言,强烈不推荐使用 with 关键字。在日常编码中,只需知道不使用 with 即可;但有时可能会遇到关于 with 的复杂问题,想要找出真正原因,需深入理解 with 关键字。这有助于深入学习 JS 这门语言,同时也是学习 JS 的一个乐趣。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章