JS引擎(V8)在解析JS代码时做了哪些事情

前言

作为一个前端工程师,少不了每天跟浏览器打交道,跟浏览器打交道的媒介就是通过JS,那么JS是怎么被浏览器解析的呢,这个时候,背后的V8引擎就在默默的为浏览器做这件事情,V8引擎具体会做那些事情呢?

客户端请求网页

当客户端请求网页时,服务器首先会返回一个index.html(大多数情况下都是叫index.html,但也有可能叫main.html),当浏览器拿到这个请求的html文件后会解析html文件,随后会根据html文件引入的css和js地址,去请求js文件和css文件。

以webkit为例,事实上webkit由两部分组成

  1. WebCore:负责html解析、布局、渲染等等相关的工作。
  2. JavascriptCore:解析、执行JavaScript代码。

所以html和css文件是由WebCore去解析并生成render tree 的

生成Render tree的过程

可以看到浏览器解析HTML文件的时候会将HTML转换生成一个DOM tree 但是这个DOM tree会受到JS的DOM操作影响,所以解析HTML的时候如果遇到JS代码则会先解析JS代码(JS代码则由JavasciptCore解析),也可以通过设置scirpt标签熟悉改变解析JS代码的时机(defer和async)。

defer和async都是用于控制浏览器在解析HTML文档时如何处理script标签的属性。它们的主要区别在于:

  1. defer属性:告诉浏览器该脚本将在文档解析完毕后执行,但在DOMContentLoaded事件触发之前执行。多个defer脚本会按照它们在文档中出现的顺序依次执行。这个属性只适用于外部脚本,或带有src属性的内部脚本。
  2. async属性:告诉浏览器该脚本将与文档同时解析和执行,不会阻塞HTML的解析。多个async脚本的执行顺序无法保证。这个属性只适用于外部脚本,或带有src属性的内部脚本。

解析CSS时,会将CSS转换成CSSOM规则,随后会将DOM tree和CSSOM tree 合并生成一个Render tree 随后进行布局,渲染展示。 这里需要注意,布局改变会影响Render tree,所以回流的时候会改变Render tree,但是重绘不会。

解析JS代码

这里我就以V8引擎为例,V8引擎是用来解析JS代码,JS代码运行时所需要的环境,浏览器和node中都存在V8引擎。

根据上边的流程图可以看出,当浏览器拿到javascript代码后先会进行parser,在这个环节会对JS代码进行语法分析和词法分析然后生成AST抽象语法树,随后AST抽象语法树会经过Ignition转换成字节码。

这里为什么会生成字节码呢?这就是浏览器跨平台的关键之处,浏览器之所以能够在windows和mac上跨平台使用就是因为Ignition会将AST抽象语法树先转换成字节码,然后再由字节码转换成汇编再转换成对应的cpu指令,因为各个系统平台的cpu指令是不一样的,所以我们不能直接的去转换成cpu指令,需要先转换成字节码,然后再根据运行的系统去转换对应平台的cpu指令(这里有点像Java的JVM的味道)。

但是,所有的代码都通过字节码去转换成对应的机器码(cpu指令)肯定会造成冗余的性能问题,所以就会出现TurboFan。

比如现在有一个这样的函数

js 复制代码
function foo(){
    console.log('1in')
}

这个函数会执行很多次,那我们每次执行都需要去用字节码转换的话是不是会浪费很多性能,我们只需要将他转换的机器码保存下来,下一次调用的时候直接拿来机器码调用就好了,所以Ignition会在解析AST抽象语法树的时候,会将多次执行的函数标记成hot函数告诉TurboFan,hot函数转换的机器码就会被保存下来,当下一次调用时直接调用保存下来的机器码,不会再一次通过字节码转换。

但是,如果hot函数的参数类型发生改变,就会重新生成字节码进而重新生成机器码,不会复用上一次缓存的,比如

js 复制代码
function sum(value1,value2){
    return value1+value2
}

//如果是这样的数字类型相加的函数连续复用的话,机器码会复用
const reslut1=sum(1,2)

const result2=sum(10,20)

//但是如果中途有这样的一个函数
const result3=sum('1','in')

像上边所提到的result3就不会复用之前的缓存的机器码,因为之前缓存的是数字类型的相加,而result3的相加是字符串的拼接,区别很大,所以需要重新生成字节码。

从上边可以看出,我们如果用TypeScript编写代码的话,约束了函数的入参类型后,无形中就会增加V8解析执行JS代码的效率。

相关推荐
蟾宫曲2 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心2 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029402 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
魏时烟3 小时前
css文字折行以及双端对齐实现方式
前端·css
哥谭居民00014 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
踢足球的,程序猿4 小时前
Android native+html5的混合开发
javascript
2401_882726484 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203984 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github
胡西风_foxww4 小时前
【ES6复习笔记】迭代器(10)
前端·笔记·迭代器·es6·iterator