with 是什么?

179 阅读3分钟

前言

with 语句 扩展一个语句的作用域链。

JavaScript 查找某个未使用命名空间的变量时,会通过作用域链来查找,作用域链是跟执行代码的 context 或者包含这个变量的函数有关。with语句将某个对象添加到作用域链的顶部.

如果在 statement 中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果沒有同名的属性,则将拋出 ReferenceError 异常。

var obj = {
  a: 123,
  b: 321,
};
// 便捷写法
with (obj) {
  var c = c; // ReferenceError
}

利与弊

那位微不足道的利

这里 with 括号中的值就是我们的公共对象,下面就是每个对象中的值

var a = obj.a;
var b = obj.b;
var c = obj.c;
// 便捷写法
with (obj) {
  var a = a;
  var b = b;
  var c = c;
}
var qs1 = location.search.substring(1);
var hostname1 = location.hostname;
var url1 = location.href;
// 上面几行代码都包含了location对象,可使用with语句简写为以下形式
with (location) {
  var qs2 = search.substring(1);
  var hostname2 = hostname;
  var url2 = href;
}

这样无疑会大大提高我们的效率(性能上却是一种丢失)

弊弊弊

  1. 意外创建全局变量

    with (obj) {
      a = 2;
    }
    console.log(obj.a); // 输出 1
    console.log(a); // 输出 2
    

    这里的“输出 1”和“输出 2”的结果要看 obj 是否带有 a 这个属性。如果 obj 带有属性 a,那么会预期将 obj 的 a 属性赋予 2 的值,“输出 1”的结果为 2,“输出 2”会报错 a is not defined。如果 obj 不带有属性 a,我们可能会认为产生这样的效果 obj.a = 2,obj 将赋予属性 a 且 a 的值等于 2,但事实上并不会,而是会在全局作用域里创建一个全局变量 a,a 的值等于 2,也就是说,“输出 1”的结果为 undefined,“输出 2”的结果为 2。这种特性会意外地创建全局变量,从而污染全局作用域

  2. 语义不明

    当存在像以下这样的代码,如果我们不知道 b 里面有什么属性和方法,我们怎么知道 a 是 b 里的 a,还是 function 的入参 a 呢?

    function f(a, b) {
      with (b) {
        console.log(a);
      }
    }
    
  3. 性能丢失

    with (document.forms[0]) {
      name.value = "lee king";
      address.value = "Peking";
      zipcode.value = "10000";
    }
    
    // 可以看出 with 语句的简洁明了,不过在代码的世界里是很难找到真正的完美。
    document.forms[0].name.value = "lee king";
    document.forms[0].address.value = "Peking";
    document.forms[0].zipcode.value = "10000";
    

    js 的解释器需要检查 with 块中的变量是否属于 with 包含的对象,没有的话作用域要关联到全局,这将使 with 语句执行速度大大下降,并且导致 js 语句很难被优化。所以在以后的高效代码开发中我们应该尽可能的避免使用 with 语句。

最后

with 语句就是一个没卵用的语句,w3school 和 MDN 都明确不推荐使用。在 js 的严格模式下,with 语句甚至是完全禁用的。