当前位置: 首页 > 科技观察

揭开JavaScript引擎的面纱

时间:2023-03-19 15:52:31 科技观察

前言本来JavaScript只能运行在浏览器端,但是随着Node的出现,JavaScript也可以运行在服务器端了。虽然我们可能知道何时何地使用它,但我们真的了解这些脚本执行的幕后情况吗?如果你认为你对JavaScript引擎有所了解,请给自己一个大大的掌声,但不要急于关闭这篇文章,相信你看完后仍然可以从中学到一些东西。JavaScript是一种高级语言,但最终计算机只能理解1和0。那么我们写的代码是如何被计算机理解的呢?掌握所学编程语言的基础知识将使您能够编写出更好的代码。在本文中,我们只讨论一个问题:JavaScript是如何工作的?进入正题~JavaScript引擎这是本文要探讨的主要内容,它负责让计算机理解我们写的JS代码。JavaScript引擎是用于将我们的代码转换为机器可读语言的引擎。如果没有JavaScript引擎,您编写的代码对计算机来说只不过是“乱码”。不仅仅是JavaScript,其他所有编程语言都需要一个类似的引擎来将这种“废话”转换成对计算机有意义的语言。目前有几种JavaScript引擎可用。您可以在维基百科上查看所有可用的JavaScript引擎。它们也被称为ECMAScript引擎,具体原因如下所述。这里列举一些我们日常生活中可能会用到的JavaScript引擎:Chakra,MicrosoftIE/EdgeSpiderMonkey,FireFoxV8,Chrome除了这些之外的其他引擎,大家可以自行搜索。接下来,我们将深入研究这些引擎以了解它们如何翻译JavaScript文件。JavaScript引擎的内部结构既然我们知道引擎是必需的,那么可能不禁要问:谁发明了JavaScript引擎?答案是,任何人都可以。它只是分析我们的代码并翻译它的另一种语言工具。V8是最流行的JavaScript引擎之一,被Chrome和NodeJS使用。它是用C++(一种低级语言)编写的。但是如果每个人都创造一个引擎,那场面就失控了。因此,为了给这些引擎建立一个规范,ECMA标准诞生了,它主要提供了如何编写引擎以及JavaScript的所有功能的规范。这就是为什么可以在ECMAScript6、7、8上实现新功能的原因。同时,引擎也进行了更新以支持这些新功能。因此,我们能够在开发过程中检查浏览器中高级JS功能的可用性。让我们进一步探索V8引擎,因为所有引擎的基本概念都是一致的。JavaScriptV8Engine上图是JSEngine的内部工作流程。我们输入的代码会经过以下几个阶段,ParserASTInterpreterGenerateByteCodeProfilerCompilerGenerateoptimizedcode不要被上面的过程给骗了,几分钟后你就会明白它们是一起工作的。在进一步深入这些阶段之前,您需要了解解释器和编译器之间的区别。InterpreterVSCompiler一般来说,有两种方法可以将代码转换成机器可读的语言。我们将要讨论的概念不仅适用于JavaScript,也适用于大多数编程语言,如Python、Java等。解释器逐行读取代码并立即执行。编译器读取您的整个代码,进行一些优化,并生成优化代码。让我们看看下面的例子。functionadd(a,b){returna+b}for(leti=0;i<1000;i++){add(1+1)}上面的例子循环调用add函数1000次,将两个数相加并返回和。一旦Interpreter收到上面的代码,它会逐行读取并立即执行代码,直到循环结束。它的工作只是将代码翻译成我们的计算机可以实时理解的东西。如果这段代码的接收者是Compiler,它首先会完整地读取整个程序,分析我们要执行的代码,生成计算机可以理解的机器语言。这个过程就像获取X(我们的JS文件)并生成Y(机器语言)。如果我们使用解释器执行Y,我们会得到与执行X相同的结果。从上图可以看出,ByteCode只是一个中间代码,计算机还需要对其进行翻译才能执行。但是Interpreter和Compiler都是将源代码转换成机器语言,它们之间的区别只是转换的过程不同。解释器逐行将源代码转换为等效的机器代码。编译器首先将所有源代码转换为机器代码。看完上面的推荐文章,你可能已经知道Babel其实就是一个JSCompiler,它可以接收你写的新版本的JS代码,并将其编译成浏览器兼容的JS代码(旧版本的JS代码)。Interpreter和Compiler的优缺点Interpreter的优点是代码可以立即执行,不需要等待编译。这对于在浏览器中运行JS来说是一个极大的便利,因为所有用户都不想浪费时间等待代码编译。但是,当有大量的JS代码需要执行时,运行速度会变慢。还记得上面例子中的那一小段代码吗?代码中有1000个函数调用。函数add被调用了1000次,但其输出保持不变。但是Interpreter还是逐行执行,会显得比较慢。同样的情况下,Compiler可以通过将循环替换为2来做一些优化(因为add函数每次都执行1+1)。Compiler最终给出的优化代码可以在更短的时间内执行。综上所述,Interpreter可以立即开始执行代码,但不会优化。编译器虽然编译代码需要一些时间,但它会生成更利于执行的代码。好了,我们了解了Interpreter和Compiler的必要知识。现在让我们回到正题——JS引擎。那么,考虑到编译器和解释器的优缺点,如果我们利用两者呢?这就是JIT(JustInTime)编译器的用武之地。它是解释器和编译器的组合,现在大多数浏览器都可以更快、更高效地实现此功能。同时,V8引擎也使用了这个功能。在这个过程中,Parser是一个解析器,它通过各种JavaScript关键字对程序的各个部分进行识别、分析和分类。它可以区分代码是方法还是变量。然后,AST(AbstractSyntaxTree)根据Parser的分类构造树结构。您可以使用ASTExplorer来查看这棵树的结构。然后将AST提供给解释器以生成字节码。上面说了,ByteCode不是最底层的代码,但是可以执行。这个阶段浏览器使用V8引擎执行ByteCode来工作,用户不需要等待。同时,Profiler会找到可以优化的代码并将它们传递给Compiler。在Compiler生成优化代码的同时,浏览器暂时使用ByteCode执行操作。并且,一旦Compiler生成优化后的代码,优化后的代码将完全替代临时的ByteCode。这样,我们就可以充分利用Interpreter和Compiler的优势。当Interpreter执行代码时,Profiler寻找可以优化的代码,而Compiler创建优化的代码。然后,将ByteCode代码替换为优化的低级代码,例如机器码。这只是意味着性能会逐渐提高,同时不会有阻塞执行时间。关于ByteCode作为机器码,ByteCode并不是所有的计算机都能理解和执行的。它仍然需要像虚拟机或JavascriptV8引擎这样的中间件来将其转换为机器可读的语言。这就是为什么我们的浏览器可以在上述5个阶段借助JavaScript引擎在Interpreter中执行ByteCode。那么你可能会有另一个问题,JavaScript是一种解释型语言吗?JavaScript不完全是一种解释型语言。BrendanEich最初在JavaScript早期创建了JavaScript引擎“SpiderMonkey”。引擎有一个解释器来告诉浏览器如何执行代码。但是现在我们的引擎不仅包括Interpreter,还包括Compiler。我们的代码不仅可以转换成ByteCode,还可以编译输出优化后的代码。所以从技术上讲,这完全取决于引擎的实现方式。这就是JavaScript引擎的整体工作方式。我相信您不需要学习JavaScript就能理解它。当然,您甚至可以在不知道JavaScript工作原理的情况下编写代码。然而,如果我们了解一点幕后情况,它可能会让我们写出更好的代码。