美文网首页
深入浅出执行上下文、词法环境、变量环境

深入浅出执行上下文、词法环境、变量环境

作者: 灯下草虫鸣314 | 来源:发表于2020-11-20 19:52 被阅读0次

    执行上下文的概念

    执行上下文:javascript 代码解析和执行时所在的环境。

    执行上下文的类型

    执行上下文分为三种类型:

    1.全局执行上下文

    • js代码开始运行后。首先进入全局执行上下文环境中,不在任何函数中的js代码都会在全局执行上下稳重
    • 一个js程序中只存在一个全局执行上下文。创建时会压人栈底,只有当程序结束时才会弹出
    • 全局执行上下文会做两件事。1.创建全局对象,2.将this指向这个全局对象
    • 浏览器环境中全局对象是window, 在node环境中全局对象是global

    2.函数执行上下文

    • 函数每次调用都会产生一个新的函数执行上下文,每个函数都拥有自己的执行上下文,但是只有调用的时候才会被创建
    • 函数执行上下文的生命周期分为两个阶段。创建和执行

    3.Eval执行上下文

    • eval函数执行时产生的执行上下文。

    执行上下文栈

    执行上下文栈是一个后进先出的数据结构,
    具体执行流程如下

    • 首先创建全局执行上下文, 压入栈底
    • 每当调用一个函数时,创建函数的函数执行上下文。并且压入栈顶
    • 当函数执行完成后,会从执行上下文栈中弹出,js引擎继续执栈顶的函数。

    如以下函数执行时的执行栈变化为:

    function fun1(){
        console.log('func1')
        fun2()
    }
    function fun2(){
        console.log('func2')
    }
    fun1()  
    /*
    *                     fun2
    *           fun1      fun1       fun1   
    * global => global => global => global => global
    */
    

    执行上下文生命周期

    变量对象VO和活动对象AO

    在讲生命周期钱。我们必须了解讲个对象,变量对象VO和活动对象AO

    变量对象VO:

    • 用来存储执行上下文中可以被访问。但不能被delete的函数标识。
    • 包括:arguments 对象,形参实参键值对,函数声明,变量声明和this
      活动对象AO:
    • 他可以被理解为当函数被激活调用时创建的对象。用来访问VO对象中存储的那些个标识。
      全局上下文中变量对象:
    • 就是全局对象
    • 初始化时:初始化一系列原始属性:Math,String,Date,Window等
    • 在浏览器中window对象引用全局对象,全局环境中this也引用自身

    创建阶段

    此阶段执行上下文会执行以下操作

    • 创建作用域链
    • 通过变量对象VO创建活动AO,
      • 首先创建arguments对象
      • 创建形参实参的键值对
      • 创建函数声明 (经典面试题:为什么函数声明提前?)
      • 创建变量声明 (经典面试题:为什么变量声明提升?)
    • 创建this

    执行阶段

    • 变量赋值。函数引用,执行其他代码逻辑
    • 当执行完毕后。执行上下文出栈,等待垃圾回收机制回收

    举例说明

    function a(name, age){
        var a = 1
        function c(){}
        var d = funciton (){}
        (function e(){})
        var f = function g(){}
    }
    a('John')
    // 如上代码。
    //在创建预编译阶段生成的AO对象如下
    AO = {
        arguments:{
            0: 'John',
            1: undefined,
            length: 2
        },
        name: 'John',
        age: undefined,
        c: reference to function c(){},
        a: undefined,
        d: undefined,
        f: undefined,
    }
    // 函数表达式 e,不在AO中 
    // 函数g不在AO中
    
    // 函数执行时AO对象如下
    AO = {
        arguments:{
            0: 'John',
            1: undefined,
            length: 2
        },
        name: 'John',
        age: undefined,
        c: reference to function c(){},
        a: 1,
        d: reference to FunctionExpression "d",
        f: reference to FunctionExpression "f",
    }
    

    函数声明提前

    从AO对象的创建过程我们就可以发现,AO对象想是先扫描函数体内的函数声明才去扫描变量声明。所以这也就是为啥会有声明提前。

    变量提升

    AO对象创建时已经将函数内部的变量提前扫描声明。是指在函数执行的过程中开始依次赋值。

    词法环境和变量环境

    在ES6中提出词法环境和变量环境两个概念。主要是执行上下文创建过程。

    词法环境(LexicalEnvironment)

    词法环境是一种包含 标识符 => 变量 隐射关系的一种结构。

    在词法环境中有两个组成部分:

    • 环境记录(EnvironmentRecord): 储存变量和函数声明的实际位置
    • 对外部环境的引用(Outer):当前可以访问的外部词法环境

    词法环境分为两种类型:

    • 全局环境: 全局执行上下文,他没有外部环境的引用,拥有一个全局对象window和关联的方法和属性eg: Math,String,Date等。还有用户定义的全局变量,并将this指向全局对象。
    • 函数环境: 用户在函数定义的变量将储存在环境记录中。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。环境记录中包含。用户声明的变量。函数。还有arguments对象。

    举例词法环境在伪代码中如下:

    GlobalExectionContent = {
      LexicalEnvironment: {
        EnvironmentRecord: {
          Type: "Object",
          // 剩余标识符
        },
        Outer: null,
      }
    }
    
    FunctionExectionContent = {
      LexicalEnvironment: {
        EnvironmentRecord: {
          Type: "Declarative",
          // 剩余标识符
        },
        Outer: [Global or outer function environment reference],
      }
    }
    

    变量环境(VariableEnvironment)

    变量环境也是一个词法环境。他具有词法环境中所有的属性
    在ES6中,LexicalEnvironment和VariableEnvironment 的区别在于前者用于存储函数声明和变量let 和 const 绑定,而后者仅用于存储变量 var 绑定。

    用以下代码举例:

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

    在预编译阶段。生成的词法环境和变量环境如下

    GlobalExectionContent = {
      LexicalEnvironment: {
        EnvironmentRecord: {
          Type: "Object",
          a: <uninitialied>,
          b: <uninitialied>,
          add: <func>
          // 剩余标识符
        },
        Outer: null,
      },
    
      VariableEnvironment: {
        EnvironmentRecord: {
          Type: "Object",
          c: undefined,
          // 剩余标识符
        },
        Outer: null,
      }
    }
    
    FunctionExectionContent = {
      LexicalEnvironment: {
        EnvironmentRecord: {
          Type: "Declarative",
          arguments: {
            0: 20,
            1: 30,
            length: 2,
          },
          e: 20,
          f: 30,
          c: reference to function c(){}
          // 剩余标识符
        },
        Outer: GlobalLexicalEnvironment,
      },
      VariableEnvironment: {
        EnvironmentRecord: {
          Type: "Declarative",
          g: undefined,
          // 剩余标识符
        },
        Outer: GlobalLexicalEnvironment,
      }
    }
    

    我们发现使用let和const声明的变量在词法环境创建时是未赋值初始值。而使用var定义的变量在变量环境创建时赋值为undefined。这也就是为什么const、let声明的变量在声明钱调用会报错,而var声明的变量不会。

    代码执行阶段

    此阶段的执行流程就是函数执行时的流程。给变量赋值,和执行其他逻辑代码。

    相关文章

      网友评论

          本文标题:深入浅出执行上下文、词法环境、变量环境

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