js的执行上下文

ぃ、小莉子 提交于 2020-01-08 01:00:58

js的运行环境有三种:

1. 全局环境
2. 函数内环境
3. eval环境

1. EC

Execute Context:  执行上下文。

    1)全局执行上下文

        js引擎遇到可执行的js代码,默认创建一个全局执行上下文。

    2)函数执行上下文

       js引擎遇到函数调用,会立即创建一个函数执行上下文。

执行上下文周期:

执行上下文周期分为两个阶段:

  • 创建阶段

创建阶段的任务有三个:

1. 生成变量对象(VO)

2. 建立作用域链Scope

3. 声明this指向

   this在创建阶段只是声明,在执行阶段根据函数运行时所在的执行上下文确定this指向的上下文的变量对象VO。

  • 执行阶段

当函数执行内部代码时,进入执行阶段。

2. VO / AO / GO

Variable Object: 变量对象,创建阶段的变量对象。

Active Object: 激活后的变量对象,即执行上下文执行阶段的VO。

Global Object: 全局对象,全局执行上下文对应的变量对象(AO/VO)。

每个执行上下文对象都有一个变量对象的属性,它用于存放执行上下文周期的创建阶段的形式参数、函数声明和变量声明。不能被用户直接访问。

在执行上下文周期的执行阶段,VO中对应的属性被赋值,变为AO,AO中内容是变量查找的内容。

变量对象的创建顺序如下:

  • 1. 创建arguments对象,初始化形式参数
    function a(x,y) {
      console.log(x);
    }
    a(1); // 1
    /*
    aContext: {
      AO: {
        x:1
      }
    }
    */
  • 2.查找函数声明(非函数表达式)

遍历函数声明,将其函数名称作为AO的属性,它会覆盖同名的属性。

    function a(x,y) {
      function x() { }
      console.log(x);
    }
    a(1); // function x(){}
    /*
    aContext: {
     AO: {
        x: pointer to function x
      }
    }
    */
  • 3.查找变量声明

查找遍历声明内容,将其变量名作为AO的属性。

1. 如果AO中已经有同名的属性,则该变量声明忽略。

    function a(x,y) {
      function x() { }
      var x;
      console.log(x);
    }
    a(1); // function x(){}
    /*
    aContext: {
     AO: {
        x: pointer to function x
      }
    }
    */

2. 如果有同名属性,但是如果查询阶段赋值,会覆盖前面同名属性和对应的值。

    function a(x,y) {
      function x() { }
      var x=2;
      console.log(x);
    }
    a(1); // 2
    /*
    aContext: {
      AO: {
        x: 2
      }
    }
    */

3. 如果AO中已经后同名的形式参数,变量声明也会被忽略。

    function a(x,y) {
      var x;
      console.log(x)
    }
    a(1);  // 1
    /*
    aContext: {
      AO: {
        x: 1
      }
    }
    */

3. 作用域链Scope

    function a() {
      function b() {
        function c() {
        }
      }
    }
函数声明时就会产生一个作用域,位于函数的[[Scope]]属性上,指向其所有的父执行上下文的变量对象VO。
    a[[Scope]] = [// 父作用域
      globalContext.VO
    ]
    b[[Scope]] = [
      aContext.VO,
      globalContext.VO
    ]
    c[[Scope]] = [
      bContext.VO,
      aContext.VO,
      globalContext.VO
    ]

而执行上下文的Scope属性,指向当前作用的完整的作用域链。即函数的[[Scope]]属性加上当前作用域的AO对象。

// 例如:
cContext = {
    AO:{
       arguments: {
           length: 0
       },
    },
    Scope: [AO, ..c[[Scope]]]
}

代码执行后,查找变量会根据执行上下文的作用域链开始查找,即从当前函数执行上下文的AO对象开始查找,查找不到则到上层的变量对象查找,依次查找,直到查找到全局执行上下文的变量对象为止。

4. ECS

Execute Context Stack: 执行上下文栈,也称为函数调用栈。

用于存储管理所有的执行上下文对象。

示例:

    function foo(i) {  
        if(i  == 3) {  
            return;  
        }  
        foo(i+1);  
        console.log(i);  
    }  
    foo(0);    // 运行结果    2    1    0

js引擎遇到可执行代码,默认生成全局上下文。即位于栈底。

ECS = [globalContext];

遇到函数调用,将函数入栈。

ECS.push(foo0Context);
ECS.push(foo1Context);
ECS.push(foo2Context);
ECS.push(foo3Context);

其栈结构如下:

    |------------------|
    |foo(3);console(2);|
    |------------------|
    |foo(2);console(1);|
    |------------------|
    |foo(1);console(0);|
    |------------------|   |foo(0);           |   |------------------|    |globalContext     |    |------------------|

函数执行完成后,一一出栈。最后的全局上下文在浏览器关闭的时候才会销毁。

ECS.pop();         

        

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!