JavaScript中Lexical environment、Scope、Execution context三者有何区别?
嘿,我太懂你这种越查越懵的感觉了——MDN有时候把概念拆得太细,反而容易绕成一团浆糊。咱们用大白话把这三个家伙掰扯得明明白白:
Scope(作用域):抽象的「访问规矩」
Scope本质是一套静态的访问规则——它规定了变量、函数在代码的哪些位置能被访问到,哪些位置不行。从你写完代码的那一刻,它的作用域就定死了,跟代码有没有执行完全没关系。
最常见的就是「词法作用域」(Lexical Scope),也就是根据代码书写的嵌套关系来定的:比如你在函数内部写的变量,外部代码就碰不到;内部函数却能拿到外部函数的变量,这就是作用域规则在起作用。
举个直观的例子:
const globalName = "Alice"; function greet() { const greeting = "Hello"; console.log(`${greeting}, ${globalName}`); // 这里能拿到外部的globalName } console.log(greeting); // 报错!外部访问不到greet内部的变量
这段代码的作用域规则,在你写完的时候就确定了——greeting只能在greet内部访问,globalName全局可访问。
Lexical Environment(词法环境):实现规则的「存储盒子」
如果说Scope是看不见摸不着的规矩,那Lexical Environment就是JS引擎在内存里给你造的实际存储容器,专门用来实现Scope的规则。它是引擎内部的东西,但你可以把它理解成和代码的词法结构一一对应的“盒子”:
每个词法环境都包含两个核心部分:
- 环境记录(Environment Record):就是盒子里的格子,存着当前作用域的变量、函数声明
- 外部环境引用(Outer Environment Reference):就是盒子上的一根绳子,连向外层的词法环境——这就是作用域链的本质
还是拿刚才的例子说:
- 全局代码有一个全局词法环境,里面的格子存着
globalName,它的外部引用是null(因为没有外层了) greet函数对应的词法环境,格子里存着greeting,外部引用指向全局词法环境
当greet里要访问globalName时,引擎就顺着这个绳子找到外层盒子,取出变量——这就是Scope规则的具体实现过程。
Execution Context(执行上下文):执行时的「临时工作台」
Execution Context是动态的临时快照——只有当代码开始执行(比如调用函数、全局代码启动)时,JS引擎才会创建它;执行结束后,它就会被销毁,彻底从内存里消失。
这个“工作台”包含了当前执行期间的所有状态信息,核心内容有:
- 当前正在使用的词法环境(就是上面说的那个盒子):用来查找变量和函数
this值:当前执行上下文里的this指向谁- 调用栈信息:记录当前执行的位置,方便执行完回到上一步
比如当你调用greet()时,引擎会:
- 创建
greet的执行上下文,把它压入调用栈 - 把
greet对应的词法环境挂载到这个上下文里 - 开始执行
greet内部的代码,访问变量时就从这个词法环境里找 - 执行完
greet(),这个执行上下文就被弹出调用栈,直接销毁
- Scope:是抽象的规则,定好“哪些变量能在哪用”,静态不变
- Lexical Environment:是实现规则的实体盒子,跟着代码结构走,静态存在但执行时才被用上
- Execution Context:是执行时的临时工作台,把盒子拿来用,动态创建和销毁
简单说就是:Scope是「规矩」,Lexical Environment是「装东西的盒子」,Execution Context是「干活时的工作台」——工作台会把盒子搬过来,按照规矩取东西干活。
内容的提问来源于stack exchange,提问作者roman_kuzyk

