关于v8:浏览器工作原理和V8引擎

42次阅读

共计 3518 个字符,预计需要花费 9 分钟才能阅读完成。

一、浏览器的工作原理

比方在浏览器中输出网址,而后 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 等;
等等 …

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

排版引擎(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 指令来执行;

  1. 比拟常见的 JavaScript 引擎有哪些呢?

SpiderMonkey:第一款 JavaScript 引擎,由 Brendan Eich 开发(也就是 JavaScript 作者);
Chakra:微软开发,用于 IT 浏览器;
JavaScriptCore:WebKit 中的 JavaScript 引擎,Apple 公司开发;
V8:Google 开发的弱小 JavaScript 引擎,也帮忙 Chrome 从泛滥浏览器中怀才不遇;
等等…

  1. 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 ++ 应用程序中。

  1. 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. 全局代码执行前的解析(红色框内)

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

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

bar 函数执行体执行完后,则函数执行上下文 (FEC) 退出 ECS 执行上下文栈;foo 函数执行体执行完后;函数执行上下文 (FEC) 一样退出 ECS 执行上下文栈。
基于晚期 ECMA 的版本标准:
每一个执行上下文会被关联到一个变量对象(variable object,VO),在源代码中的变量和函数申明会被作为属性增加到 VO 中。对与函数来说,参数也会被增加到 VO 中。
在最新的 ECMA 的版本标准中,对于一些词汇进行了批改:
每一个执行上下文会被关联到一个变量环境(VariableEnvironment),在执行代码中的变量和函数申明会被作为环境记录(Environment Record)增加到变量环境中。对与函数来说,参数也会被环境记录增加到变量环境中。
八、作用域晋升面试题
倡议:要是像我一样这种基础薄弱的人来说,做题时还是倡议画一下执行上下文来了解,这样更加加深印象和了解!!!

<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

正文完
 0