编译原理
JavaScript是一门“解释执行”语言。JavaScript 引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能比预想的要复杂。在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。
1。分词/词法分析
2。解析/语法分析
3。代码生成
而对于 JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒。简单地说,任何 JavaScript 代码片段在执行前都要进行编译(通常就在执行前)。因此,JavaScript 编译器首先会对 var a = 1; 这段程序进行编译,然后做好执行它的准备,并且通常马上就会执行它。
参与编译的成员
首先让我们来了解一下参与编译的有哪些成员1。JavaScript引擎:从头到尾负责整个 JavaScript 程序的编译及执行过程。
2。编译器:负责语法分析及代码生成等脏活累活。
3。作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
例子:
var a = 1;
1。遇到 var a ,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果已拥有,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为 a 。
2。接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理 a = 1 这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作 a 的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。
如果引擎最终找到了 a 变量,就会将 1 赋值给它。否则引擎就会举手示意并抛出一个异常!
总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
作用域嵌套
function foo(a) {
console.log(a + b);
}
var b = 2;
foo(2); // 4
这里对 b 进行的 RHS 查询在 foo 作用域中无法找到,但可以在上一级作用域(这个例子中就是全局作用域)中找到。
引擎如何查找? LHS : RHS;
var a = 1;
var b = a + 2;
对变量进行赋值所执行的查询叫 LHS。
找到并使用变量值所执行的查询叫 RHS。
区分LHS和RHS的重要性
变量还没有声明的情况下,这两种查询的行为是不一样的。
如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError异常。
如果LHS查询在所有嵌套的作用域中遍寻不到所需的变量,会导致自动隐式地创建一个全局变量(非严格模式)。
(严格模式抛出 ReferenceError 异常)
总结:
编译器、引擎和作用域是 javascript 代码执行的基础,掌握好这些会对我们深入学习 javascript 起到事半功倍的效果。
参考文献
《你不知道的JavaScript》