引擎:大哥 负责从始至终的编译和执行我们的 JavaScript 程序。
编译器:小弟 处理所有的解析和代码生成的重活儿。
作用域:小弟 收集并维护一张所有被声明的标识符(变量)的列表,并对当前执行中的代码如何访问这些变量强制实施一组严格的规则。
以 var a = 1;
为例子
一、在一段js代码被引擎大哥执行的前一刻,引擎大哥会让小弟编译器进行一个编译(编译器编译的三个步骤):
第一步:分词/词法分析——将var a = 1;
分开成对语言本身有意义的片段var
a
=
1
;
;
第二部:解析——将这个有意义的片段var
a
=
1
;
,解析成一个抽象语法树,它综合地表示了程序的语法结构;
第三步:代码生成——首先在抽象语法树里发现var a
,并去作用域里查看是否存在a
这个变量,如果存在 则跳过,否则就会让作用域声明个名字为a
的变量。然后生成稍后要执行的代码,来处理a = 1
。
二、引擎大哥运行到代码var a = 1;
时,首先让 小弟作用域 去查看在当前的作用域集合中是否有一个称为 a
的变量可以访问。如果有,引擎就使用这个变量进行赋值操作a = 1
。如果没有,引擎就会到其他地方查看,如果最终没有找到a
变量,引擎 将会举起它的手并喊出一个错误!
总结来说:对于一个变量赋值 var a = 1;
,发生了两个不同的动作:第一,代码执行前一刻,编译器声明一个变量var a
(如果先前没有在当前作用域中声明过),第二,代码执行时,引擎 在 当前的作用域集合中 查询这个变量 a
并给它赋值a = 1
,如果找到的话。