Execute Context
程序本质上是一个状态机,所谓的状态就是 变量,js 代码在执行的过程中,如何 存储状态 读取状态 , 改变状态 ?
☘️ 什么是上下文?
上:之前执行过程中,形成的接下来可能需要用的状态
下:接下来执行可能需要用到的状态
js在执行语句之前,为代码执行提供 查找状态 的地方,就是所谓的上下文
为什么需要上下文?
程序执行时, 查找变量需要依赖上下文
上下文中有哪些内容?
- 文本环境 (Lexical Environment)(词法环境)
- this Binding
js 通过文本环境查找需要的变量
☘️ 上下文在什么地方?JS如何管理上下文?
答案是 执行上下文栈 (Excution Context Stack)
- JS每次创建一个新的 上下文,就会把他放在 ECS的栈顶
- JS在执行的过程中,总是在执行栈的栈顶的上下文(当前执行上下文)中查找变量
- 当一个函数执行完,其依赖的上下文弹出栈顶
- 当整个页面(程序)结束,全局上下文弹出
☘️ 什么情况会创建上下文?
目前有四种情况会创建上下文:
- 进入全局代码
- 函数执行前
- 执行
eval指定的代码 - 进入
module代码前
☘️ 上下文的创建过程?
全局上下文和函数的上下文有所不同,本篇主要讨论
全局上下文,目的是对上下文的创建过程产生认识,而函数的上下文创建将放在下节函数的作用域讲解。
Step1
第一步是在 执行上下文中栈中压入 全局执行上下文
可以看到, 全局上下文 由两部分组成
- This Binding
- Lexical Environment
其中, This Binding 指向 全局对象 , 在浏览器环境下是 window 对象
全局上下文有一个很特殊的地方,他的 Lexical Environment (文本环境)由两部分组成
- 全局对象 (window)
- 全局scope (后面会解释它的作用)
step2
第二部是处理声明
- 找到所有的
非函数中的var声明 - 找到所有
顶级函数声明(不是函数表达式) - 找到
顶级letconstclass声明
解释: 顶级 是指该行代码以 声明开头,前面没有其他符号
找到后干嘛呢? 放入Lexical Environment
⚠️ 核心:
全局的代码中:
varfunction声明的变量, 创建在全局对象中letconstclass声明的变量创建在全局scope中- 查找变量时,先在
全局scope查找, 找不到则在全局对象中查找
代码验证:
let demo = "demo";
console.log(demo); //"demo"
console.log(window.demo); //"undefined"
代码说明了上面的 2 , let 声明的变量不会在全局对象上,而是在 全局scope 中
var demo = "demo"
console.log(demo); //"demo"
console.log(window.demo); //"demo"
代码说明了上面的 1
step3
第三步是 检查重复定义
分两步:
全局scope中是否有重复声明, 有则报错全局 scope中是否有与全局对象重复声明的变量, 有则报错
这一步是 let const class 声明的变量 不能重复定义
step4
第四步是创建绑定,也就是为上下文中的变量 初步赋值
全局对象中的变量:
var声明的变量 赋值为undefined- 函数声明会直接
创建函数对象, 然后指向该对象
⚠️ 注意:
-
执行顺序如上, 因此函数声明 可能会覆盖 var 声明
-
函数对象会直接创建,函数对象创建时,会在 ‘体内’([[scope]]属性) 保存函数创建时执行上下文的文本环境:
这里也解释了,为什么说 JS是 词法环境 ,是因为函数的作用域在其声明的时候就已经确定,而不是在执行的时候确定
全局scope中的变量:
不会进行初始化
⚠️ TDZ:暂时性死区
scope中的变量不会进行初始化,js规定,对未初始化的变量进行引用, 会报错,也就是所谓的暂时性死区。
step5
至此,执行上下文已经生成完毕,可以开始执行代码
outer
前面忽略了一个细节,文本环境中,除了记录变量查找,还有一个 outer 记录,该值指向了当前上下文外部的一个上下文,在全局上下文中,outer的值比较特殊,为 null
总结:
上下文是函数或者全局执行前的环境,是代码执行时查找变量和this的地方,js通过 ECS执行上下文栈
来管理上下文,新创建的上下文位于栈顶,js执行时总是通过栈顶的上下文查找需要的状态,因此栈顶的上下文称为 当前执行上下文 current execute context
第一行js代码开始解析时,要创建全局上下文
全局上下文的创建步骤如下:
- 将全局上下文压入
ECS - 将
var和顶级函数声明放入全局对象(浏览器window) - 将
顶级letconstclass声明放入全局scope 全局scope变量声明查重,如果在全局对象或者scope中有重复的声明就报错- 为
var声明变量赋值undefined - 为
顶级函数声明赋值函数对象,该对象内部会记录 当前上下文的Lexcial Environment - 创建完成,开始执行代码