写点什么

浏览器工作原理和 V8 引擎

作者:CRMEB
  • 2022 年 3 月 11 日
  • 本文字数:3053 字

    阅读完需:约 10 分钟

浏览器工作原理和V8引擎

一、浏览器的工作原理



比如在浏览器中输入网址,然后 dns 进行解析,解析出的就是服务器的一个 ip 地址。服务器返回一个 html 文件,浏览器内核在解析 html 文件的过程中,遇到 link 标签和 script 标签引用的 css 文件和 JavaScript 文件就会去下载下来。

二、浏览器内核

1. 我们经常会说:不同的浏览器有不同的内核组成:

  • Gecko:早期被 Netscape 和 Mozilla Firefox 浏览器浏览器使用;

  • Trident:微软开发,被 IE4~IE11 浏览器使用,但是 Edge 浏览器已经转向 Blink;

  • Webkit:苹果基于 KHTML 开发、开源的,用于 Safari,Google Chrome 之前也在使用;

  • Blink:是 Webkit 的一个分支,Google 开发,目前应用于 Google Chrome、Edge、Opera 等;

  • 等等...

2. 事实上,我们经常说的浏览器内核指的是浏览器的排版引擎:

  • 排版引擎(layout engine),也称为浏览器引擎(browser engine)、页面渲染引擎(rendering engine) 或样版引擎

三、浏览器渲染过程



浏览器内核的 HTML Parse 将 HTML 转化为 DOM 树(DOM Tree),DOM 的 JavaScript 代码可以对 DOM 树(DOM Tree)进行操作(JavaScript 代码是由 JavaScript 引擎执行的)。CSS Parse 将 css 转化为 CSS 规则(Style Rules)。然后 DOM 树(DOM Tree)和 CSS 规则(Style Rules)通过附加(Attachment)生成渲染树(Render Tree),在 布局引擎(Layout)具体操作下,进行绘制(Painting),浏览器就可以进行展示(Dispaly)。之所以需要布局引擎(Layout),是因为浏览器在不同状态下布局有所不同。

四、认识 JavaScript 引擎

1. 为什么需要 JavaScript 引擎呢?

  • 我们前面说过,高级的编程语言都是需要转成最终的机器指令来执行的;

  • 事实上我们编写的 JavaScript 无论你交给浏览器或者 Node 执行,最后都是需要被 CPU 执行的;

  • 但是 CPU 只认识自己的指令集,实际上是机器语言,才能被 CPU 所执行;

  • 所以我们需要 JavaScript 引擎帮助我们将 JavaScript 代码翻译成 CPU 指令来执行;

2. 比较常见的 JavaScript 引擎有哪些呢?

  • SpiderMonkey:第一款 JavaScript 引擎,由 Brendan Eich 开发(也就是 JavaScript 作者);

  • Chakra:微软开发,用于 IT 浏览器;

  • JavaScriptCore:WebKit 中的 JavaScript 引擎,Apple 公司开发;

  • V8:Google 开发的强大 JavaScript 引擎,也帮助 Chrome 从众多浏览器中脱颖而出;

  • 等等…

3. JavaScript 是一门高级编程语言:

  • 机械语言————>汇编语言————>高级语言

五、浏览器内核和 JS 引擎的关系

这里我们先以 WebKit 为例,WebKit 事实上由两部分组成的:

  • WebCore:负责 HTML 解析、布局、渲染等等相关的工作;

  • JavaScriptCore:解析、执行 JavaScript 代码;

另外一个强大的 JavaScript 引擎就是 V8 引擎

六、V8 引擎原理

1. 我们来看一下官方对 V8 引擎的定义:

  • V8 是用 C ++编写的 Google 开源高性能 JavaScript 和 WebAssembly 引擎,它用于 Chrome 和 Node.js 等。

  • 它实现 ECMAScript 和 WebAssembly,并在 Windows 7 或更高版本,macOS 10.12+和使用 x64,IA-32, ARM 或 MIPS 处理器的 Linux 系统上运行。

  • V8 可以独立运行,也可以嵌入到任何 C ++应用程序中。



