代码的执行
说到代码的执行,大家的第一印象应该是一行一行的代码,一个函数一个函数的在顺序执行,但是在怪异的JavaScript中也是如此吗,我们先来看几个案例:
案例1
1 | function foo() { |
案例2
1 | function foo() { |
案例3
1 | function foo() { |
我们先来看上述三个案例的结果,对于案例1的结果应该是毫无疑问的,但是案例2、3的却与常识不相符。
原因在于,js代码在执行之前,js引擎会对代码先做分析,分析过程中把代码划分以一段一段可以执行的代码,这一段一段的可执行代码我们统称为执行上下文。
执行上下文(Execution context,简称EC)
执行上下文类型
执行上下文一般分为3种
- 全局执行上下文:全局上下文在浏览器中指的是
window
对象,也是this
globalThis
- 函数执行上下文:函数被调用的时候会被创建,调用函数时都会创建一个新的执行上下文。
- eval执行上下文:通过
eval
运行的代码
执行上下文有几个重要的属性:
变量对象-Variable Object
作用域链-Scope Chain
this
这里先不一一展开
执行上下文栈(EC stack)
我们一般会把执行上下文栈称为调用栈,本质是一个堆栈的结构,具有后进先出(LIFO)
的特性,用于存储执行上下文对象。
1 | // 以这个js文件为例 |
我们创建一个数组来模拟这个栈结构,初始化时这个栈是空的
1 | const stack: ExecutionContext[] = []; |
执行JS代码时,会先创建一个全局执行上下文(globalExecutionContext)并Push到当前的执行栈中,在浏览器关闭(页面的js进程退出)前都会存在。
1 | stack.push(globalContext as ExecutionContext); |
发生函数时,js引擎都会为该函数创建一个新的函数执行上下文(functionExcutionContext)并Push到当前执行栈的栈顶。
1 | // 先执行了 foo2 函数 |
当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的前一个执行上下文。
1 | // foo1执行后退出 |
我们可以用这样一幅图来简单描述