《你不知道的JavaScript - 上》之作用域

99 阅读5分钟

文章内容全部来自《你不知道的JavaScript - 上》,记录学习呀呀呀!!!

汇总: 作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。

编译原理

尽管通常将JavaScript归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。

解释执行:借助解释器将我们的代码一句一句解释成计算机可以识别的机器语言然后执行,每次运行都需要解释器解释我们的代码去执行!有特殊的计算能力!

编译执行:借助编译器将我们的代码编译成计算机可识别机器语言并保存起来,之后只需要运行编译好的代码就行!编译一次多次运行!有确定的运算性能!

在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。

  • 分词/词法分析
    • 这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元。例如,考虑程序var a = 2;。这段程序通常会被分解成为下面这些词法单元:var、a、=、2 、;。空格是否会被当作词法单元,取决于空格在这门语言中是否具有意义。
  • 解析/语法解析
    • 在解析阶段,编译器会将源代码分解成一个个词法单元,然后根据语言的语法规则,将这些词法单元转换为语法树的节点,最终生成抽象语法树
  • 代码生成
    • 将AST转换为可执行代码的过程。

对于JavaScript来说,大部分编译发生在代码执行前的几微秒(甚至更短!)的时间内。

理解作用域

预备演员:

  • 引擎:从头到尾负责整个JavaScript程序的编辑及执行过程。
  • 编译器:引擎的好朋友之一,负责语法分析及代码生成等脏活累活(前面分析的三个步骤)。
  • 作用域:引擎的另一位好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

var a = 2;进行分析:

在编译阶段要求声明变量,是为了让 JavaScript 引擎在执行阶段能够正确地查找变量的值。在执行阶段,引擎会按照作用域的规则查找变量的值,这并不与编译阶段要求声明变量矛盾。

var a = 2;
引擎:先拿给编译器这个兄弟进行处理吧,处理好之后在交给我吧。
编译器:来活了,来了个var a,先去咨询一下作用域看看它有没有。
作用域:没有啦,走开!
编译器:(变量的声明操作)没有,那你先给我声明一个名字叫a的变量。
作用域:var a,已经好了,拿去。(注意:此阶段只是进行了变量的声明)。
引擎:(赋值操作a=2)先去问问当前作用域老兄有没有?
作用域:巧了,有这个变量。
引擎:这回这活省事,将2赋值给a。
(假如没有持续向上找,直到最后抛出异常)

RHS与LHS查询

LHS(Left-Hand Side)查询是指当变量出现在赋值操作的左侧时进行的查询。例如,a = 2 中的 a 就是一个 LHS 查询。LHS 查询的目的是为了找到变量的容器,以便对其进行赋值操作。

RHS(Right-Hand Side)查询是指当变量出现在赋值操作的右侧时进行的查询。例如,console.log(a) 中的 a 就是一个 RHS 查询。RHS 查询的目的是为了获取变量的值,以便进行后续的操作。

分析下面代码的LHS与RHS查询

function foo(a) { 
    var b = a; 
    return a + b; 
} 
var c = foo(2);
1foo(2)执行 =》 RHS
2、a形参声明 =》 LHS
3、获取a的值 =》 RHS
4、b变量 =》 LRH
5、a + b 分别获取a、b的值 =》 2RHS
6、将得到的结果找到容器c装起来 =》 LHS

非严格模式下,RHS在所有的作用域都找不到时,抛出ReferenceError异常;LHS查询时,如果在顶层作用域还是无法查找到变量,就会创建一个具有该名称的变量;

严格模式下,LHS查询失败时,并不会创建并返回一个变量,会抛出ReferenceError异常。

当RHS查找找到一个变量,但是你尝试对这个变量的值进行不合理操作,比如2(),就会抛出TypeError错误。

ReferenceError同作用域判别失败相关,而TypeError则代表作用域判别成功了,但是对结果的操作是非法或不合理的。

作用域嵌套

作用域嵌套如下图楼层一样,第一层代表当前的执行作用域,建筑的顶层代表全局作用域。

image.png

LHS和RHS都会在当前楼层进行查找,如果没有找到,就会坐电梯前往上一层楼,如果还是没有找到就继续向上,以此类推。一旦抵达顶层(全局作用域),可能找到了你所需的变量,也可能没找到,但无论如何查找过程都将停止。