编译原理
JS是动态语言,但事实上是一门编译语言,与传统编译语言不同的是,它不是提前编译的。
尽管如此,JS引擎进行编译的步骤与传统编译语言非常相似,传统编译语言的流程中,一般在执行之前会经历三个步骤
-
分词、词法分析
将代码分解成代码块,如
var a - 2,会被分解为var,a,=,2 -
解析、语法分析
将词法数组转化成一个语法树,如
var a = 2,会分解成下面的语法树(AST)
-
代码生成
将上一步生成的语法树(AST)转化为可执行代码(机器指令)
当然,JS引擎要复杂得多,JS引擎会在语法分析和代码生成阶段又特定的步骤对运行性能进行优化,与其他语言不同的是,JS的编译过程不是发生在构建之前,大部分情况下编译发生在代码执行前的极短的时间内
理解LHS和RHS
来看下面的一段代码
var a = 2
这段代码的作用是“为一个变量分配内存,将其命名为a,然后将值2保存进这个变量”,但实际上编译器会进行如下的操作
-
遇到
var a,编译器询问作用域是否已经存在该名称的变量如果有:则忽略,继续进行编译
如果没有:则要求当前作用域声明一个新的变量,命名为a
-
接下来会处理
a = 2的操作,引擎会询问当前作用域是否存在一个a的变量如果存在:使用这个变量
如果不存在:继续查找该变量
在上面的代码执行过程中,引擎会执行一个查询变量的操作,在例子中,引擎会对变量a进行LHS查询,与之对应的是RHS查询
很容易猜到,L和R就是left,right,也就是变量出现在赋值操作的左侧进行LHS查询,出现在右侧进行RHS查询
更准确一点讲:RHS查询就是查找某个变量的值,LHS查询则是试图找到变量的容器本身
这串代码console.log(a)中,对a的操作就是RHS
来个例子加深下理解
function foo(a){
console.log(a)
}
foo(2)
执行过程如下:
- 首先执行
foo(2),这时引擎会对foo进行RHS(1) - 然后进入到foo函数体中,对
a进行LHS(1) - 执行
console.log(a),对console进行RHS(2) - 最后,对a进行RHS(3),将得到的值传给
console.log(..)