深入编译原理
前言
最近在看《YOU DON’T KNOW JS》
一书,里面讲到JS
的编译原理,只是宏观和简单的介绍了一下,于是自己写了此文,一来总结文中所讲的传统语言的编译和JS
的编译原理,二来扩展一下我理解的编译原理
传统编译语言的编译原理
在传统的编译语言中,编译分三步
- 词法分析
首先编译器会把代码分解成有意义的词法单元
- 语法分析
接着,将词法单元组成的词法单元流(数组)解析成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST
)
- 代码生成
最后过程,将上面解析好的AST
转换为可执行代码。说白了,就是通过某种方法可以将AST
转化为一组机器指令
我将上述过程绘制如下图:
JS的编译原理
JS
引擎进行编译的步骤与传统编译语言非常相似,但比起那些编译只有三步的语言来说JS
引擎要复杂的多。例如,在词法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等
而且,对于JS
来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短)的时间内
简单来说,任何JS
代码片段在执行前都要进行编译
我理解的JS编译原理
要理解JS
的编译原理,其实得回顾JS
是如何运行的?
我将它JS
运行分三个步骤
- 词法、语法分析
- 预编译
- 解释执行
通常JS
在执行代码前,系统会先执行词法和语法分析,通篇扫描一遍看是否有语法错误,有错误,程序终止,没有错误就会走到预编译环节
预编译发生在函数执行前的前一刻,具体过程分四步:
1.创建AO
对象;(Active Object
,执行期上下文)
2.找形参和变量声明,将变量名和形参名作为AO
对象的属性名,值为undefined
;
3.将实参和形参相统一;
4.在函数体里面找函数声明(不包括函数表达式),将函数声明的名作为AO
对象的属性名挂起,值为该函数。
在全局开始执行的前一刻,会发生全局预编译,生成的对象是GO
(Global Object
)
预编译执行完才会开始解释执行代码,读一行执行一行,读一行执行一行。在读的过程,就会将代码变成可执行代码
整个过程简单绘制如下:
可以看到,与传统的编译相比,JavaScript
引擎有个很大的特点就是多了一个预编译的环节。预编译之后也是通过引擎将代码解析成可执行代码,然后执行代码
这就是JS
引擎的运行过程和原理
参考资料
《YOU DON’T KNOW JS》