当我们看到语句

var a = 0;

时,一般都认为是声明一个变量。但引擎认为这是两个声明,一个由编译器在编译时处理,一个在引擎运行时进行处理。引擎是怎样处理的呢?

假设以下是存在两个作用域,里面是我们要用到的,外面是其他不使用的作用域,如图:

zuoyongyu01

引擎解析过程大致是:

  1. 编译器分词var a; a = 0;
  2. 处理var a,编译器查询“使用的作用域”中是否存在a,已存在跳过/未存在在作用域中声明a;
  3. 处理a = 0,引擎查询“使用的作用域”中是否存在a,存在将a赋值为0/如果不存在继续向“其他作用域”中查询,如果“其他作用域”中存在将a赋值为0,如果最终未找到a,则引擎抛出异常。

详细解释:

引擎会为语句进行LHS和RHS(非LHS)查询,L表示赋值左侧,R表示赋值右侧(变量在左侧进行LHS查询,在右侧进行RHS查询);RHS 查找变量并取得相应的值(下面详细解释),而LHS(a = 0)则是查找变量的容器,进行赋值!

解释下“RHS(非LHS)”是什么意思,看下面的语句:

console.log(a);

这里实际也是RHS查询,因为是非LHS,所以console.log 可以取到a的值!相对如下

a = 0

LHS查询并不关心当前a的值是什么,实质是只为 ” = 0″找到一个目标即可;

再说下作用域,实际开发过程作用域会产生层级嵌套,如图

zuoyongyu01

如果引擎需要查找某个声明,会先在当前作用域中进行查询,如果找到该声明即停止;如未找到引擎会向上级作用域继续查找,直到最外层作用域(全局作用域 ),然后停止。

为什么要区分LHS和RHS呢?来看下代码:

function foo(a){
	console.log(a + b);
	b = a;
}
foo(2);

引擎需要对b进行RHS查询,查询完全部的嵌套作用域后仍未找到b的声明,这时引擎会抛出错误ReferenceError。相对的,如果是LHS查询(非严格模式下),在未找到声明的情况下会在全局作用域中创建该声明后返回给引擎。

如果RHS查询到声明,对该声明进行不合理的操作,如调用一个非函数,或者引用null或undefined,这时引擎会抛出TypeError的错误。

ReferenceError是在作用域中无法找到声明,而TypeError则是在作用域中存在,但是操作非法或者不合理。