回顾
在学习JavaScript
代码执行过程中,我们学习了很多ECMA
文档的术语
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141055268?spm=1001.2014.3001.5501
- 执行上下文栈:
Execution Context Stack
,用于执行上下文的栈结构 - 执行上下文:
Execution Context
,代码在执行之前会先创建对应的执行上下文 - 变量对象:
Variable Object
,上下文关联的VO
对象,用于记录函数和变量声明 - 全局对象:
Global Object
,全局执行上下文关联的VO
对象 - 激活对象:
Activation Object
,函数执行上下文关联的VO
对象 - 作用域链:
scope chain
,作用域链,用于关联指向上下文的变量查找
引入
ES5
调整了执行上下文中的部分概念,去除了AO
,VO
的概念,添加了词法环境(Lexical Environments
)和变量环境(Variable Environment
)这两个新概念
执行上下文有两个属性:
-
LexicalEnvironment
用于存放let、const
声明的标识符
-
VariableEnvironment
用于存放var
声明的标识符
词法环境
把词法环境(Lexical Environment
)就想象成ES3
规范中的作用域(scope
),在ES5
规范中,你可以理解是作用域(scope
)这个词换了个叫法,变成了词法环境(Lexical Environment
)而已
整体图如下:
概念
它是 JavaScript
执行上下文的一部分,每当执行一个函数或代码块时都会创建一个新的词法环境
- 词法环境是静态的,是一个存储变量和函数声明的对象结构
- 使用
let
和const
声明的变量,不会在代码执行前提升到最顶部,而是绑定在词法环境中 - 包含一个环境记录(
Environment Record
) 用于存储变量和函数声明和一个外部词法环境引用(Outer Lexical Environment Reference
) ,即作用域链
分类
可以把词法环境抽象成一个大类,这个大类中分为了3个子类,如下:
-
全局词法环境(
Global Lexical Environment
):-
当
JavaScript
代码首次执行时,创建的就是全局词法环境。它包含全局变量和函数声明,并且外部环境引用为null
,因为它是最外层的环境 -
在全局执行上下文中,所有的变量和函数都是在全局作用域中声明的。出于优化和实现简化的考虑,
JavaScript
引擎通常会让词法环境和变量环境指向同一个对象。这意味着全局上下文中的var
、let
、const
声明的变量和函数会存储在同一个对象中
-
-
函数词法环境(
Function Lexical Environment
):每当一个函数被调用时,会创建一个新的词法环境。这个环境记录了函数内部的变量、参数和函数声明。它的外部环境引用指向函数定义时所在的词法环境 -
块级词法环境(
Block Lexical Environment
):- 由
let
、const
或class
声明的变量、常量和类所创建的环境。每当遇到代码块(如if
语句、循环、try-catch
等),会创建一个新的块级词法环境 - 在块级作用域中,
var
声明的变量会被提升到其最近的函数作用域或全局作用域中存储,并不存储在块级词法环境的环境记录中
- 由
环境记录
环境记录用于保存词法环境中的变量和函数,你可以把它理解成和VO
差不多的东西,但是环境记录还另外记录了当前词法环境的this
有三种主要的环境记录值:声明式环境记录、对象环境记录、模块环境记录
- 声明式环境记录:
-
函数环境和块级环境的环境记录属于声明性环境记录。它们不直接与对象挂钩,而是以一种更抽象的方式存储标识符及其绑定的值
-
函数内部的局部变量和参数,以及使用
let
、const
声明的变量,都存储在声明式环境记录中
-
- 对象式环境记录:
-
全局环境的环境记录通常是一个对象环境记录。它将全局对象作为环境记录的一部分。这意味着全局变量和函数作为全局对象的属性存在
-
例如,在浏览器环境中,全局对象是
window
,因此全局变量var a = 10;
其实是window.a = 10;
-
- 模块环境记录:
- 用来存储
ES6
模块中的变量和函数,它专门为模块创建,并允许import
和export
声明,可参考学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/142479941?spm=1001.2014.3001.5501 - 主要包含的信息有:
-
模块的唯一标识符:通常是模块的 URL(包括绝对路径或相对路径)
-
模块的依赖关系:包括它依赖的其他模块
-
模块的导入和导出:模块中
import
和export
的绑定 -
模块的执行状态:模块是否已经加载、链接和执行,是否已经缓存
-
模块的运行上下文:与模块相关联的变量、函数和上下文
-
- 用来存储
外部环境引用
是 JavaScript
执行上下文中的一个重要概念,尤其在处理作用域链(Scope Chain
)和闭包(Closure
)时显得尤为重要
- 每个执行上下文都关联着一个外部环境引用,这个外部环境引用指向它父级词法环境
- 可以直接把它当成和作用域链类似的东西,正是因为内部词法环境有了外部词法环境的引用,内部词法环境才能访问外部词法环境里定义的变量和函数
变量环境
是 JavaScript
中一个与词法环境类似的概念,主要用于存储变量的声明。它也是执行上下文的一部分,但与词法环境相比,变量环境主要负责处理 var
关键字声明的变量,而词法环境处理 let
和 const
声明的变量,其他则与词法环境相同
区别
-
词法环境(Lexical Environment) :
- 包含环境记录,记录函数或块级作用域内的
let
、const
和function
声明。 - 词法环境随着代码块的嵌套而形成作用域链(
scope chain
),用于变量的解析和查找。
- 包含环境记录,记录函数或块级作用域内的
-
变量环境(Variable Environment) :
- 主要用于存储
var
关键字声明的变量。 - 它的环境记录主要用于记录函数作用域或全局作用域内的
var
声明
- 主要用于存储
代码画图总结
var globalVar = "globalVar"
let globalLet = "globalLet";
function outer() {
var outerVar = "outerVar"
let outerLet = "outerLet";
if (true) {
var blockVar = "blockVar";
let blockLet = "blockLet";
console.log(blockLet); // "blockLet"
}
console.log(outerLet); // "outerLet"
}
outer();
console.log(globalLet); // "globalLet"
内存图示如下: