浏览器工作原理和JS引擎

1.浏览器工作原理

在浏览器中输入查找内容,浏览器是怎样将页面加载出来的?以及JavaScript代码在浏览器中是如何被执行的?

大概流程可观察以下图:

  • 首先,用户在浏览器搜索栏中输入服务器地址,与服务器建立连接;
  • 服务器返回对应的静态资源(一般为index.html);
  • 然后,浏览器拿到index.html后对其进行解析;
  • 当解析时遇到css或js文件,就向服务器请求并下载对应的css文件和js文件;
  • 最后,浏览器对页面进行渲染,执行js代码;

那么在输入服务器地址,敲下回车那一刻会发生什么?

  • 对浏览器输入的地址进行DNS解析,将域名解析成对应的IP地址;
  • 然后向这个IP地址发送http请求,服务器收到发送的http请求,处理并响应;
  • 最终浏览器得到浏览器响应的内容;

2.浏览器的内核

浏览器从服务器下载的文件最终要进行解析,那么内部是谁在帮助解析呢?这里就涉及到浏览器内核。不同的浏览器由不同的内核构成,以下是几个常见的浏览器内核:

  • Gecko:早期被Netscape和Mozilla Firefox浏览器使用过;
  • Trident:由微软开发的,IE浏览器一直在使用,但Edge浏览器内核已经转向了Blink;
  • Webkit:苹果基于KHTML开发,并且是开源的,用于Safari,Google Chrome浏览器早期也在使用;
  • Blink:Google基于Webkit开发的,是Webkit的一个分支,目前应用于Google Chrome、Edge、Opera等等;

事实上,浏览器内核指的是浏览器的排版引擎(layout engine),也称为浏览器引擎、页面渲染引擎或样版引擎。

3.浏览器的渲染过程

浏览器从服务器下载完文件后,就需要对其进行解析和渲染,流程如下:

  • HTML Parser将HTML解析转换成DOM树
  • CSS Parser将样式表解析转换成CSS规则树
  • 转换完成的DOM树和CSS规则树Attachment(附加)在一起,并生成一个Render Tree(渲染树)
  • 需要注意的是,在生成Render Tree并不会立即进行绘制,中间还会有一个Layout(布局)操作,也就是布局引擎
  • 为什么需要布局引擎再对Render Tree进行操作?因为不同时候浏览器所处的状态是不一样的(比如浏览器宽度),Layout的作用就是确定元素具体的展示位置和展示效果;
  • 有了最终的Render Tree,浏览器就进行Painting(绘制),最后进行Display展示;
  • 可以发现图中还有一个紫色的DOM三角,实际上这里是js对DOM的相关操作;
  • 在HTML解析时,如果遇到JavaScript标签,就会停止解析HTML,而去加载和执行JavaScript代码;

那么,JavaScript代码由谁来执行呢?下面该JavaScript引擎出场了。

4.JavaScript引擎

首先由两个问题来认识一下JavaScript引擎。

(1)为什么需要JavaScript引擎?

  • 首先,我们需要知道JavaScript是一门高级编程语言,所有的高级编程语言都是需要转换成最终的机器指令来执行的;
  • 而我们知道编写的JS代码可以由浏览器或者Node执行,其底层最终都是交给CPU执行;
  • 但是CPU只认识自己的指令集,也就是机器语言,而JavaScript引擎主要功能就是帮助我们将JavaScript代码翻译CPU所能认识指令,最终被CPU执行;

(2)JavaScript引擎有哪些?

  • SpiderMonkey:第一款JavaScript引擎,由Brendan Eich开发(JavaScript作者);
  • Chakra:用于IE浏览器,由微软开发;
  • JavaScriptCore:Webkit中内置的JavaScript引擎,由苹果公司开发;
  • V8:目前最为强大和流行的JavaScript引擎,由Google开发;

5.浏览器内核和JS引擎的关系

这里以Webkit内核为例。

  • 实际上,Webkit由两部分组成:

    • WebCore:负责HTML解析、布局、渲染等相关的操作;
    • JavaScriptCore(JSCore):解析和执行JavaScript代码;
  • 小程序中编写的JavaScript代码就是由JSCore执行的,也就是小程序使用的引擎就是JavaScriptCore:

    • 渲染层:由Webview来解析和渲染wxml、wxss等;

    • 逻辑层:由JSCore来解析和执行JS代码;

    • 以下为小程序的官方架构图:

6.V8引擎

下面一起深入了解一下强大的V8引擎。

6.1.V8引擎的原理

