ECMA-262-3 详解:1、执行上下文
转行来的前端,从来没有深入了解ECMA,网上找了一下,发现早在2010年就有大佬 Dmitry Soshnikov 总结了ECMA中的核心内容,我这里只是翻译记录,加深自己的印象。文章原文来自 ECMA-262-3 in detail. Chapter 1. Execution Contexts
介绍
这篇文章中将会涉及到ECMAScript的执行上下文和与之相关的可执行代码类型。
定义
每一次当控件进入ECMA的可执行代码区域,那么控件就进入到了执行上下文。
执行上下文(缩写为-EC)是ECMA-262规范用于可执行代码的典型和区分的抽象概念。
从技术实施的角度来看,这个标准并没有定义EC的准确结构和类型。这是ECMAScript引擎实施标准的问题。
从逻辑上来说,一组活动的执行上下文形成一个堆栈。栈底始终都是一个全局上下文,栈顶则是当前(活动)执行上下文。在进入和退出各种EC的时候修改(推入/推出)堆栈。
可执行代码的种类
对于可执行上下文的抽象概念,可执行代码的类型的概念是与之相关的。说道代码类型,在某些时候,是可以表示执行上下文的。
例如,我们将执行上下栈定义为一个数组
1 | ECStack = []; |
每一次进入一个函数的时候(即使这个函数是一个递归函数或者是一个构造函数),以及内置的 eval
函数工作时,堆栈都会被推入。
全局代码
这类代码在程序初始化的时候执行:例如加载外部的js文件或者通过本地的内联js代码(包含在 <script></script>
中的代码)。全局代码不包含任何函数体中的代码。
初始的时候(程序开始运行的时候), ECStack
看起来是这样:
1 | ECStack = [ |
函数代码(功能代码)
当进入函数代码时候(各种函数,构造函数,递归,IIFE,eval等), ECStack
推入一个新的元素。需要注意的是,具体函数的代码不包括内部函数的代码。
举个例子,我们运行一个一次递归的函数
1 | (function foo(flag){ |
然后,对 ECStack 进行如下修改:
1 | // 第一次运行 foo 函数,即 foo 的立即执行 |
函数的每个返回(可以理解为这段函数执行完成)都会退出当前的执行上下文,并且 ECStack
进行相对应的退出,这是堆栈的客观规律。上面这段代码(包含一次递归)执行完成后, ECStack
将再次只包含 globalContext
,知道程序结束。
抛出但未捕获的异常也可能会导致一个或者多个执行上下文退出。
1 | (function foo(){ |
虽然上面的代码是推出了栈,但是,程序出错,可能就不执行了。
Eval
代码
eval
代码让事情变得更加有趣了。在使用 eval
的情况下,关于 调用上下文 的概念产生了。即,在一个上下文中调用了 eval
函数。
由 eval
进行的操作,比如定义一个函数或者函数声明的时候会直接影响了调用上下文。
1 | // 影响了全局上下文 |
Note:在 ES5
的严格模式中, eval
不会影响调用上下文,取而代之的是,会对沙箱代码进行评估。
上面的例子会对 ECStack
进行如下修改:
1 | ESStack = [ |