作用域是什么

  • 存储变量的规则
    存储在哪里?怎么获取?

var a = 2

为一个变量分配内存,名字为a,将值为2保存进这个变量(不完全正确)

  1. var a 编译器会询问作用域是否已经存在这样一个变量存在于同一个作用域内,如果有编译器会忽略该声明,继续编译,如果没有,他会要求作用域在当前作用域内声明一个新的变量,并命名为a。
  2. 接下载编译器会为引擎生成运行时所需要的代码,这些代码被用来处理a = 2 的操作,引擎运行时会询问作用域当前作用域集合中是否存在叫做a的变量,如果有,就是用该变量,如果没有会再向再外层的作用域询问,最终会到全局作用域。找到后会将2赋值给它,找不到就会抛出异常

总结:变量的赋值操作会执行两个动作,编译器在作用域中声明,运行时引擎会查找该变量并进行赋值。

编译器:LHS查询 查找类型RHS(LHS,RHS),当变量出现在赋值操作的左侧时进行进行LHS查询,出现在右侧时进行RHS查询。

console.log(a) 执行的是RHS,要去找a的值;

a = 2 执行的是LHS 要为=2找到赋值的目标。

LHS:赋值操作的目标是谁;目的是对变量进行赋值

RHS:谁是赋值操作的源头;目的是获取变量的值

ReferenceError 这个错误是找不到定义的变量

词法作用域

作用域有两种主要的工作模型:词法作用域,动态作用域。

词法作用域:定义在词法阶段的作用域,就是由你写代码时将变量和块作用域写在哪里决定的,因此词法解析器处理代码时会保持作用域不变(大部分情况下是这样)

词法欺骗:

eval( )

function foo(str, a) {
    eval(str); // 欺骗
    console.log(a, b);
}
var b = 2;

foo('val b = 3;', 1); // 1, 3

with(){}

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

var o1 = {
    a: 3
}

var o2 = {
    a: 3
}

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

foo(o2);
console.log(o2.a) // undefined
console.log(a) // 2  ==a被泄漏到全局上面去了==

总结
eval()函数如果接受了一个或多个声明的代码,就会修改其所处的词法作用域;
with声明实际上是根据传递给他的对象凭空创建了一个全新的词法作用域

eval,with会降低性能,所以不建议使用