javascript的作用域是一个老生常谈的问题了。高程啊,或是各种书籍上都有关于javascript的作用域的阐述。这一次看了,《你不知道的JavaScript》里关于作用域的描述,感觉挺不错的。
编译
JavaScript是脚本语言,解释型语言,很多人可能认为它要跟其他的编译型语言划分界限,毫不相干。但是JavaScript确实是有“编译”的。不过与一众编译型语言不同,JavaScript进行的是预编译,这个编译发生在代码执行前的几微妙。
跟作用域相关的角色
主要有三:
- 引擎:从头到尾负责整个JavaScript程序的编译和执行过程。
- 编译器:负责语法分析,代码生成。
- 作用域:负责收集并维护由所有声明的标识符组成的一系列查询,并实施一套非常严格的规则,确定当前执行代码对这些标识符的访问权限。
看个例子
var a = 2;
大家常常会把这句语句的执行过程想成这样:为一个变量分配内存,并命名为a,然后将2这个值保存进这个变量。
然后,这并不完成正确。
实际情况是比较复杂的。大概的过程如下:
- 对于var a,编译器询问作用域是否已经有一个名为a的变量存在于当前作用域中,如果有,这个声明会被忽略。如果没有,就会在当前作用域的集合中声明一个新的变量,命名为a。
- 对于a = 2部分,引擎在运行时会询问当前作用域,是否有这个名为a的变量,如果有,引擎使用这个变量。如果没有,向上一级作用域寻找。
声明发生在编译,赋值则是运行时候的事情了。
LHS和RHS
LHS和RHS是引擎对变量的两种查询手段。简单来说,L和R分别代表左侧和右侧。再简单来说,左侧就是指如果要对一个变量进行赋值,那么我们要知道的是是否有这个变量,也就是找到操作的目标,我们不关心它的值。右侧是指我们要取得某个变量的指时。
用个例子说明:
function foo(a){var b = a;return a + b;
}
var c = foo(2);
LHS查询:
- 第2行,var b
- 第5行,var c
- 第5行,foo(2)里的2,
RHS查询: - 第2行,第2行,b = a
- 第3行, a + b 里的a
- 第3行, a + b 里的b
- 第5行中对于函数的调用。
作用域嵌套
LHS和RHS查询都会再当前执行作用域中开始,如果有需要(也就是说它们没有找到所需的标识符),就会向上一级作用域继续查询目标标识符。要是一直都找不到就会查询到全局作用域为止。