彻底理解词法环境

回顾

在学习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调整了执行上下文中的部分概念,去除了AOVO的概念,添加了词法环境(Lexical Environments)和变量环境(Variable Environment)这两个新概念

执行上下文有两个属性:

  • LexicalEnvironment用于存放let、const声明的标识符
    在这里插入图片描述

  • VariableEnvironment用于存放var声明的标识符
    在这里插入图片描述

在这里插入图片描述

词法环境

把词法环境(Lexical Environment)就想象成ES3规范中的作用域(scope),在ES5规范中,你可以理解是作用域(scope)这个词换了个叫法,变成了词法环境(Lexical Environment)而已

整体图如下:
在这里插入图片描述

概念

它是 JavaScript 执行上下文的一部分,每当执行一个函数或代码块时都会创建一个新的词法环境

  • 词法环境是静态的,是一个存储变量和函数声明的对象结构
  • 使用 letconst 声明的变量,不会在代码执行前提升到最顶部,而是绑定在词法环境中
  • 包含一个环境记录(Environment Record 用于存储变量和函数声明和一个外部词法环境引用(Outer Lexical Environment Reference ,即作用域链

分类

可以把词法环境抽象成一个大类,这个大类中分为了3个子类,如下:

  • 全局词法环境(Global Lexical Environment

    • JavaScript 代码首次执行时,创建的就是全局词法环境。它包含全局变量和函数声明,并且外部环境引用为 null,因为它是最外层的环境

    • 全局执行上下文中,所有的变量和函数都是在全局作用域中声明的。出于优化和实现简化的考虑,JavaScript 引擎通常会让词法环境和变量环境指向同一个对象。这意味着全局上下文中的 varletconst 声明的变量和函数会存储在同一个对象中

  • 函数词法环境(Function Lexical Environment:每当一个函数被调用时,会创建一个新的词法环境。这个环境记录了函数内部的变量、参数和函数声明。它的外部环境引用指向函数定义时所在的词法环境

  • 块级词法环境(Block Lexical Environment

    • letconstclass 声明的变量、常量和类所创建的环境。每当遇到代码块(如 if 语句、循环、try-catch 等),会创建一个新的块级词法环境
    • 在块级作用域中,var 声明的变量会被提升到其最近的函数作用域或全局作用域中存储,并不存储在块级词法环境的环境记录中

环境记录

环境记录用于保存词法环境中的变量和函数,你可以把它理解成和VO差不多的东西,但是环境记录还另外记录了当前词法环境的this
在这里插入图片描述

有三种主要的环境记录值:声明式环境记录、对象环境记录、模块环境记录
在这里插入图片描述

  • 声明式环境记录
    • 函数环境和块级环境的环境记录属于声明性环境记录。它们不直接与对象挂钩,而是以一种更抽象的方式存储标识符及其绑定的值

    • 函数内部的局部变量和参数,以及使用 letconst 声明的变量,都存储在声明式环境记录中

  • 对象式环境记录
    • 全局环境的环境记录通常是一个对象环境记录。它将全局对象作为环境记录的一部分。这意味着全局变量和函数作为全局对象的属性存在

    • 例如,在浏览器环境中,全局对象是 window,因此全局变量 var a = 10; 其实是 window.a = 10;

  • 模块环境记录
    • 用来存储 ES6 模块中的变量和函数,它专门为模块创建,并允许 importexport 声明,可参考学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/142479941?spm=1001.2014.3001.5501
    • 主要包含的信息有:
      1. 模块的唯一标识符:通常是模块的 URL(包括绝对路径或相对路径)

      2. 模块的依赖关系:包括它依赖的其他模块

      3. 模块的导入和导出:模块中 importexport 的绑定

      4. 模块的执行状态:模块是否已经加载、链接和执行,是否已经缓存

      5. 模块的运行上下文:与模块相关联的变量、函数和上下文

外部环境引用

JavaScript 执行上下文中的一个重要概念,尤其在处理作用域链(Scope Chain)和闭包(Closure)时显得尤为重要

  • 每个执行上下文都关联着一个外部环境引用,这个外部环境引用指向它父级词法环境
  • 可以直接把它当成和作用域链类似的东西,正是因为内部词法环境有了外部词法环境的引用,内部词法环境才能访问外部词法环境里定义的变量和函数

变量环境

JavaScript 中一个与词法环境类似的概念,主要用于存储变量的声明。它也是执行上下文的一部分,但与词法环境相比,变量环境主要负责处理 var 关键字声明的变量,而词法环境处理 letconst 声明的变量,其他则与词法环境相同

区别

  • 词法环境(Lexical Environment) :

    • 包含环境记录,记录函数或块级作用域内的 letconstfunction 声明。
    • 词法环境随着代码块的嵌套而形成作用域链(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"

内存图示如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值