美文网首页
执行上下文(个人笔记)

执行上下文(个人笔记)

作者: kevision | 来源:发表于2020-07-25 13:17 被阅读0次

    本文参考:javaScript执行上下文和执行上下文栈
    一篇文章看懂JS执行上下文
    什么是执行上下文?什么是调用栈?

    一:什么是执行上下文?

    JS代码在执行前,JS引擎总要做一番准备工作,这份工作其实就是创建对应的执行上下文;

    执行上下文有且只有三类,全局执行上下文,函数上下文,与eval上下文;由于eval一般不会使用,这里不做讨论。

    当JavaScript代码运行的时候,确定它运行所在的环境是非常重要的。运行环境由下面三种不同的代码类型确定:
    全局代码(Global Code):代码首次执行时候的默认环境
    函数代码(Function Code):每当执行流程进入到一个函数体内部的时候
    Eval代码(Eval Code):当eval函数内部的文本执行的时候

    您可以在网上找到大量关于scope的参考资料。
    1.全局执行上下文

    全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的window对象,我们能通过this直接访问到它。

    image

    全局对象window上预定义了大量的方法和属性,我们在全局环境的任意处都能直接访问这些属性方法,同时window对象还是var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问。

    image image
    2.函数执行上下文

    函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。

    说到这你是否会想,上下文种类不同,而且创建的数量还这么多,它们之间的关系是怎么样的,又是谁来管理这些上下文呢,这就不得不说说执行上下文栈了。

    注意!全局上下文有且只有一个,程序中其他任意的上下文都可以访问全局上下文。你可以拥有任意数量的函数上下文。每一次函数调用都会创建一个新的上下文,它会创建一个私有域,函数内部做出的所有声明都会放在这个私有域中,并且这些声明在当前函数作用域外无法直接访问。一个函数可以访问它所在的上下文外部的变量,但是一个外部的上下文无法访问内部函数内部声明的变量/函数。为什么会发生这样的情况?代码究竟是如何被解析的呢?

    二:执行上下文栈

    执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。
    JS代码首次运行,都会先创建一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创建一个新的函数执行上下文并压入栈内;由于执行栈LIFO的特性,所以可以理解为,JS代码执行完毕前在执行栈底部永远有个全局执行上下文。

    浏览器中的JS解释器是单线程的。也就是说在浏览器中同一时间只能做一个事情,其他的action和event都会被排队放入到执行栈中(Execution Stack)。下图表示了一个单线程栈的抽象视图

    image

    如我们所知,当一个浏览器第一次load你的代码的时候,首先它会进入到一个全局执行上下文中。如果在你的全局代码中,你调用了一个函数,那么程序的执行流程会进入到被调用的函数中,并创建一个新的执行上下文,并将这个上下文推入到执行栈顶。
    如果在当前的函数中,你又调用了一个函数,那么也会执行同样的操作。执行流程计入到刚被调用的函数内部,重新创建一个新的执行上下文,并再次推入到执行栈顶。浏览器会一直执行当前栈顶的执行上下文,一旦函数执行完毕,该上下文就会被推出执行栈。下面的例子展示了一个递归函数以及该程序的执行栈:

    (function foo(i) {
      if (i === 3) {
        return;
      }
      else {
        foo(++i);
      }
    }(0));
    
    
    image

    这个代码循环调用了三次,每次对i累加1。每次函数foo调用的时候,都会有一个创建新的执行上下文。一旦上下文完成了执行,就会推出栈,将控制流返回给它下面的执行上下文,这样一直到全局上下文。

    例2:

    function f1() {
        f2();
        console.log(1);
    };
    
    function f2() {
        f3();
        console.log(2);
    };
    
    function f3() {
        console.log(3);
    };
    
    f1();//3 2 1
    

    我们通过执行栈与上下文的关系来解释上述代码的执行过程,为了方便理解,我们假象执行栈是一个数组,在代码执行初期一定会创建全局执行上下文并压入栈,因此过程大致如下:

    //代码执行前创建全局执行上下文
    ECStack = [globalContext];
    // f1调用
    ECStack.push('f1 functionContext');
    // f1又调用了f2,f2执行完毕之前无法console 1
    ECStack.push('f2 functionContext');
    // f2又调用了f3,f3执行完毕之前无法console 2
    ECStack.push('f3 functionContext');
    // f3执行完毕,输出3并出栈
    ECStack.pop();
    // f2执行完毕,输出2并出栈
    ECStack.pop();
    // f1执行完毕,输出1并出栈
    ECStack.pop();
    // 此时执行栈中只剩下一个全局执行上下文
    

    关于执行栈,有5点需要记住:

    • 单线程
    • 同步执行
    • 一个全局上下文
    • 无数的函数上下文
    • 每次函数调用都会创建一个新的执行上下文,即使是调用自身

    三:执行上下文详解

    我们已经知道每当一个函数调用发生,都会创建一个新的执行上下文。但是在JS解释器内部,每次调用一个执行上下文都分为两个步骤

    1. 创建阶段[在函数被调用,但还未执行任何代码之前]
    1. 激活/代码执行阶段
    • 分配变量,以及到函数的引用,然后解析/执行代码

    一个执行上下文从概念上可以视为一个包含三个property的Object

    链接:https://www.jianshu.com/p/12fab84daeba

    相关文章

      网友评论

          本文标题:执行上下文(个人笔记)

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