「四」浏览器中js引擎解析过程(看完秒懂!)

作者 : 开心源码 本文共4249个字,预计阅读时间需要11分钟 发布时间: 2022-05-13 共132人阅读

我们前几章和讲解了什么浏览器的组成部分以及渲染引擎,今天我们主要讲一下js引擎的相关知识点,那么在开讲之前我们需要回顾一下有关渲染引擎的相关知识点

渲染引擎

关键渲染路径是指浏览器从最初接收请求来的HTML、CSS、javascript等资源,而后解析、构建树、渲染布局、绘制,最后呈现给用户能看到的界面这整个过程。

image.png

JavaScript引擎

JavaScript引擎是一个专门解决JavaScript脚本的虚拟机,一般会附带在网页浏览器中。JavaScript引擎从头到尾负责整个JavaScript程序的编译和执行过程。js的引擎有很多种,而最为大家熟知的无疑是V8引擎,他用于Chrome浏览器和Node中。

2019-06-19-13-00-37

V8引擎由两个主要部件组成:

emory Heap(内存堆)?—?内存分配地址的地方
Call Stack(调用堆栈) — 代码执行的地方

上面只是简单的对js引擎进行一下基本的理解,下面开始正式详情js引擎的执行过程(以V8引擎为例)

js引擎执行过程

全面分析js引擎的执行过程,主要分为三个阶段:

1. 语法分析
2. 预编译阶段
3. 执行阶段

下面着重讲一下这三个阶段:

语法分析

分析该js脚本代码块的语法能否正确,假如出现不正确,则向外抛出一个语法错误(SyntaxError),中止该js代码块的执行,而后继续查找并加载下一个代码块;假如语法正确,则进入预编译阶段

预编译阶段

js代码块通过语法分析阶段之后,语法都正确的下回进入预编译阶段。
在分析预编译阶段之前,我们先来理解一下js的运行环境,运行环境主要由三种:
1、全局环境(js代码加载完毕后,进入到预编译也就是进入到全局环境)
2、函数环境(函数调用的时候,进入到该函数环境,不同的函数,函数环境不同)
3、eval环境(不建议使用,存在安全、性能问题)

每进入到一个不同的运行环境都会创立一个相应的执行上下文(execution context)「下文会详情」,那么在一段js程序中一般都会创立多个执行上下文,js引擎会以栈的数据结构对这些执行进行解决,形成函数调用栈(call stack),栈底永远是全局执行上下文(global execution context),栈顶则永远时当前的执行上下文
注意:执行上下文的相关概念会在下面进一步进行详细详情

执行阶段

在执行阶段,我们暂时先不考虑异步(由于异步阶段涉及到的知识点是事件循环【event loop】),等读完这篇文章之后并且了解之后,去看我写的深入了解事件循环这篇文章,就会进一步了解。

我们上文讲到V8引擎由两个主要部件组成:
emory Heap(内存堆)?—?内存分配地址的地方
Call Stack(调用堆栈)— 代码执行的地方

我们公告的函数与变量被储存在『内存堆』中,而当我们要执行的时候,就必需借助于『调用栈』来处理问题。函数调用栈就是使用栈存取的方式进行管理运行环境,特点是先进后出,后进后出

我们来分析一下js代码来了解函数调用栈:

function bar() {    var B_context = "bar saucxs";    function foo() {        var f_context = "foo saucxs";    }    foo()}bar()

上面代码块通过语法分析后,进入预编译阶段创立执行上下文,如图所示

image.png

1、首先进入到全局环境,创立全局执行上下文(global Execution Context ),推入到stack中;
2、调用bar函数,进入bar函数运行环境,创立bar函数执行上下文(bar Execution Context),推入stack栈中;
3、在bar函数内部调用foo函数,则再进入到foo函数运行环境中,创立foo函数执行上下文(foo Execution Context),如上图,因为foo函数内部没有再调用其余函数,那么则开始出栈;
5、foo函数执行完毕之后,栈顶foo函数执行上下文(foo Execution Context)首先出栈;
6、bar函数执行完毕,bar函数执行上下文(bar Execution Context)出栈;
7、全局上下文(global Execution Cntext)在浏览器或者者该标签关闭的时候出栈。

说明:不同的运行环境执行都会进入到代码预编译和执行两个阶段,语法分析则在代码块加载完毕时统一检查语法。

上面的就是我们简单的对一段代码进行分析的过程,下面,我们讲一下在预编译阶段提到的执行上下文

执行上下文

