You don't know JS 读书笔记之零 --- 作用域

2017/3/21 posted in  JavaScript 前端

今日图书馆挖到一本好书,这里和大家分享一下我读这本书的心得体会.
这里是第一章的笔记,讲的主要是关于作用域的.

1.1 JS的编译原理

在理解作用域之前我们先来了解一下JS的编译原理.
在传统编译语言中,程序在执行之前会经历三个步骤,统称为"编译":
1、分词/词法分析(Tokenizing/Lexing):

 字符串=分解=>代码块即词法单元(token)
 如var a = 2; =分解=> var、a、=、2、;

2、 解析/语法分析(Parsing)


词法单元流(数组) => 元素逐级嵌套代表程序语法结构的树即"抽象语法树"
    如 var a = 2;
                  +-----------+
                  | var a = 2;|
                  +-----+-----+
                        |
                        |
               +--------+----------+
               |VariableDeclaration|
               +------+------+-----+
                      |      |
+------------+        |      |     +---------------------+
| Identifier +--------+      +---->+ AssignmentExpression|
+----+-------+                     +-----------------+---+
     |                                               |
     v             +------+      +----------------+  |
+----+---+         |  2   +<-----+ NumericLiteral <--+
|   a    |         +------+      +----------------+
+--------+

3、代码生成

将AST转换为可执行代码
简单来说就是有某种方法可以将var a = 2;的AST转化为一组机器指令,用来创建一个叫a的变量,并将一个值存储在a中.

比起那些编译过程只有三个步骤的语言的编译器来说,javascript 引擎要复杂的多。例如,在词法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。

总的来说,任何JS代码片段在执行前都要进行编译(预编译)。因此,JS编译器首先会对 var a = 4; 这段程序进行编译,然后做好执行的准备,并且通常马上就会执行它。

1.2 理解作用域

在理解作用域时将模拟几个人物之间的对话的形式.

  • 引擎: 负责整个JS程序编译和执行过程
  • 编译器: 负责语法分析,生成代码
  • 作用域: 负责收集、维护油所有声明的标识符(变量)组成的一系列查询,并严格实施一套规则,确定当前执行代码对这些标识符的访问权限.

下面用var a = 4;作为例子看看他们的工作和对话.

一、 编译器: var a = 4; =分解=> 词法单元 =解析=> 树结构

  1. 遇到var a,编译器问作用域:"有没有一个a变量存在同一个作用域里面啊?"如果有则忽略声明继续编译;否则要求作用域在当前作用域集合中声明一个新变量叫a
  2. 然后编译器要为引擎生成运行所需的代码.引擎运行时,首先会问作用域:"当前有没有一个变量叫a啊?",如果有就使用这个变量,如果没有,引擎会继续寻找这个变量.

引擎查找变量有两种类型:LHSRHS
通俗点说就是:"复制操作是谁(LHS)"、"谁是赋值的源头(RHS)"

引擎与作用域的对话

function foo(a){
    console.log(a); //2
}
foo(2);

把上面的代码的执行模拟成引擎和作用域的对话的话将会是这样的:

引擎:"我需要foo的RHS引用"
作用域:"刚刚编译器声明了它,它是一个函数"
引擎:"作用域我还需要a的LHS引用"
作用域:"编译器把他作为foo的形参了"
引擎:"我现在把2赋值给a,继续执行.我还需要console的RHS引用(隐式a = 2)"
作用域:"它是一个内置对象."
引擎:"我去看看它里面有没有log(),作用域,帮我确认一下a的RHS引用"
作用域:"a没有改变"
......

作用域嵌套

当一个块或函数嵌套在另一个块或者函数中时,就会发生作用域嵌套.当引擎在当前作用域无法找到某个变量时,就会向外层嵌套的作用域中继续查找,直到最外层的作用域(全局变量).

小结

  • 作用域是一套规则,用于确定在何处及如何查找变量(标识符).对变量赋值=>LHS,获取变量的值=>RHS.
  • LHS和RHS查询都会从当前作用域开始,若找不到会向上级查找,直到找到或抵达全局变量.
  • 不成功RHS => ReferenceError异常
  • 不成功LHS => 隐式创建全局变量(非严格模式),该变量被LHS=>ReferenceError异常(严格模式)

#EOF#