• 引擎
从头到尾负责整个 JavaScript 程序的编译及执行过程。
• 编译器
引擎的好朋友之一,负责语法分析及代码生成等脏活累活(详见前一节的内容)。
• 作用域
引擎的另一位好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查
询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
var a = 2;
当你看见 var a = 2; 这段程序时,很可能认为这是一句声明。事实上,引擎认为这里有两个完全不同的声明,一个由编译器在编译时处理,另一个则由引擎在运行时处理。
下面我们将 var a = 2; 分解,看看引擎和它的朋友们是如何协同工作的。
- 编译器首先会将这段程序分解成词法单元,然后将词法单元解析成一个树结构。但是当编译器开始进行代码生成时,它对这段程序的处理方式会和预期的有所不同。
- 可以合理地假设编译器所产生的代码能够用下面的伪代码进行概括:“为一个变量分配内存,将其命名为a,然后将值2保存进这个变量。”然而,这并不完全正确。
- 事实上编译器会进行如下处理(编译器的两步动作)。
- 第一步. 遇到 var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作
用域的集合中声明一个新的变量,并命名为 a。 - 第二步. 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理 a = 2 这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作 a 的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。如果引擎最终找到了 a 变量,就会将 2 赋值给它。否则引擎就会举手示意并抛出一个异常!
- 总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。