一、作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。赋值操作符会导致 LHS 查询。=操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。
LHS和RHS: “L”和”S”分别代表的是一个赋值的左侧和右侧,当变量出现在赋值操作的左侧时进行 LHS 查询,出现在右侧时进行 RHS 查询。简单一点说RHS就是查找某个变量的值,而LHS就是找到变量属于哪一个父容器,从而可以对其赋值。
二、JS引擎首先会在代码执行之前对其进行编译(一般为几微秒,甚至更短)
在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”:
(1)分词/词法分析
这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)
(2)解析/语法分析
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树,这个树被称为“抽象语法树”(AST)。
(3)代码生成
将 AST 转换为可执行代码的过程称被称为代码生成。这个过程与语言、平台等等因素相关。
以 var example = 1 声明为例,它会被分解为两个独立的步骤:
- 首先,var example在其作用域中声明新变量。这会在最开始的阶段,也就是代码执行前进行。
- 接下来,example= 1 会查询(LHS 查询)变量 example 并对其进行赋值。
LHS 和 RHS 查询都会在当前执行作用域中开始,如果说它们没有找到所需的标识符,就会向上级作用域继续查找目标标识符,这样每次上升一级作用域(从底开始),最后抵达全局作用域(顶部),无论找到或没找到都将停止。
不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛出 ReferenceError 异常(严格模式下)。
代码1:
function f1(a) {
console.log( a + b );
b = a;
}
f1( 2 ); //ReferenceError 异常
第一次对 b 进行 RHS 查询时是无法找到该变量的。因为 b 是一个“未声明”的变量,在任何相关的作用域中都无法找到它,所以抛出ReferenceError异常
代码2:
// 宽松模式下
function f2(a){
b = a;
console.log(b);
}
f2(1); // 1
当JS引擎执行 LHS 查询时,如果在全局作用域中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎。
但是在“严格模式”中是禁止自动或者隐式的创建局部变量,所以上面的代码如果书写在严格模式下也将会抛出ReferenceError异常
另外还要提到一种错误:TypeError
它和ReferenceError的不同就是ReferenceError 是作用域判别失败了,而TypeError代表作用于判别成功了,但是对结果的操作是不合理的。
例如你要对一个非函数类型的值进行函数调用,或者是想引用null类型的值当中的属性等等。