美文网首页
JavaScript 中的执行上下文和执行栈

JavaScript 中的执行上下文和执行栈

作者: 时间的溺水者 | 来源:发表于2020-11-17 22:35 被阅读0次

    什么是执行上下文?

    执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。

    JavaScript 中有三种执行上下文类型。

    1、全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
    2、函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
    3、val 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。

    执行栈

    执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文。

    当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。

    引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。

    代码示例来理解:

    let a = 'Hello World!';
    
    function first() {
      console.log('Inside first function');
      second();
      console.log('Again inside first function');
    }
    
    function second() {
      console.log('Inside second function');
    }
    
    first();
    console.log('Inside Global Execution Context');
    
    上述代码的执行上下文栈

    当上述代码在浏览器加载时,JavaScript 引擎创建了一个全局执行上下文并把它压入当前执行栈。当遇到 first() 函数调用时,JavaScript 引擎为该函数创建一个新的执行上下文并把它压入当前执行栈的顶部。

    当从 first() 函数内部调用 second() 函数时,JavaScript 引擎为 second() 函数创建了一个新的执行上下文并把它压入当前执行栈的顶部。当 second() 函数执行完毕,它的执行上下文会从当前栈弹出,并且控制流程到达下一个执行上下文,即 first() 函数的执行上下文。

    当 first() 执行完毕,它的执行上下文从栈弹出,控制流程到达全局执行上下文。一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文。

    怎么创建执行上下文?

    创建执行上下文有两个阶段:1、 创建阶段 和 2、 执行阶段

    在 JavaScript 代码执行前,执行上下文将经历创建阶段。在创建阶段会发生三件事:

    a、**this 值的决定,即我们所熟知的 This 绑定。**
    b、**创建词法环境组件。**
    c、**创建变量环境组件。**
    

    所以执行上下文在概念上表示如下:

    ExecutionContext = {
      ThisBinding = <this value>,
      LexicalEnvironment = { ... },
      VariableEnvironment = { ... },
    }
    
    This 绑定
    在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。
    
    在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)。例如
    
    let foo = {
      baz: function() {
        console.log(this);
      }
    }
    
    foo.baz();   // 'this' 引用 'foo', 因为 'baz' 被
                 // 对象 'foo' 调用
    
    let bar = foo.baz;
    
    bar();       // 'this' 指向全局 window 对象,因为
                 // 没有指定引用对象
    
    
    词法环境
    **es6定义**:词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用外部词法环境的空值组成。
    
    简单来说词法环境是一种持有标识符—变量映射的结构。(这里的标识符指的是变量/函数的名字,而变量是对实际对象[包含函数类型对象]或原始数据的引用)。
    
    现在,在词法环境的内部有两个组件:(1) 环境记录器和 (2) 一个外部环境的引用
    
        1、环境记录器是存储变量和函数声明的实际位置。
        2、外部环境的引用意味着它可以访问其父级词法环境(作用域)。
    

    词法环境有两种类型:

      - 全局环境(在全局执行上下文中)是没有外部环境引用的词法环境。全局环境的外部环境引用是 null。它拥有内建的 Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且 this的值指向全局对象。
      - 在函数环境中,函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数。
    

    环境记录器也有两种类型:

    1.声明式环境记录器存储变量、函数和参数。
     2.对象环境记录器用来定义出现在全局上下文中的变量和函数的关系。
    

    简而言之,

    在全局环境中,环境记录器是对象环境记录器。
    在函数环境中,环境记录器是声明式环境记录器。
    
    注意 — 对于函数环境,声明式环境记录器还包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射)和传递给函数的参数的 length。
    

    变量环境:

    它同样是一个词法环境,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系。
    如上所述,变量环境也是一个词法环境,所以它有着上面定义的词法环境的所有属性。
    在 ES6 中,词法环境组件和变量环境的一个不同就是前者被用来存储函数声明和变量(let 和                   
    const)绑定,而后者只用来存储 var 变量绑定。
    

    执行阶段

    在此阶段,完成对所有这些变量的分配,最后执行代码。
    
    在ES6中,词法组件和变量环境组件之间的区别是前者用于存储函数声明和变量(let和const)绑定,而后者仅用于存储变量var绑定。
    
    说说变量提升的原因,在创建阶段,函数声明存储在环境中,而变量会被设置为undefined或保持未初始化。
    
      这就是为什么可以在声明之前访问var定义的变量,但如果在声明之前访问let和const定义的变量就会提升引用错误的原因。
    
    
    ```
    var da1, da2 = 1;
     function foo() {
            var da3, da4;
      };
      foo();
    
    
          js在执行这段代码时,创建了一个词法环境(global environment- ge),确定(ge)的环境记录,里面包含了da1,da2,foo标识符的记录,设置外部词法环境的引用,因为(ge)已经在最外面了,外部词法环境引用就是Null,到此(ge)就确立完毕了。
    
    
          接着执行代码,当执行到foo(),js调用了foo函数,foo函数是一个(FunctionDeclaration),js开始执行函数创建了一个新的词法环境表示为(ge2),设置(ge2)的外部词法环境引用,很明显就是(ge),(ge2)的环境记录(da3,da4)。
        在执行阶段,如果 JavaScript 引擎不能在源码中声明的实际位置找到 let 变量的值,它会被赋值为 undefined。
    
    

    相关文章

      网友评论

          本文标题:JavaScript 中的执行上下文和执行栈

          本文链接:https://www.haomeiwen.com/subject/huuhiktx.html