浏览器及JavaScript引擎工作原理

2,288 阅读7分钟

常见的浏览器内核

浏览器/RunTime内核(渲染引擎)JavaScript 引擎
ChromeBlink(28~) Webkit(Chrome 27)V8
FireFoxGeckoSpiderMonkey
SafariWebkitJavaScriptCore
EdgeEdgeHTMLChakra(for JavaScript)
IETridentChakra(for JScript)
PhantomJSWebkitJavaScriptCore
Node.js--V8

浏览器的主要组成部分

  • 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
  • 呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
  • 浏览器引擎- 在用户界面和呈现引擎之间传送指令。
  • 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
  • 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
  • JavaScript 解释器 - 用于解析和执行 JavaScript 代码。
  • 数据存储 - 这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

浏览器的主要组件

浏览器是如何渲染UI的?

  • 浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree
  • 与此同时,进行CSS解析,生成Style Rules
  • 接着将DOM Tree与Style Rules合成为 Render Tree
  • 接着进入布局(Layout)阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标
  • 随后调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来

WebKit主流程图

什么是JavaScript解析引擎?

简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2。

“JavaScript 引擎” 通常被称作一种虚拟机

JavaScript 虚拟机是一种进程虚拟机,专门设计来解释和执行的 JavaScript代码。 JavaScript 引擎的基本工作是把开发人员写的 JavaScript代码转换成高效、优化的代码,这样就可以通过浏览器进行解释甚至嵌入到应用中。每个JavaScript引擎都实现了一个版本的ECMAScript,JavaScript是它的一个分支。随着ECMAScript的不断发展,JavaScript引擎也不断改进。之所以有这么多不同的引擎,是因为它们每个都被设计运行在不同的web浏览器、headless浏览器、或者像Node.js那样的运行时环境中,它的唯一的目的就是读取和编译JavaScript代码。

  1. 解析引擎就是根据ECMAScript定义的语言标准来动态执行JavaScript字符串;
  2. 解析JS的过程分成两个阶段:语法检查阶段运行阶段;
  3. 语法检查包括词法分析语法分析,运行阶段又包括预解析运行阶段(V8引擎会将JavaScript字符串编译成二进制代码,此过程应该归到语法检查过程中);
  4. 在JavaScript解析过程中,如遇错误就直接跳出当前代码块,直接执行下一个script代码段。所以在同一个script内的代码段有错误的话就不会执行下去,但是不会影响下一个script内的代码段。

第一阶段:语法检查

语法检查也是JavaScript解析器的工作之一,包括词法分析 和 语法分析,过程大致如下:

一:词法分析:

JavaScript解释器先把JavaScript代码(字符串)的字符流按照ECMAScript标准转换为记号流。

a = (b - c);

转换为记号流:

NAME "a"
EQUALS
OPEN_PARENTHESIS
 NAME "b"
MINUS 
NAME "c"
CLOSE_PARENTHESIS
SEMICOLON

二:语法分析:

JavaScript语法分析器在经过词法分析后,将记号流按照ECMAScript标准把词法分析所产生的记号生成语法树。通俗地说就是把从程序中收集的信息存储到数据结构中,每取一个词法记号,就送入语法分析器进行分析。

语法分析不做的事:去掉注释,自动生成文档,提供错误位置(可以通过记录行号来提供)。 ECMAScript标准如下:

varifelsebreakcontinue等是JavaScript的关键词
怎么样算是数字、怎么样算是字符串等等
定义了操作符(+,-,=)等操作符
定义了JavaScript的语法
定义了对表达式,语句等标准的处理算法,比如遇到==该如何处理 
...

当语法检查正确无误之后,就可以进入运行阶段了。

第二阶段:运行阶段

预解析

第一步:创建执行上下文。解析器将语法检查正确后生成的语法树复制到当前执行上下文中。

第二步:属性填充。解析器会对语法树当中的变量声明、函数声明以及函数的形参进行属性填充。 预解析阶段创建的执行上下文包括:变量对象、作用域链、this

变量对象(Variable Object):由var declaration、function declaration(变量声明、函数声明)、arguments(参数)构成。变量对象是以单例形式存在。

作用域链(Scope Chain):variable object + all parent scopes(变量对象以及所有父级作用域)构成。 this值:(thisValue):content object。this值在进入上下文阶段就确定了。一旦进入执行代码阶段,this值就不会变了。

console.log(a); //function a(){console.log(4);}
var a = 1;
function a(){console.log(2);}
console.log(a);
var a = 3;
console.log(a);
function a(){console.log(4);}
console.log(a)

第一行:会弹出functiona(){console.log(4);} ,因为预解析完成之后,被存进内存的a的值就是functiona(){console.log(4);}

第二行:第三行里有表达式,a被赋了一个新的值1表达式会改变变量的值。表达式可以改变预解析的值。

第三行:只是函数的声明,并没有用到表达式,而且也没有函数的调用,所以不会改变a的值。

第四行:因为a的值没有变化,所以还是1

第五行:使用了表达式,a被赋了一个新的值3

第六行:会弹出3

第七行:函数的声明,不会改变a的值。

第八行:a的值没有改变,所以还是3

JavaScript解析引擎与ECMAScript的关系

JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?这就需要定义规则。比如,之前提到的var a = 1 + 1;,它表示:

  • 左边var代表了这是申明(declaration),它申明了a这个变量
  • 右边的+表示要将1和1做加法
  • 中间的等号表示了这是个赋值语句
  • 最后的分号表示这句语句结束了

标准的JavaScript引擎就会根据ECMAScript这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如IE的JS引擎。这也是为什么JavaScript会有兼容性的问题。

JavaScript解析引擎与浏览器是什么关系

简单地说,JavaScript引擎是浏览器的组成部分之一。因为浏览器还要做很多别的事情,比如解析页面、渲染页面、Cookie管理、历史记录 等等。那么,既然是组成部分,因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。比如:IE9的Chakra、Firefox的 TraceMonkey、Chrome的V8等等。

从而也看出,不同浏览器都采用了不同的JavaScript引擎。

浏览器的单线程及事件循环、宏任务和微任务 可阅读我前面的 JS事件循环、宏任务和微任务

浏览器详细的原理可查看 浏览器原理

JavaScript引擎可查看 JavaScript引擎运行原理解析