这是我参与8月更文挑战的第二十三天,活动详情查看:8月更文挑战
编译原理
JavaScript通常被归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。但是它和传统的编译语言是不同的,它不是提前编译的,编译结果也不能在分布式系统中进行移植。尽管如此,JavaScript引擎在进行编译的步骤和传统的编译语言非常的类似。在某些环节可能比想象的要复杂。在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”:
- 分词/词法分析:将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元。
- 解析/语法分析:将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树:AST)。
- 代码生成:将AST转换为可执行代码。
JS编译原理
- 引擎:从头到尾负责整个JavaScript的程序编译及执行过程。
- 编译器:负责语法分析及代码生成。
- 作用域:负责收集并维护由所有变量(标识符)组成的一系列查询,并实施一套规则,确定当前执行的代码对这些标识符的访问权限。 例如:变量的赋值操作会执行两个动作:
- 编译器会在当前作用域中声明一个变量(如果之前没有声明过)
- 在运行时,引擎会在作用域中查找该变量,如果能够找到就会对它赋值。 那么,引擎是怎么查找变量的呢?
LHS查询和RHS查询
LHS(left-hand Side)查询和RHS(right-hand Side)查询,通常是指赋值运算的左右边的引用。
当变量出现在赋值操作的左侧时进行LHS查询(找到存储的位置并赋值),出现在右侧时进行RHS查询(找到它具体的值)
LHS和RHS的含义是“赋值操作的左侧或右侧”并不一定意味着就是“=赋值操作符的左侧或右侧”。
赋值操作还有其他几种形式,因此在概念上最好将其理解为“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”
举个例子:
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
LHS查询:
- c=... ,c是赋值操作的目标,所以对c需要LHS查询。
- 隐藏的a=2(隐式变量分配),在调用foo(2)的时候,需要将实参2赋值给形参阿,所以对a需要LHS查询
- b=.... ,同1
*RHS查询
- foo(2) ,foo(2)在赋值操作的右边,需要知道foo(2)的值,所以是RHS查询
- =a,a在赋值操作的右边,需要知道a的值,对a进行RHS查询
- a...,在return a+b中,需要知道a和b的值,应该对a和b分别进行RHS查询。