概述
1. 执行上下文概念
2.执行上下文类型
3. 执行上下文的内容
4.执行上下文的生命周期
执行上下文概念
执行上下文为我们的可执行代码块提供了执行前的必要准备工作,如变量对象的定义、作用域链的扩展、提供调用者的对象引用等信息。
执行上下文的类型
JS中有三种执行上下文类型:
执行上下文的内容
执行上下文是一个抽象的概念,我们可以将它理解为一个Object,一个执行上下文里包括以下内容:
1. 变量对象
2. 活动对象
3. 作用域链
4. 调用者信息
变量对象
原文:Every execution context has associated with it a variable object. Variables and functions declared in the source text are added as properties of the variable object. For function code, parameters are added as properties of the variable object.
每个执行上下文都与一个变量对象相联系,声明的变量和方法作为属性添加到这个变量对象中。对于函数来说,参数也被添加为这个变量对象的属性。
全局执行环境的变量对象始终存在,而函数局部环境的变量,只会在函数执行的过程中存在。
注意 ! :只有函数声明会被加入到变量对象中,而函数表达式会被忽略
// 函数声明,会被加入变量对象
function a(){}
// b是变量s声明,也会被加入变量对象,
// 但是作为函数表达式_b 不会被加入变量对象
var b = function _b(){}
全局执行上下文和函数执行上下文中的变量对象区别:
活动对象(activation 简称AO)
函数进入执行阶段时,原本不能访问的变量对象被激活成为一个活动对象,这样才可以访问到其中的属性。
其实变量对象和活动对象是一个东西,只不过处于不同的状态和阶段
作用域链
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限,当查找变量时,会先从当前上下文的变量对象中查找,如果没有找到,就从父级执行上下文的变量中查找,一直找到全局上下文的变量对象,即全局对象。这样多个执行上下文的变量对象构成的链表叫做作用域链。
函数的作用域在函数创建时就已经确定了,当函数创建时,会有一个名为[[scope]]的内部属性保存所有父级变量在其中。当函数执行中,会创建一个执行环境,然后通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链,然后,变量对象VO被激活生成AO并添加到作用域链的前端,完整的作用域链创建完成:
Scope = [AO].concat([[scope]])
当前执行代码块的调用者(this)
执行上下文数据结构模拟
executionContext:{
[variable object | activation object]:{
arguments,
variables: [...],
funcions: [...]
},
scope chain: variable object + all parents scopes
thisValue: context object
}
执行上下文的生命周期
执行上下文的生命周期
1.创建阶段
函数执行上下文的创建阶段,发生在函数调用时且在执行函数体内的具体代码之前,在创建阶段,JS引擎会做如下操作:
2.执行阶段
执行阶段中,JS代码开始逐条执行,在这个阶段,JS引擎开始对定义的变量赋值、开始顺着作用域链访问变量、如果内部有函数调用就会创建一个新的执行上下文压入执行栈并把控制权交出
3.销毁阶段
一般来讲当函数执行完成后,当前执行上下文会被弹出执行上下文栈并且销毁,控制权被重新交给执行栈上一层的执行上下文。
执行上下文栈
当一个脚本运行起来的时候,可能会调用很多函数并产生很多函数执行上下文,为了管理这些执行上下文,JS引擎就创建了“执行上下文栈”(Excution context stack简称ECS)来管理执行上下文。
执行上下文栈遵循LIFO(后进先出)的特征,代码执行期间创建的所有执行上下文,都会交给执行上下文栈进行管理。
当JS引擎开始解析脚本代码时,会首先创建一个全局执行上下文,压入栈底。
每当引擎发现一处函数调用,就会创建一个新的函数执行上下文压入栈内,并将控制权交给该上下文,待函数执行完成后,即将该执行上下文从栈中弹出销毁,将控制权重新给到栈内上一个执行上下文。
注意:递归可能会造成栈溢出
参考:https://juejin.im/post/5ebced85e51d454dc1467664#heading-1
当我们不再需要外在的认可来证明自己时
才能获得真正的自由