在js的编程过程中,很少会涉及到编译原理相关知识,但如果想对js底层有更深入的理解,想在架构的道路上走得更远,理解js的编译原理是必不可少的。
接下来会通过编程语言的分类、编译过程两个部分详细分析js的编译原理。
一、编程语言的分类
众所周知,任何编程语言都需要先转为机器码才可以作为机器指令被操作系统识别,编程语言基于转为机器码的时机可分为两大类:编译型语言、解释型语言。
其中,编译型语言在编译阶段转为机器码文件,该文件可供计算机直接使用,代码执行效率特别高,但跨平台比较差;解释型语言则可以直接供计算机使用,不过计算机在使用前依然会通过解释器将其转为机器码,他们是边解释边执行,效率上比较低,但跨平台性比较强。
js是典型的解释型语言,即js可以不经过任何处理直接交给浏览器或其他宿主环境的js引擎,js引擎自带的解释器会在运行时以语句为基本单位将js转为机器码再执行。
二、编译过程
编译过程大致分为三个阶段:分析阶段、预编译阶段,解释执行阶段,具体如下
分析阶段
该阶段在一个script块内的js代码执行前触发
- 词法分析(将字符流(char stream) 转换成标记流(token stream))
- 语法分析(基于标记流生成AST树,若在语法分析的过程中发现无法生成AST树,则会抛出语法异常)
- 基于AST树生成字节码
预编译阶段
该阶段在js代码生成字节码之后,一个script块内的js代码执行前或一个函数调用前触发,主要目的为给当前作用域生成执行上下文(全局上下文,函数上下文)。一个执行上下文一般分为三个部分: this,词法环境,变量环境
this
全局上下文中,this即为全局对象;函数上下文中,this即为调用函数的对象
- 没有调用对象时为全局对象隐式调用
- 通过call或apply方法可以改变this的指向为方法的第一个参数,若方法第一个参数为null或undefined则指向全局对象
词法环境
词法环境的内部有两个组件:环境记录器和一个外部环境的引用
- 环境记录器是存储变量(let,const)和函数声明的实际位置。
- 外部环境的引用意味着它可以访问其父级词法环境。
变量环境
变量环境同样是一个词法环境,与词法环境的不同之处在于变量环境的环境记录器只存储var变量
解释执行阶段
- 将字节码转为机器码
- 计算机执行转换后的机器码
- 识别下一条js语句对应的字节码,重复前两步,直到识别不到任何js语句字节码为止
字节码是机器码的抽象,可以看作是小型的构建块,这些构建块组合到一起构成任何JavaScript功能。字节码比机器码占用更小的内存,这也是为什么V8使用字节码的一个很重要的原因。字节码不能够直接在处理器上运行,需要通过解释器将其转换为机器码后才能执行