- JS引擎是按
可执行代码块来执行代码的,在任意的JavaScript可执行代码被执行时,执行步骤可按如下理解
- 创建一个 新的执行上下文(Execution Context)
- 创建一个 新的词法环境(Lexical Environment)
- 将该执行上下文的 变量环境组件(VariableEnvironment)和 词法环境组件(LexicalEnvironment)都指向新创建的词法环境
- 将该执行上下文 推入执行上下文栈 并成为 正在运行的执行上下文
- 对代码块内的 标识符进行实例化和初始化
- 运行代码
- 运行完毕后执行上下文出栈
~有点抽象,一个个来理解吧
执行上下文(EC)
-
定义
- 可以理解为,在代码执行时创建的环境,用于存储变量、函数声明、this指向等信息
- 每当js代码在运行时,都是在执行上下文中运行的
- 每个执行上下文都有自己的生命周期,在JavaScript引擎内部,通过执行上下文栈来管理,栈顶的执行上下文表示当前正在执行的代码块
-
三种执行上下文类型
-
全局执行上下文(Global Execution Context)
- 是JS程序的默认执行上下文
- 在整个程序执行期间始终存在,是所有其他执行上下文的最外层
- 在浏览器环境中,通常与全局对象
window关联
-
函数执行上下文 (Function Execution Context)
- 每当函数被调用(注意是调用)时,都会创建一个新的执行上下文
- 每个函数都有它自己的执行上下文,包括
函数的参数、局部变量等 - 函数执行上下文可以形成执行上下文的链,通过作用域链进行访问
-
Eval函数执行上下文
- 使用 eval函数执行代码时,会创建一个特殊的 Eval函数执行上下文
- eval函数可以修改其所在的作用域,因此在语法分析阶段可能无法确定作用域链
-
执行栈(ECS)
-
定义
- 当JS引擎第一次遇到脚本时,首先会创建一个全局的执行上下文并且压入当前执行栈。
- 当js引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压 入栈 顶
-
实例
-
let a = '1'; function first() { console.log('a'); 1 second(); console.log('insideA'); 3 } function second() { console.log('b'); 2 } first(); console.log('c'); 4 -
控制台打印
-
-
分析执行上下文栈
- 上述代码在浏览器加载时,JavaScript 引擎创建了一个全局执行上下文并把它压入当前执行栈。当遇到first函数 调用时,JavaScript 引擎为该函数创建一个新的执行上下文并把它压入当前执行栈的顶部,输出a
- 当first()函数内部调用 second函数时,同样的创建并且压入栈。当second()函数调用完毕输出b,它的执行上下文会从当前栈弹出,并且控制流程到达下一个执行上下文,即first()函数的执行上下文
- 当first()执行完毕输出insideA后,它的执行上下文从栈中弹出,流程到达全局执行上下文,一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文
如何创建执行上下文
- 两个阶段:创建阶段 执行阶段
创建阶段
词法环境
- ES6中定义:
词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用外部词法环境的空值组成
- 内部的两个组件
环境记录器
- 对于全局执行上下文,会创建一个全局环境记录,用于处理全局作用域中的变量声明
- 对于函数执行上下文,会创建一个变量环境,用于存储函数内的变量、函数声明和形参,这个环境记录器负责处理变量的声明和赋值
外部词法环境引用
- 对于全局执行上下文,外部词法环境引用为
null - 对于函数执行上下文,会建立对外部(父级)词法环境的引用,这个引用用于构建词法作用域链,形成函数的闭包
变量环境
- 也是一个词法环境,持有变量声明语句在执行上下文中创建的绑定关系
- 存储函数内部的变量声明(包括通过
var声明的变量)和函数声明。 - 在创建阶段,会对变量进行初始化,将变量声明提升到环境记录的顶部
确定this值
总结过一篇粗糙的笔记:this
- 在普通函数中,this 由调用函数的方式决定
- 在箭头函数中,this 由外层最接近的非箭头函数的上下文决定
执行阶段
-
完成对所有变量的初始化
-
顺序执行函数体的代码
文章只是进行笔记的总结,一些概念还是没有很清晰地理解,若有误,可以评论提醒我勒~