执行上下文可了解为当前的执行环境,与该运行环境相对应.前面我们提到过,JavaScript中有三种可执行代码块,当然也对应着三种执行上下文。

  • 全局执行上下文
    这是基础上下文,任何不在函数内部的代码都在全局上下文中。一个程序中只会有一个全局执行上下文。它会执行两件事:创立一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。

  • 函数执行上下文
    每当一个函数被调用时, 都会为该函数创立一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创立的。函数上下文可以有任意多个。每当一个新的执行上下文被创立。

  • Eval 执行上下文
    执行在 eval 内部的代码也会有它属于自己的执行上下文,除非你想搞黑魔法,不然不要轻易使用它。

执行上下文分为两个阶段:
  • 创立阶段
  • 执行阶段

我们主要探讨创立阶段,执行阶段的主要工作就是分配变量

创立阶段

创立执行上下文的过程中,主要是做了下面三件事,如图所示:

1、确定 this 的值,也被称为 This Binding。
2、LexicalEnvironment(词法环境) 组件被创立。
3、VariableEnvironment(变量环境) 组件被创立。

This Binding
  • 全局执行上下文中,this 的值指向全局对象,在浏览器中this 的值指向 window对象,而在nodejs中指向这个文件的module对象。

  • 函数执行上下文中,this 的值取决于函数的调用方式。具体有:默认绑定、隐式绑定、显式绑定(硬绑定)、new绑定、箭头函数,具体内容请参考JavaScript深入之史上最全–5种this绑定全面解析这篇文章

词法环境有两个组成部分
  • 1、环境记录:存储变量和函数公告的实际位置

  • 2、对外部环境的引用:可以访问其外部词法环境

词法环境有两种类型

  • 1、全局环境:是一个没有外部环境的词法环境,其外部环境引用为 null。拥有一个全局对象(window 对象)及其关联的方法和属性(例如数组方法)以及任何客户自己设置的全局变量,this 的值指向这个全局对象。

  • 2、函数环境:客户在函数中定义的变量被存储在环境记录中,包含了arguments 对象。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。

直接看伪代码可能更加直观

GlobalExectionContext = {  // 全局执行上下文  LexicalEnvironment: {       // 词法环境    EnvironmentRecord: {        // 环境记录      Type: "Object",              // 全局环境      // 标识符绑定在这里       outer: <null>                // 对外部环境的引用  }  }FunctionExectionContext = { // 函数执行上下文  LexicalEnvironment: {       // 词法环境    EnvironmentRecord: {        // 环境记录      Type: "Declarative",         // 函数环境      // 标识符绑定在这里             // 对外部环境的引用      outer: <Global or outer function environment reference>    }  }
变量环境

变量环境也是一个词法环境,因而它具备上面定义的词法环境的所有属性。
在 ES6 中,词法 环境和 变量 环境的区别在于前者用于存储函数公告和变量( letconst绑定,然后者仅用于存储变量( var绑定。

使用例子进行详情

let a = 20;  const b = 30;  var c;function multiply(e, f) {   var g = 20;   return e * f * g;  }c = multiply(20, 30);

执行上下文如下所示

GlobalExectionContext = {  ThisBinding: <Global Object>,  LexicalEnvironment: {      EnvironmentRecord: {        Type: "Object",        // 标识符绑定在这里        a: < uninitialized >,        b: < uninitialized >,        multiply: < func >      }      outer: <null>    },  VariableEnvironment: {      EnvironmentRecord: {        Type: "Object",        // 标识符绑定在这里        c: undefined,      }      outer: <null>    }  }FunctionExectionContext = {    ThisBinding: <Global Object>,  LexicalEnvironment: {      EnvironmentRecord: {        Type: "Declarative",        // 标识符绑定在这里        Arguments: {0: 20, 1: 30, length: 2},      },      outer: <GlobalLexicalEnvironment>    },  VariableEnvironment: {      EnvironmentRecord: {        Type: "Declarative",        // 标识符绑定在这里        g: undefined      },      outer: <GlobalLexicalEnvironment>    }  }

变量提升的起因:在创立阶段,函数公告存储在环境中,而变量会被设置为 undefined(在 var 的情况下)或者保持未初始化(在 letconst 的情况下)。所以这就是为什么可以在公告之前访问 var 定义的变量(虽然是 undefined ),但假如在公告之前访问 letconst 定义的变量就会提醒引用错误的起因。这就是所谓的变量提升。

参考文档

https://www.cxymsg.com/guide/mechanism.html#javascript%E7%9A%84%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83
https://segmentfault.com/a/1190000017812175
https://juejin.im/post/5dde27615188256ebd1618fb
https://muyiy.cn/blog/1/1.1.html

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 「四」浏览器中js引擎解析过程(看完秒懂!)

发表回复