08 javaScript执行上下文和执行上下文栈

在这篇文章中,我将深入探讨JavaScript中一个最基本的部分。 在本文结束时,您应该更清楚地知道解释器是怎么工作的,为什么某些函数/变量在声明之前就可以使用以及它们的值是如何确定的。

一:什么是执行上下文?

当JavaScript代码运行的时候,确定它运行所在的环境是非常重要的。运行环境由下面三种不同的代码类型确定

  • 全局代码(Global Code):代码首次执行时候的默认环境
  • 函数代码(Function Code):每当执行流程进入到一个函数体内部的时候
  • Eval代码(Eval Code):当eval函数内部的文本执行的时候

您可以在网上找到大量关于scope的参考资料。

image

在上图中,我们有1个全局上下文(Global Context),使用紫色边框表示;有3个不同的函数上下文(Function Context)由绿色,蓝色,和橙色边框表示。注意!全局上下文有且只有一个,程序中其他任意的上下文都可以访问全局上下文。
你可以拥有任意数量的函数上下文。每一次函数调用都会创建一个新的上下文,它会创建一个私有域,函数内部做出的所有声明都会放在这个私有域中,并且这些声明在当前函数作用域外无法直接访问。在上面的例子中,一个函数可以访问它所在的上下文尾部的变量,但是一个外部的上下文无法访问内部函数内部声明的变量/函数。为什么会发生这样的情况?代码究竟是如何被解析的呢?

二:执行上下文栈

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

image

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

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

image

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

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

三:执行上下文详解

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

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

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

作者:李永奇
链接:https://www.jianshu.com/u/960598148ccd
来源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(08 javaScript执行上下文和执行上下文栈)