JavaScript代码是如何被执行的

基本概念

  • 编译器,解释器
  • 抽象语法树
  • 字节码和机器码

编译器和解释器

计算机不能直接理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序。根据语言的执行流程,可以把语言分成编译型语言和解释型语言。

编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、go等.

解释型语言: 程序不需要编译,程序在运行时才翻译成机器语言(所以执行前需要环境中安装了解释器),每执行一次都要翻译一次。因此效率比较低。效率比较低,依赖解释器,跨平台性好。

编译器和解释器

我们都知道 JavaScript 存在变量提升,在函数作用域内的任何变量的声明都会被提升到顶部并且值为 undefined。
所以JS引擎好像对同一个脚本执行了两次,第一次完成所有声明,然后第二次才执行代码?还是先编译整个代码然后运行它?这两种都不对。

其实变量声明不过只执行上下文的小把戏。在执行任何语句之前,解释器就要从创建执行上下文后已经存在的作用域中找到变量的值。

抽象语法树

抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有两个分支的节点来表示。

字节码和机器码

字节码(Byte-code):是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。

机器码 (Machine-code):计算机直接使用的程序语言,其语句就是机器指令码,机器指令码是用于指挥计算机应做的操作和操作数地址的一组二进制数。

JavaScript代码执行过程

  1. 生成AST(抽象语法树)
  2. 生成字节码
  3. 执行代码

生成AST

生成AST的步骤可以拆分成以下两个小步骤:

  1. 词法分析:将JavaScript代码解析成一个个词法单元(token)
  2. 语法分析:将词法单元根据一定规则组装成抽象语法树

  1. 词法分析:将JavaScript代码解析成一个个词法单元(token)

例如let a = 2;,通常会被分解为下面这些词法单元 leta=2; 空格是否会被当做词法单元取决于空格在这门语言中是否会具有意义。

分解Token示意图

  1. 语法分析:将词法单元根据一定规则组装成 AST

let a = 2;

console.log(a);

我们可以看到生成的AST结构如下:

AST

高级语言是开发者可以理解的语言,编译器和解释器理解不了。所以无论你使用的是解释型语言还是编译型语言,在编译过程中,它们都会生成一个 AST。当生成 AST之后,编译器/解析器后续的工作都要依靠 AST而不是源码。

生成字节码

JavaScript引擎通过解释器来将 AST 转换成字节码,字节码是无法直接执行的,需要将其转为机器码才能直接执行。V8早期的时候,是直接将AST转成机器码的,后来因为 V8 需要消耗大量的内存来存放转换后的机器码,导致严重的内存占用问题。为了解决这个问题,引入 了字节码。字节码是比机器码轻量得多的代码。

执行代码

生成字节码之后,就到了解释和执行字节码阶段了,

监听热点代码并优化为二进制机器码

解释器会逐条执行字节码,(解释器除了负责生成字节码,还会负责解释执行机器码) 如果发现一段代码重复执行多次,就会它记为热点代码(HotSpot),V8会将这段热点代码提交给优化编辑器,优化编辑器会在后台将字节码编译为二进制代码,然后在对编译后的二进制代码执行优化操作,并保存下来。保存下来的机器码的作用和缓存很类似,当解释器再次遇到相同的内容时,就可以直接执行保存下来的机器码。

这样代码执行得越久,执行效率就会越快,因为会有越来越多的字节码被标记为 热点代码,遇到他们就可以直接执行,而不用转成机器码。

反优化生成的二进制机器码

JavaScript是一种非常灵活的动态语言,对象的结构和属性在运行时任意被改变,而经过优化后的代码只能针对某种固定结构。一旦在执行过程中,对象的结构被动态修改了,那么优化后的代码会变成无效的代码,这时候优化编辑器就需要执行反优化操作,经过反优化的代码下次执行时就会回退到解释器解释执行。

字节码的执行是需要配合编译器和解释器的(这种技术称为即时编译 JIT)所以之前说 JS是一种解释型语言并不准确。

总结

整个过程如下面流程图所示:

JavaScript代码执行过程

参考

  • JavaScript到底是解释型语言还是编译型语言?
  • javascript-ast
  • 极客时间-浏览器工作原理与实践。

以上是 JavaScript代码是如何被执行的 的全部内容, 来源链接: utcz.com/a/30958.html

回到顶部