JavaScript代码是如何被执行的
基本概念
-
编译器,解释器 -
抽象语法树 -
字节码和机器码
编译器和解释器
计算机不能直接理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序。根据语言的执行流程,可以把语言分成编译型语言和解释型语言。
编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、go等.
解释型语言: 程序不需要编译,程序在运行时才翻译成机器语言(所以执行前需要环境中安装了解释器),每执行一次都要翻译一次。因此效率比较低。效率比较低,依赖解释器,跨平台性好。
编译型与解释型,两者各有利弊, 不能一概而论。前者由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,像C/C++、Pascal/Object Pascal(Delphi)等都是编译语言,而一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言,如JavaScript、VBScript、Perl、Python、Ruby、MATLAB 等等。
我们都知道 JavaScript 存在变量提升,在函数作用域内的任何变量的声明都会被提升到顶部并且值为 undefined。所以JS引擎好像对同一个脚本执行了两次,第一次完成所有声明,然后第二次才执行代码?还是先编译整个代码然后运行它?这两种都不对。
其实变量声明不过只执行上下文的小把戏。在执行任何语句之前,解释器就要从创建执行上下文后已经存在的作用域中找到变量的值。
抽象语法树
抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有两个分支的节点来表示。
字节码和机器码
字节码(Byte-code):是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。
机器码 (Machine-code):计算机直接使用的程序语言,其语句就是机器指令码,机器指令码是用于指挥计算机应做的操作和操作数地址的一组二进制数。
JavaScript代码执行过程
-
生成AST(抽象语法树) -
生成字节码 -
执行代码
生成AST
生成AST的步骤可以拆分成以下两个小步骤:
-
词法分析:将JavaScript代码解析成一个个词法单元(token) -
语法分析:将词法单元根据一定规则组装成抽象语法树
通过 javascript-ast[1] 网站,可以大概了解 代码生成的 Tokens 以及 AST大致的样子。
-
词法分析:将JavaScript代码解析成一个个词法单元(token)
例如let a = 2;
,通常会被分解为下面这些词法单元 let
、a
、=
、2
、;
空格是否会被当做词法单元取决于空格在这门语言中是否会具有意义。
-
语法分析:将词法单元根据一定规则组装成 AST
let a = 2;
console.log(a);
我们可以看到生成的AST结构如下:
高级语言是开发者可以理解的语言,编译器和解释器理解不了。所以无论你使用的是解释型语言还是编译型语言,在编译过程中,它们都会生成一个 AST。当生成 AST之后,编译器/解析器后续的工作都要依靠 AST而不是源码。
AST是一个非常重要数据结构,比如Babel的工作原理就是:ES6 的代码解析成 AST -> 将 ES6 的 AST 转换成 ES5 的AST -> 将 ES5的 AST 转成 ES5的代码。Babel的相关文章推荐 深入浅出 Babel 上篇:架构和原理 + 实战[2];我们使用的 Eslint(检查JavaScript编写规范的插件) 的检测流程也是先将源码转换成 AST, 然后利用 AST 来检查代码规范的问题
生成字节码
JavaScript引擎通过解释器来将 AST 转换成字节码,字节码是无法直接执行的,需要将其转为机器码才能直接执行。V8早期的时候,是直接将AST转成机器码的,后来因为 V8 需要消耗大量的内存来存放转换后的机器码,导致严重的内存占用问题。为了解决这个问题,引入 了字节码。字节码是比机器码轻量得多的代码。
字节码是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需要通过解释器将其转换成机器码后才能执行。
执行代码
生成字节码之后,就到了解释和执行字节码阶段了,
监听热点代码并优化为二进制机器码
解释器会逐条执行字节码,(解释器除了负责生成字节码,还会负责解释执行机器码) 如果发现一段代码重复执行多次,就会它记为热点代码(HotSpot),V8会将这段热点代码提交给优化编辑器,优化编辑器会在后台将字节码编译为二进制代码,然后在对编译后的二进制代码执行优化操作,并保存下来。保存下来的机器码的作用和缓存很类似,当解释器再次遇到相同的内容时,就可以直接执行保存下来的机器码。
这样代码执行得越久,执行效率就会越快,因为会有越来越多的字节码被标记为 热点代码,遇到他们就可以直接执行,而不用转成机器码。
反优化生成的二进制机器码
JavaScript是一种非常灵活的动态语言,对象的结构和属性在运行时任意被改变,而经过优化后的代码只能针对某种固定结构。一旦在执行过程中,对象的结构被动态修改了,那么优化后的代码会变成无效的代码,这时候优化编辑器就需要执行反优化操作,经过反优化的代码下次执行时就会回退到解释器解释执行。
字节码的执行是需要配合编译器和解释器的(这种技术称为即时编译 JIT)所以之前说 JS是一种解释型语言并不准确。
总结
整个过程如下面流程图所示:
参考
-
JavaScript到底是解释型语言还是编译型语言? [3] -
javascript-ast [4] -
极客时间-浏览器工作原理与实践。
参考资料
javascript-ast: https://resources.jointjs.com/demos/javascript-ast
[2]深入浅出 Babel 上篇:架构和原理 + 实战: https://juejin.im/post/5d94bfbf5188256db95589be
[3]JavaScript到底是解释型语言还是编译型语言?: https://blog.csdn.net/qq_38836118/article/details/98878286
[4]javascript-ast: https://resources.jointjs.com/demos/javascript-ast
本文分享自微信公众号 - 牧码的星星(gh_0d71d9e8b1c3)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
V8内存管理及垃圾回收机制
JavaScript引擎的内存空间主要分为栈和堆。 栈 栈是临时存储空间,主要存储局部变量和函数调用。 基本类型数据(Number, Boolean, String, Null, Undefined, Symbol, BigInt)保存在在栈内存中。引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中。 基本类型赋值,系统会为新的变量在栈内存中分配一个新值,这个很好理解。引用类型赋值,系统会为新的变量在栈内存中分配一个值,这个值仅仅是指向同一个对象的引用,和原对象指向的都是堆内存中的同一个对象。 对于函数,解释器创建了”调用栈“来记录函数的调用过程。每调用一个函数,解释器就可以把该函数添加进调用栈,解释器会为被添加进来的函数创建一个栈帧(用来保存函数的局部变量以及执行语句)并立即执行。如果正在执行的函数还调用了其他函数,新函数会继续被添加进入调用栈。函数执行完成,对应的栈帧立即被销毁。 两种查看调用栈的方法 使用 console.trace() [1] 向Web控制台输出一个堆栈跟踪. 浏览器开发者工具进行断点调试 栈虽然很轻量,在使用时创建,使用结...
- 下一篇
Julia/Python/Matlab基本语法比较
相信很多朋友刚开始做算法时应该都是用matlab做理论模型的验证,后来Python又大火,很多小伙伴又争相学起来python,可过了没多久,一个更牛逼的语言又进入了我们的视野--Julia,号称是有matlab似的直观数学表达式,有C的运算速度。相信又有不少朋友蠢蠢欲动了,而小编发现在刚开始学习某种语言时或者在多个语言之间来回切换时,很容易把它的语法跟其他语言搞混,所以今天我们就整理了一份Julia/Python/Matlab三种算法工程师常用的编程语言的基本语法的比较,小伙伴们可以收藏起来,在忘记某个语法时拿出来看看。 基本数据类型 Python:数字、字符串、列表、元组、集合、字典 Matlab:数字、字符串、逻辑值、表、结构体、元胞数组、函数句柄 Julia:数字、字符串、自定义类型(struct/Union/Tuple/Array等) 注:julia 网上很多教程都是针对0.3版本,跟最新的1.0版有很多语法不太一样,在学习时最好看julia官方文档 Python Matlab Julia 基本操作 类型 动态语言,在运行期间才去做数据类型检查,因此无需指定数据类,在第一次赋值...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8编译安装MySQL8.0.19