先了解一下官方对V8引擎的定义:

  • V8引擎使用C++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等,可以独立运行,也可以嵌入到任何C++的应用程序中。。

  • 所以说V8并不单单只是服务于JavaScript的,还可以用于WebAssembly(一种用于基于堆栈的虚拟机的二进制指令格式),并且可以运行在多个平台

  • 下图简单的展示了V8的底层架构:

6.2.V8引擎的架构

V8的底层架构主要有三个核心模块(Parse、Ignition和TurboFan),接下来对上面架构图进行详细说明。

(1)Parse模块:将JavaScript代码转换成AST(抽象语法树)。

  • 该过程主要对JavaScript源代码进行词法分析和语法分析;

  • 词法分析:对代码中的每一个词或符号进行解析,最终会生成很多tokens(一个数组,里面包含很多对象);

    • 比如,对const name = 'curry'这一行代码进行词法分析:

      1. // 首先对const进行解析,因为const为一个关键字,所以类型会被记为一个关键词,值为const
      2. tokens: [
      3. { type: 'keyword', value: 'const' }
      4. ]
      5. // 接着对name进行解析,因为name为一个标识符,所以类型会被记为一个标识符,值为name
      6. tokens: [
      7. { type: 'keyword', value: 'const' },
      8. { type: 'identifier', value: 'name' }
      9. ]
      10. // 以此类推...
  • 语法分析:在词法分析的基础上,拿到tokens中的一个个对象,根据它们不同的类型再进一步分析具体语法,最终生成AST;

  • 以上即为简单的JS词法分析和语法分析过程介绍,如果想详细查看我们的JavaScript代码在通过Parse转换后的AST,可以使用AST Explorer工具:

  • AST在前端应用场景特别多,比如将TypeScript代码转成JavaScript代码、ES6转ES5、还有像vue中的template等,都是先将其转换成对应的AST,然后再生成目标代码;

  • 参考官方文档:https://v8.dev/blog/scanner

(2)Ignition模块:一个解释器,可以将AST转换成ByteCode(字节码)。

  • 字节码(Byte-code):是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码。
  • 将JS代码转成AST是便于引擎对其进行操作,前面说到JS代码最终是转成机器码给CPU执行的,为什么还要先转换成字节码呢?
    • 因为JS运行所处的环境是不一定的,可能是windows或Linux或iOS,不同的操作系统其CPU所能识别的机器指令也是不一样的。字节码是一种中间码,本身就有跨平台的特性,然后V8引擎再根据当前所处的环境将字节码编译成对应的机器指令给当前环境的CPU执行。
  • 参考官方文档:https://v8.dev/blog/ignition-interpreter

(3)TurboFan模块:一个编译器,可以将字节码编译为CPU认识的机器码。

  • 在了解TurboFan模块之前可以先考虑一个问题,如果每执行一次代码,就要先将AST转成字节码然后再解析成机器指令,是不是有点损耗性能呢?强大的V8早就考虑到了,所以出现了TurboFan这么一个库;
  • TurboFan可以获取到Ignition收集的一些信息,如果一个函数在代码中被多次调用,那么就会被标记为热点函数,然后经过TurboFan转换成优化的机器码,再次执行该函数的时候就直接执行该机器码,提高代码的执行性能;
  • 图中还存在一个Deoptimization过程,其实就是机器码被还原成ByteCode,比如,在后续执行代码的过程中传入热点函数的参数类型发生了变化(如果给sum函数传入number类型的参数,那么就是做加法;如果给sum函数传入String类型的参数,那么就是做字符串拼接),可能之前优化的机器码就不能满足需求了,就会逆向转成字节码,字节码再编译成正确的机器码进行执行;
  • 从这里就可以发现,如果在编写代码时给函数传递固定类型的参数,是可以从一定程度上优化我们代码执行效率的,所以TypeScript编译出来的JavaScript代码的性能是比较好的;
  • 参考官方文档:https://v8.dev/blog/turbofan-jit

6.3.V8引擎执行过程

V8引擎的官方在Parse过程提供了以下这幅图,最后就来详细了解一下Parse具体的执行过程。

  • ①Blink内核将JS源码交给V8引擎;
  • ②Stream获取到JS源码进行编码转换
  • ③Scanner进行词法分析,将代码转换成tokens;
  • ④经过语法分析后,tokens会被转换成AST,中间会经过Parser和PreParser过程:
    • Parser:直接解析,将tokens转成AST树;
    • PreParser:预解析(为什么需要预解析?)
      • 因为并不是所有的JavaScript代码,在一开始时就会执行的,如果一股脑对所有JavaScript代码进行解析,必然会影响性能,所以V8就实现了Lazy Parsing(延迟解析)方案,对不必要的函数代码进行预解析,也就是先解析急需要执行的代码内容,对函数的全量解析会放到函数被调用时进行。
  • ⑤生成AST后,会被Ignition转换成字节码,然后转成机器码,最后就是代码的执行过程了;

