你不知道JavaScript--作用域、LHS查询和RHS查询
1、编译原理
Javascript程序中的一段代码在执行之前经历三个步骤,统称为“编译”。
(1)分词/词法分析
(2)解析/语法分析
(3)代码生成
2、LHS查询和RHS查询
LHS和RHS的含义是“赋值操作的左侧或右侧”并不一定意味着就是“ =赋值操作符的左侧或右侧”。
当变量出现在赋值操作的左侧时进行LHS查询,出现在赋值操作的右侧时进行RHS查询
RHS查询与简单地查找某个变量的值别无二致,而LHS查询则是试图找到变量的容器本身,从而可以对其赋值。
如果查找目的是对变量进行赋值,那么就使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。
考虑以下代码:
console.log(a)
其中对a的引用是一个RHS引用,因为这里a并没有赋予任何值。相应的,需要查找并取得a的值,这样才能将值传给console.log(…).
相比之下,例如:
a=2
这里对 a 的引用则是 LHS 引用,因为实际上我们并不关心当前的值是什么,只是想要为 =2 这个赋值操作找到一个目标。
考虑下面的程序,其中既有 LHS 也有 RHS 引用:
function foo(a){
console.log(a);//2
}
foo(2);
最后一行 foo(..) 函数的调用需要对 foo 进行 RHS 引用,意味着“去找到 foo 的值,并把它给我”。并且 (..) 意味着 foo 的值需要被执行,因此它最好真的是一个函数类型的值。
代码中隐式的 a=2 操作可能很容易被忽略掉。这个操作发生在 2 被当作参数传递给foo(..) 函数时, 2 会被分配给参数 a 。为了给参数 a (隐式地)分配值,需要进行一次LHS 查询。
这里还有对 a 进行的 RHS 引用,并且将得到的值传给了 console.log(..) 。 console.log(..) 本身也需要一个引用才能执行,因此会对 console 对象进行 RHS 查询,并且检查得到的值中是否有一个叫作 log 的方法。
3、作用域嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。遍历嵌套作用域链的规则很简单:引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。
4、 异常
因为在变量还没有声明(在任何作用域中都无法找到该变量)的情况下,这两种查询的行为是不一样的。
考虑如下代码:
function foo(a) {
console.log( a + b );
b = a;
}
foo( 2 );
报错:Uncaught ReferenceError: b is not defined
‘b=a’不是变量声明,预编译过程中不会将其提前
第一次对b进行RHS查询时是无法找到改变量,也就是说,这是一个“未声明”的变量,因为在任何相关的作用域中都无法找到它。
如果 RHS 查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError异常。值得注意的是, ReferenceError 是非常重要的异常类型。