原文地址 https://medium.com/jspoint/how-javascript-works-in-browser-and-node-ab7d0d09ac2f
JavaScript概要
JavaScript是一种解释性的语言,浏览器读取源代码,然后执行。
JavaScript是动态类型语言。静态类型语言例如C或者Java,必须显示声明int,或者string。而在动态类型语言JavaScript中,所有类型数据int,string以及复杂数据都都可以用var 来申明
JavaScript的历史
在Web刚刚兴起时,web页面是静态的,只展示,没有和用户的交互。为了满足和用户交互的需求,当时大名鼎鼎的网景浏览器(Netscape)在1995年引入了一个新的语言(当时命名LiveScript),这个就是后来的JavaScript。这个语言的第一个版本只花了网景工程师Brendan Eich十天的时间。
同时期出现的还有其他语言,ActionScript,Silverlight,Flash等等,但最终JavaScript赢得了比赛。
JavaScript引擎的解刨
EcmaScript标准规定了浏览器应该怎么实现JavaScript.但是并没有规定JavaScript应该在浏览器里面怎么运行,所以不同的浏览器厂商自己决定怎么实现JavaScript。
每个浏览器实现一个JavaScript引擎。Netscape公司使用SpiderMonkey。这个引擎使用最原始的解释器并且没有任何优化。JavaScript运行起来非常慢
Alt text
从上图可以看到原始的JavaScript引擎包含一个基线编译器,把JavaScript源代码编译成中间代码(Intermediate representation),也就是字节码,喂给解释器。
解释器把字节码转换成机器码,最终在在CPU上执行。
基线编译器的工作是尽可能快的产生字节码,由于喂给解释器的字节码没有经过优化,程序运行起来很慢,但是启动快。
当动态交互越来越多时,上面的技术产生的用户体验就会非常差。Google的Chrome浏览器在展示GoogleMap时,就遇到这个问题,然后他们提出了V8 JavaScript引擎来解决这个问题。
Alt text
在2010版本的V8 JavaScript引擎中,主要有两个模块,如上图所示full-codegen是基线编译器,为了提高程序的启动速度,尽可能快的产生机器码。当程序运行过车中,crankshaft编译器插入进来,优化源代码并且把源代码中可以优化的部分产生的机器码替换掉full-codegen产生的部分。
JavaScript怎么被优化的
JavaScript有很多种被优化的标准,当JavaScript被传入基线编译器或者解释器的时候,必须先被转换成抽象法树(Abstract Syntax Tree,简称AST)
当我们运行JavaScript程序时,启动时并不需要所有的代码,例如用户点击才会触发的函数,那个函数可以在被点击时才被解析。
识别需要立即解析并生成机器代码的内容是加快应用程序引导速度的最佳策略。
JavaScript没有类型系统的特征使JavaScript引擎产生优化程度较低的代码。所以,基于已经赋值的值,JavaScript Engine可以猜测它的类型产生优化程度较高的代码。
Paul Ryan在他关于V8引擎的博文对整个过程做了很好的说明。有兴趣深入研究的同学可以了解下。
与此同时,JavaScript引擎还可以收集代码执行的分析数据,并寻找运行较慢的代码。这中代码被称为"热"代码,可能是因为它比较费CPU。这种代码可以进一步优化,并用优化后的机器码替换。
考虑大上面这些问题已经full-codegen和crankshaft产生的其他问题,V8团队从头开始做了一款新的V8引擎。2017年发布。
Alt text
)
从上图可以看出,V8团队引入新的解释器管道Ignition来执行编译的过程。首先才能够用基线编译器从JavaScript源代码产生字节码,然后用解释器去解释这个字节码,最终产生机器码。
当程序在运行的时候, TurboFan优化编译器在后台优化基线编译器产生的字节码,产生一个优化后的机器码,最终替换原来的版本的机器码。
Turbofan接受Ignition解释器代码执行的分析数据,并且查看对于的代码是否是“热”代码(是否可以优化)。然后优化对应的代码
其他的JavaScript引擎
现在我们大概知道了V8 JavaScript引擎是怎么工作的。其他浏览器制造商的其他引擎是采用类似的模型。例如网景和火狐采用的的SpiderMonkey,Firefox的Chakra等。
除了Google Chrome浏览器,Chromium项目,Electron.js 以及Node.js都采用V8引擎。
JavaScript运行时
JavaScript是一个运行时多线程语言。就是同一时间只能有一条指令执行。
当你打开一个网站的时候,他用一个线程执行JavaScript,这个线程负责处理所有的事情,例如抓取web页面,在web页面上输出,监听Dom事件等等。
当JavaScript执行被阻塞时,浏览器就会卡在这个点。例如死循环等。
有一些现代浏览器每个tab或者每个domain使用一个JavaScript线程。这样在一个页面上的阻塞,只会阻碍当前页面。
我们用一小段代码来说明JavaScript是怎么执行程序的,理解JavaScript的运行时,以及不同的模块怎么参与的
function baz() {
console.log( 'Hello from baz' );
}
function bar() {
baz();
}
function foo() {
bar();
}
foo();
它的调用栈是这样的:
1.gif
同时,把这段代码放到浏览器中执行,得到的调用栈如下
2.png
后续
后续会补充浏览器中的线程模型
网友评论