深入浏览器工作原理和JS引擎(V8引擎为例)的更多相关文章

  1. JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧

    概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...

  2. JavaScript工作原理和Node异步I/O

    1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比如var a=1+2:对于静态语言来说 ...

  3. HTTPS工作原理和TCP握手机制

    1.HTTPS的工作原理 HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手, 在握手过程中将确立双方加密传输数据的密码信息. TLS/SSL协议不仅仅是一套加密传输的协议, ...

  4. 网卡工作原理和wireshark混杂模式

    通过设置网卡为混杂模式就能捕获局域网内所有发包内容,包括非广播包和非发给自己主机的数据包 这是为什么呢? 即主机A发送一个数据包给主机B,我作为主机C怎么也能截获这个数据包呢,原理是什么? 我的网卡为 ...

  5. git的工作原理和git项目创建及克隆

    Git基本理论(重要)三个区域Git本地有三个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或Git Directory).如 ...

  6. Node.js和Chrome V8 引擎了解

    说起Node就不得不先介绍一个Chrome V8 引擎. 随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScr ...

  7. node.js背后的引擎V8及优化技术

    本文将挖掘V8引擎在其它方面的代码优化,如何写出高性能的代码,及V8的性能诊断工具.V8是chrome背后的javascript引擎,因此本文的相关优化经验也适用于基于chrome浏览器的javasc ...

  8. JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍

    概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...

  9. V8 引擎是如何工作的?

    V8 引擎是如何工作的? 本文翻译自:How the V8 engine works? ​ V8是谷歌德国开发中心构建的一个JavaScript引擎.它是由C++编写的开源项目,同时被客户端(谷歌浏览 ...

随机推荐

  1. redis入门到精通系列(三):key的通用操作和redis内部db的通用操作

    五种数据类型都用到了key,key本身是一种字符串,通过key可以获取redis中保存的对象.这一篇博客就将介绍key的通用操作. (一)key基本操作 删除key del key key是否存在 e ...

  2. 使用MySQL的SELECT INTO OUTFILE ,Load data file,Mysql 大量数据快速导入导出

    使用MySQL的SELECT INTO OUTFILE .Load data file LOAD DATA INFILE语句从一个文本文件中以很高的速度读入一个表中.当用户一前一后地使用SELECT ...

  3. 南邮CTF-MISC-Remove Boyfriend

    Remove Boyfriend 打开wireshark,找到关键字部分Remove Boyfriend 在第五行 在此行右击 点击追踪流 选择TCP流,可以分析出流量的传输过程 通过上面的执行列表 ...

  4. Android 内存泄漏检测工具 LeakCanary(Kotlin版)的实现原理

    LeakCanary 是一个简单方便的内存泄漏检测框架,做 android 的同学基本都收到过 LeakCanary 检测出来的内存泄漏.目前 LeakCanary 最新版本为 2.7 版本,并且采用 ...

  5. how2heap libc2.31学习

    今天是四月十九,想在五月份之前把how2heap中的高版本(2.31)的例子过一遍.所以这个系列目前还是在更新中.如果比较简单就几句话带过了,遇到难一点的会写的详细一点. fastbin_dup 源代 ...

  6. [ZJCTF 2019]EasyHeap

    目录 逆向分析 create 函数 edit 函数 delete 函数 利用思路 exp 脚本 get flag 内容来源 逆向分析 -------------------------------- ...

  7. 使用bochs调试汇编程序

    使用bochs调试汇编程序 前面我们已经搭建好了bochs的环境,并且将我们的汇编程序写入了硬盘里面,现在我们来看看如何通过bochs来调试我们的程序. 前文:https://www.cnblogs. ...

  8. C# ASP.NET MVC/WebApi 或者 ASP.NET CORE 最简单高效的跨域设置

    概述 前面写了一篇:<C# ASP.NET WebApi 跨域设置>的文章,主要针对 ASP.NET WebApi 项目. 今天遇到 ASP.NET MVC 项目也需要设置跨域,否则浏览器 ...

  9. 关于@Autowired和@Resource注解区别

    区分一下@Autowired和@Resource两个注解的区别: 1.@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配 2.@A ...

  10. VMware 安装Linux (以CentOS7-2009为例)

    1.VMware下载安装 链接:https://pan.baidu.com/s/11Y-AFB3aaAFxafdGPw4zaw 提取码:hskj 2.CentOS镜像官网下载:https://www. ...