With

本质上是通过将一个对象的引用当做作用于来处理,将对象的属性当做作用域中的标识符来处理,从而创建一个新的词法作用域。

首先,with是JS中欺骗词法的方法之一,另一种方法是eval()

  1. with通常被当做重复引用同一对象中的多个属性的快捷方式,可以不需要重复引用对象本身
1
2
3
4
5
6
7
8
9
10
11
12
13
let obj = {
a : 3,
b : 4,
d : 7
}
with (obj) {
a = 8,
b = 9,
d = 10
}
// 使用with就相当于直接使用obj.a=8、obj.b=9、obj.d=10
console.log(obj);
// a=8, b=9, d=10
  1. 看如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo (obj) {
with (obj) {
a = 2
}
}
let o1 = {
a : 7
}
let o2 = {
b : 7
}
foo(o1)
console.log(o1.a); // 2
foo(o2)
console.log(o2.a); // undefined
console.log(a); // a

为什么会出现这种情况呢??

对于o1来说,其本身就有一个a属性,使用with关键字后,可以理解为with关键字的作用域就是o1,o1本身就有一个a属性,再对其进行了LHS引用,使a的值改变,这是显而易见的

对于o2来说,其本身没有一个a属性,使用with关键字后,with关键字的作用域就变成了o2,但是o2中没有a属性,所以o2.a就被赋值为了undefined,然后使用的LHS引用,从他的上一级作用域去去查找a,直到找到全局作用域,如果还没有找到a,就会在全局创建一个a并赋值为2(当然这是在非严格模式下的情况)

所以with会在运行时修改或创建一个新的作用域,以此来欺骗其他在书写时定义的词法作用域。

在严格模式下使用with会直接报错,严格模式下with是被禁止使用的

  1. 虽然with在某些方面极为方便,但是非常不推荐使用with关键字
    • 会影响JS引擎在编译阶段的性能优化,如果代码中由大量的with,那么运行起来会非常慢
    • 严格模式下无法使用