2. V8 引擎架构

  • Parse 模块会将 JavaScript 代码转换成 AST(抽象语法树),这是因为解释器并不直接认识 JavaScript 代码

如果函数没有被调用,那么是不会被转换成 AST 的。PreParse(预解析),并不是一开始所有代码都需要执行,所以 V8 引擎就实现了 Lazy Parsing(延迟解析)的方案,它的作用是将不必要的函数进行预解析,也就是只解析暂 时需要的内容,而对函数的全量解析是在函数被调用时才会进行;

  • Ignition 是一个解释器,会将 AST 转换成 ByteCode(字节码)

同时会收集 TurboFan 优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算); 如果函数只调用一次,Ignition 会执行解释执行 ByteCode;

  • TurboFan 是一个编译器,可以将字节码编译为 CPU 可以直接执行的机器码

如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过 TurboFan 转换成优化的机器码,提高代码的执行性能; 但是,机器码实际上也会被还原为 ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如 sum 函数原来执行的是 number 类型,后来执行变成了 string 类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码。

七、执行上下文

<script>        var name = 'why'                foo(123)        function foo (num) {            console.log(m)            var m = 10            var n = 20                        function bar () {                console.log(name)            }            bar()                    }</script>复制代码
复制代码

1. 全局代码执行前的解析(红色框内)

 


2. 全局代码执行和 foo 函数执行体执行前的解析(红色框内)



3. foo 函数执行体执行(红色框内)



4. bar 函数执行体执行前的解析



5. bar 函数执行体执行

 


因为 bar 函数内无 name 属性,所以向上到父级作用域中找 (看函数定义时的位置,其所在的上一层作用域为父级作用域,不时看调用位置)。如果在 GO 或之前找到,则输出 name 值,否则报出 undefined。

  • bar 函数执行体执行完后,则函数执行上下文(FEC)退出 ECS 执行上下文栈;foo 函数执行体执行完后;函数执行上下文(FEC)一样退出 ECS 执行上下文栈。

  • 基于早期 ECMA 的版本规范:每一个执行上下文会被关联到一个变量对象(variable object,VO),在源代码中的变量和函数声明会被作为属性添加到 VO 中。对与函数来说,参数也会被添加到 VO 中。

  • 在最新的 ECMA 的版本规范中,对于一些词汇进行了修改:每一个执行上下文会被关联到一个变量环境(VariableEnvironment),在执行代码中的变量和函数声明会被作为环境记录(Environment Record)添加到变量环境中。对与函数来说,参数也会被环境记录添加到变量环境中。

八、作用域提升面试题

建议:要是像我一样这种基础薄弱的人来说,做题时还是建议画一下执行上下文来理解,这样更加加深印象和理解!!!

  1.  

<script>        var n = 100
function foo () { n = 100 } foo()
console.log(n)//100</script>复制代码
复制代码


<script>        function foo () {            console.log(n)//undefined            var n = 200            console.log(n)//200        }
var n = 100 foo()</script>复制代码
复制代码


<script>        var n = 100
function foo1 () { console.log(n)//100 }
function foo2 () { var n = 200 console.log(n)//200 foo1() }
foo2() console.log(n)//100</script>复制代码
复制代码


<script>        var n = 100
function foo () { console.log(n) return var n = 100//undefined }
foo()</script>复制代码
复制代码


<script>        function foo () {            var a = b = 100            // b=100(该赋值语句在该函数作用域到全局作用域之中找不到,则添加到全局作用域中)            // var a=100        }
foo()
console.log(a)//报错 a is not defined console.log(b)//100</script>
复制代码

最后

如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star:http://github.crmeb.net/u/defu不胜感激 !

PHP 学习手册:https://doc.crmeb.com

技术交流论坛:https://q.crmeb.com

用户头像

CRMEB

关注

还未添加个人签名 2021.11.02 加入

CRMEB就是客户关系管理+营销电商系统实现公众号端、微信小程序端、H5端、APP、PC端用户账号同步,能够快速积累客户、会员数据分析、智能转化客户、有效提高销售、会员维护、网络营销的一款企业应用

评论

发布
暂无评论
浏览器工作原理和V8引擎_CRMEB_InfoQ写作平台