JS[重要]执行上下文概念!!!

802 阅读6分钟

本文基于

ECMA-262 /2020年10月15日

ECMAScript® 2021 Language Specification


为什么要有变量提前?

简单来说就是为了实现函数提升而产生的副作用

所以到了ES6时代,请全面替换成 let const

根据Brendan Eich本人的说法(twitter.com/BrendanEich…**"

为什么要有函数提前?

  • 允许声明前调用
function addTwo(x) { return addOne(addOne(x)) } // 能够调用后面的addOne函数
function addOne(x) { return x + 1 } // 想要调用addTwo也是可以的

贺师俊:不提升就必须把所有依赖的函数都写到最前面。

这对于程序的编写是非常不友好的,所以有了函数提升


执行上下文

执行上下文就像一本书写当前代码内部信息的表格一样,而代理就像一个幕后机器人,拿着 执行上下文 来帮助执行代码,又被称为代码评估。

执行上下文由以下组成:

  • 代码评估状态

    表示 执行、挂起和恢复与此执行上下文关联的代码计算的状态。
    简单来说:就是记录上下文的状态。

  • Function

    如果此执行上下文正在计算函数对象的代码,则此组件的值是该函数对象。 简单来说:不知道为什么要有这个组件

  • 领域 (全局环境)

    关联代码访问 ECMAScript 资源的域记录。
    领域由一组内部对象、ECMAScript 全局环境、在该全局环境范围内加载的所有 ECMAScript 代码以及其他关联的状态和资源组成。 简单来说:领域里面装着全局对象。所以在执行上下文中可以随意调用全局对象和内置对象。

  • 脚本或模式

    关联代码的来源的模块记录或脚本记录。如果没有原始脚本或模块,如初始化主机定义Realm中创建的原始执行上下文的情况一样,该值为null。
    简单来说:为了区分module和script的运行环境。

  • 词汇环境(可选)

    用于解析代码在此执行上下文中进行的标识符引用的环境记录。
    简单来说:词汇环境是环境记录的一个集合

其实就是JS在创建执行上下文时,将声明的变量和函数都统一装进里面环境记录中。而后调用变量或者函数时,对照这张表就能找到对应内存并执行代码。

执行栈

执行上下文堆栈用于跟踪执行上下文。正在运行的执行上下文始终是此堆栈的顶部元素。

每当控件从与当前运行的执行上下文关联的可执行代码转移到与该执行上下文不关联的可执行代码时,都会创建一个新的执行上下文。新创建的执行上下文被推送到堆栈上,并成为正在运行的执行上下文。

当可执行代码执行结束,则弹出顶部元素。

程序开始运行时,会先创建一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创建一个新的执行上下文并压入栈内。

生命周期

执行上下文会经过三个阶段

  1. 创建环境
  2. 执行代码
  3. 弹出执行栈

环境记录

环境记录 就是建立了标识符——变量的映射对象。这里的标识符指的是变量名称或函数名,而变量则是实际变量原始值或者对象/函数的引用地址。

每个环境记录都有一个 [OuterEnv](外部环境记录) 字段,该字段为 null或对外部环境记录的引用。

这用于对环境记录值的逻辑嵌套进行建模。(内部)环境记录的外部引用是逻辑上环绕内部环境记录的环境记录的引用。

当然,外部环境记录可以有自己的外部环境记录。环境记录可以用作多个内部环境记录的外部环境。例如,如果函数声明包含两个嵌套函数声明然后,每个嵌套函数的环境记录将具有作为其外部环境记录的环境记录,以当前评估周围的函数。

环境记录可以被视为存在于一个简单的面向对象的层次结构中,其中环境记录是一个抽象类,包含三个具体的子类: 声明性环境记录对象环境记录全局环境记录函数环境记录模块环境记录 是声明性环境记录的子类。

每个执行上下文包含词汇环境组件,词法环境组件包含多条不同类型的环境记录,


  • 声明性环境记录

    每个声明性环境记录都与ECMAScript 程序范围关联,该范围包含变量、常量、let、类、模块、导入和/或函数声明。声明性环境记录绑定由其范围内包含的声明定义的标识符集。

    意思就是将 const let class module function import 的声明 全部归类于 声明性环境记录。并且做标识符和值的绑定

  • 对象环境记录

    每个对象式环境记录都与一个对象相关联,这个对象叫做对象式环境记录的binding object。可以理解为对象式环境记录就是基于这个binding object,以对象属性的形式进行标识符绑定,标识符与binding object的属性名一一对应。
    对象式环境记录用来定义那些将标识符与某些对象属性相绑定的ES语法元素,例如with语句、全局var声明和函数声明。

    主要记录的是这样的环境 window.Math = Math;

  • 函数环境记录

    函数环境记录是一种声明性环境记录,用于表示函数的顶级作用域,如果函数不是箭头函数,则提供this绑定。
    该表格取自【JS夯实之执行上下文与词法环境】

  • 全局环境记录

    全局环境记录在逻辑上是单个记录,但它指定为封装对象环境记录和声明性环境记录的复合记录。该记录对象为内置全局对象、全局对象的属性以及脚本中出现的所有顶级声明提供绑定。
    该记录用于执行上下文的领域组件,相当于为每个上下文对象提供全局环境记录。

  • 模块环境记录

    模块环境记录用于体现一个模块的外部作用域(即模块export所在环境),除了正常绑定外,也提供了所有引入的其他模块的绑定(即import的所有模块,这些绑定只读),因此我们可以直接访问引入的模块。

总结

执行上下文 {
    代码评估状态,
    功能,
    领域:{全局环境记录},
    脚本或模式,
    词汇环境(可选):{
        声明性环境记录,
        对象环境记录,
        函数环境记录,
        模块环境记录,
        全局环境记录
    }
}