什么是执行上下文
执行上下文是用来描述的 JavaScript 执行的环境的抽象概念,记录了代码在执行中的信息,一般有一下几种类型:
- 全局执行上下文
- 函数执行上下文
- eval 执行上下文
执行栈
用来管理执行上下文的工具,一个 后进先出 的调用栈,在程序开始时,会先出现一个 全局执行上下文 ,后每次调用函数,都会生成一个 函数执行上下文 并压栈,函数执行完成后,退栈并被 GC 回收,下面用一个案例来讲解
var name = 'xiaoming';
function person() {
function people() {
console.log(name);
}
people();
}
person();
下面逐步讲解上述例子中执行上下文的创建和回收
创建
- 在开始执行代码时,创建全局执行上下文并压栈,初始化全局变量
name和全局方法person - 调用
person方法,创建 person 函数执行上下文并压栈,初始化 people 方法 - 调用
people方法,创建 people 函数执行上下文并压栈,初始化 console 方法 - 调用
console方法,创建 console 函数执行上下文并压栈
回收
每次调用完毕就会依次从最顶层开始退栈,并被 GC 回收,与创建过程相反,这里就不赘述了,具体如下图
执行上下文的创建
执行上下文的创建主要做这两个事情
- 创建词法环境组件:指定一个词法环境对象,用于解析该执行环境内的代码创建的标识符引用。
- 创建变量环境组件:指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码通过 VariableStatement 和 FunctionDeclaration 创建的绑定。
- this 绑定:指定该执行环境内的 ECMA 脚本代码中 this 关键字所关联的值。
词法环境可以参另一篇文章《JavaScript 词法环境》
变量环境(VariableEnvironment)
变量环境组件组件与词法环境组建同为词法环境对象,区别于变量环境只存储 var function 声明的变量
var 与 let 和 const
- 提前激活
通过 let/const 声明的变量在初始化时是 <uninitialized>,在未声明时调用会得到 Uncaught ReferenceErro,又称 暂时性死区
而通过 var 声明的变量在初始化时会被提前声明并赋值 undefined
console.log(name); // Uncaught ReferenceErro
let name = 'xiaoming';
// -----
console.log(name); // undefined
var name = 'xiaoming';
- 作用域
另一个最大的区别就是 let/const 属于 块级作用域。在 Block 或 CaseBlock 时会创建一个 块级作用域,在里面通过 let/const 声明的变量会与这个作用域强绑定,简单来说就是不能被外部所获取,而 var 则不受此限制
if (true) {
var name = 'xiaoming';
let age = 18;
}
console.log(name); // xiaoming
console.log(age); // age is not defined
如果上面所说的你都已经完全理解,相信下面这段代码也难不倒你
var name = 'xiaoming';
function sayName() {
console.log(age);
if (name === 'xiaoming') {
var age = 'xiaodong'
console.log(name);
let name = 'xiaodong';
}
}
sayName();
提前激活
提前激活 也可以称为 变量提升 ,如上面我们知道通过 var 声明的变量会在编译时就被提前初始化并设置为 undefined
这得益于 JavaScript 在词法解析阶段会生成 全局上下文 函数上下文,并初始化赋值
总结
执行上下文 创建 -> 执行 -> 回收 过程简单总结
- 创建全局上下文的词法环境,声明式环境记录,对象环境记录
- outer 指向 null
- 创建全局上下文的变量环境,过程如上
- this 指向,window
- 创建函数上下文的词法环境,如全局上下文
- outer 指向,根据实际可能为
全局上下文/父词法环境 - this 指向
- 进入函数执行阶段
- 函数执行完后 GC 回收
重新学习,如发现错漏地方,欢迎更正,共勉