查看“JS 执行过程:执行上下文、词法环境”的源代码
←
JS 执行过程:执行上下文、词法环境
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:JavaScript]] == 执行上下文(Execution Contexts) == ''' <big>执行上下文</big>(Execution Contexts)''':是一种规范设备,用于跟踪ECMAScript实现对代码的运行时评估 —— JavaScript 代码都需要运行在相应的“执行上下文”中 执行上下文一共有三种类型: # <span style="color: green">'''全局执行上下文'''</span>:基础的上下文,任何不在函数内部的代码都在其中。 #: 它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。 #* 程序首次允许时创建 —— '''一个程序中只会有一个全局执行上下文'''。 # <span style="color: green">'''函数执行上下文'''</span>:函数内部的代码所在的上下文。 #* 每次函数被调用都会创建一个新的执行上下文 —— '''同一函数的多次执行,会多个上下文''' # <span style="color: green">'''eval执行上下文'''</span>:eval() 内部的代码所在的执行上下文 #* 使用“eval方法”时创建 在任何时间点,最多有一个“执行上下文”在实际执行代码,这被称为“正在运行的执行上下文”(running execution context)。 === 执行上下文栈 === '''<big>执行上下文栈</big>(Execution context stack,ECS)''':又称“调用栈 / 执行栈”(call stack),用于跟踪“执行上下文” —— “正在运行的执行上下文”就是这个其的栈顶元素 函数的“执行过程”就是:对应的“执行上下文”(栈帧)在“调用栈”中的“'''入栈'''”(开始执行)、“'''出栈'''”(执行完毕)操作。 示例: : <syntaxhighlight lang="JavaScript" highlight=""> let a = 'Hello World!'; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context'); </syntaxhighlight> : [[File:JavaScript:执行上下文栈(Execution context stack,ECS):示例.png|800px]] === 状态组件(State Components) === {| class="wikitable" style="width:100%;" |+ 执行上下文的状态组件 |- ! style="width:20%;" | <big>组件(Component)</big> ! style="width:80%;" | <big>用途(Purpose)</big> |- ! colspan="2" | “所有执行上下文”的状态组件 |- | 代码评估状态(code evaluation state) || 对“与执行上下文关联的代码的执行、挂起和恢复”进行评估所需的任何状态。 |- | 函数(Function) || 如果这个执行上下文正在评估函数对象的代码,那么这个组件的值就是那个函数对象。如果上下文正在评估脚本或模块的代码,则该值为null。 * “正在运行的执行上下文(running execution context)”的 Function 组件的值也被称为“活动函数对象(active function object)” |- | 领域(Realm) || “关联代码”访问“ECMAScript 资源”的领域 * “正在运行的执行上下文(running execution context)”的 Realm 组件的值也被称为“当前 Realm(current Realm)”。 |- ! colspan="2" | “ECMAScript代码执行上下文”的附加状态组件 |- | 词法环境(LexicalEnvironment) || 标识“词法环境”,用于解析代码在此执行上下文中造成的标识符引用。 |- | 变量环境(VariableEnvironment) || 标识“词法环境”,其 EnvironmentRecord 保存由 VariableStatements 在此执行上下文中创建的绑定。 |- ! colspan="2" | “生成器执行上下文”的附加状态组件 |- | 词法环境(LexicalEnvironment) || 标识“词法环境”,用于解析代码在此执行上下文中造成的标识符引用。 |} 执行上下文的 LexicalEnvironment 和 VariableEnvironment 组件始终是“词法环境(Lexical Environments)”。 创建执行上下文时,其 LexicalEnvironment 和 VariableEnvironment 组件最初具有相同的值。 === 生命周期 === 可以分为 2 个主要阶段: # '''创建阶段(Creation Phase)''':(“预编译阶段”)JavaScript 引擎会做以下几件事情: ## 创建<span style="color: green">“'''词法环境(Lexical Environment)'''”</span>组件 ## 创建<span style="color: green">“'''变量环境(Variable Environment)'''”</span>组件 # '''执行阶段(Code Execution Phase)''':(“执行阶段”)JavaScript 引擎会按照代码的顺序逐行执行,并且根据需要更新变量和函数的值 #* 在执行阶段,如果 JavaScript 引擎不能在源码中声明的实际位置找到 '''let''' 变量的值,它会被赋值为 <code>'''undefined'''</code>。 == 词法环境(Lexical Environment) == '''<big>词法环境</big>(Lexical Environment)''':是一种规范类型,基于 ECMAScript 代码的“词法嵌套结构”来<span style="color: green; font-size: 120%">定义“'''标识符'''”和具体“'''变量 / 函数'''”的关联</span> —— '''标识符''':变量 / 函数的名字 —— '''标识符''':对“原始数据”或“实际对象”(包含函数类型对象)的引用 <big>⭐</big> 即:<span style="color: blue; font-size: 120%">词法环境,是一种持有'''“标识符-变量”映射'''的结构</span> 通常,词汇环境与 ECMAScript 代码的某些特定语法结构相关联,如 FunctionDeclaration、BlockStatement、Try 语句的 Catch 子句,'''每次评估此类代码时都会创建一个新的词汇环境'''。 特殊的“词法环境”: # '''全局环境(global environment)''':是一个没有外部环境的词汇环境(“外部词法环境引用”为 null)。 #* 全局环境的 EnvironmentRecord 可以预先填充标识符绑定,并包含关联的“全局对象”,该对象的属性提供了一些全局环境的标识符绑定; #* 这个“全局对象”是全局环境的 This 绑定的值; #* 在执行 ECMAScript 代码时,可以向“全局对象”添加其他属性,并修改初始属性; # '''模块环境(module environment)''':是一个包含模块的顶级声明的绑定的词法环境。 #* 它还包含由模块显式导入的绑定; #* 其“外部词法环境引用”是一个“全局环境”; # '''函数环境(function environment)''':是一个“对应于ECMAScript函数对象的调用”的词汇环境。 #* 它可以建立一个新的 this 绑定; #* 它还将捕获支持 super 方法调用所需的状态; <div class="mw-code" style="white-space: normal; line-height: 1.6;"> <big>⭐</big> 词法环境,三个部分组成: # 一个“<span style="color: blue">'''环境记录(Environment Record)'''</span>” # 一个“'''(可能为空值的)'''<span style="color: blue">'''外部词法环境引用(Outer Lexical Environment)'''</span>” # 以及“<span style="color: blue">'''This 绑定(This Binding)'''</span>” </div> 其伪代码如下: : <syntaxhighlight lang="JavaScript" highlight=""> // 全局执行上下文 GlobalExectionContext = { // 词法环境 LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // 环境记录: "对象式环境记录" // 在这里绑定标识符 }, outer: <null>, // 外部词法环境引用: null this: <global object> // this 绑定: "全局对象" } } // 函数执行上下文 FunctionExectionContext = { // 词法环境 LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // 环境记录: "声明式环境记录" // 在这里绑定标识符 }, outer: <Global or outer function environment reference>, // 外部词法环境引用: 外部"词法环境" this: <depends on how function is called> // this 绑定: 取决于函数调用方式 } } </syntaxhighlight> === <span style="color: Teal">环境记录(Environment Record)</span> === '''<big>环境记录</big>(Environment Record)''':记录<span style="color: blue">'''在其关联的“词法环境”的范围内创建的“标识符”绑定'''</span> —— 任何在“环境记录”中的标识符都可以在当前“词法环境”直接以标识符形式访问 两种主要的“环境记录”: # <span style="color: green">'''声明式环境记录(Declarative Environment Record)'''</span>:主要用于函数 、catch 词法环境 # <span style="color: green">'''对象式环境记录(Object Environment Record)'''</span>:主要用于 with 和 global 的词法环境 # <span style="color: green">'''全局环境记录(Global Environment Record)'''</span>:一个对“对象式环境记录”和“声明式环境记录”组件的封装 ==== 声明式环境记录(Declarative Environment Record)<ref group="ECMAScript" name="声明式环境记录"/> ==== '''声明式环境记录''':与 ECMAScript 程序范围相关联,用于绑定范围内的<span style="color: blue">'''声明(declarations)'''</span>所定义的“标识符”集 注意: * 其方法(CreateMutableBinding、CreateImmutableBinding): *# 要求该 binding 在创建前必须不存在 —— 【let 变量无法重新定义?】 *# 会将该 binding 标记为 uninitialized * 其方法(GetBindingValue):如果该 binding 是一个 uninitialized binding,将引发 ReferenceError ——【let 的暂时性死区?】 💡 什么是“声明(declarations)”? 就是将“标识符”与“ECMAScript 语言值”直接关联。 包括:<span style="color: blue">'''variable'''、'''constant'''、'''let'''、'''class'''、'''module'''、'''import'''、'''function'''</span> 等声明 P.S. “ECMAScript 语言值”包括:'''Undefined'''、'''Null'''、'''Boolean'''、'''String'''、'''Symbol'''、'''Number''' 和 '''Object''' ==== 对象式环境记录(Object Environment Record)<ref group="ECMAScript" name="对象式环境记录"/> ==== '''对象式环境记录''':与一个<span style="color: blue">'''绑定对象(binding object)'''</span>相关联,将绑定与其“binding object”的属性名称直接对应的字符串标识符名称集 注意: * 无论<code><nowiki>[[Enumerable]]</nowiki></code>属性的设置如何,集合中始终包含了“自身的”和“继承的”属性 * 因为对象可以动态添加和删除属性,所以绑定的“标识符”集一定是可变的 ==== 全局环境记录(Global Environment Record)<ref group="ECMAScript" name="全局环境记录"/> ==== '''全局环境记录''':在逻辑上是单个记录,但被指定为封装“对象环境记录(Declarative Environment Record)”和“声明性环境记录(Object Environment Record)”的组合 —— 专门用于脚本全局声明 其中: # “'''对象环境记录'''”:将关联领域(Realm)的全局对象作为其基础对象,包括: ## 所有 built-in globals(内置全局变量)的绑定, ## 全局代码中 FunctionDeclaration、GeneratorDeclaration 或 VariableStatement 引入的所有绑定 # “'''声明性环境记录'''”:全局代码中所有其他 ECMAScript 声明的绑定 === <span style="color: Teal">外部词法环境引用(Outer Lexical Environment)</span> === '''<big>外部词法环境引用</big>(Outer Lexical Environment)''':用于<span style="color: blue">'''对词法环境值的“逻辑嵌套”进行建模'''</span> —— 意味着通过它可以'''访问其“外部词法环境”''' 如果在当前的“词法环境”中找不到变量,JavaScript 引擎可以在外部环境中查找这些变量: *(内部)词汇环境的外部引用是指在逻辑上围绕内部词汇环境的词汇环境 * 外部词汇环境可能有自己的外部词汇环境 * 一个词汇环境可以作为多个内部词汇环境的外部环境 每个“词法环境”都使用“外部词法环境引用”与其“外部词法环境”链接,如此将形成一个“<span style="color: blue">'''环境链'''</span>”(ES 3 中的'''作用域链''') —— 用于“'''标识符的解析'''”(变量的查找) === <span style="color: Teal">This 绑定(This Binding)</span> === <div class="mw-code" style="white-space: normal; line-height: 1.6;"> <big>⭐ <span style="color: blue">'''This Binding'''</span>:</big> # 在“'''全局执行上下文'''”中:this 的值指向“'''全局对象'''” #* 浏览器中,“全局对象”即 <code>'''Window'''</code> 对象 # 在“'''函数执行上下文'''”中:this 的值'''取决于函数调用方式''': ## 若函数被一个引用对象调用,this 将指向对象, ## 否则,this 的值被设置为“'''全局对象'''”(非严格模式)或者 <code>'''undefined'''</code>(严格模式) </div> == 补充内容 == “执行上下文”、“词汇环境”和“环境记录”纯粹是规范机制,不需要与 ECMAScript 实现的任何特定内容相对应。 ECMAScript 程序不可能直接访问或操作这些值。 === LexicalEnvironment 与 VariableEnvironment === '''LexicalEnvironment''' 和 '''VariableEnvironment''':都是'''执行上下文的组件''',且同是“词法环境(Lexical Environments)” —— 【注意:“Lexical Environments”指是“词法环境”这一规范机制,而“LexicalEnvironment”才指执行上下文的“词法环境”组件】 “变量环境(VariableEnvironment)”的概念是为 ES6 服务的: # “词法环境(LexicalEnvironment)”: # “变量环境(VariableEnvironment)”:只用来存储 '''var''' 变量绑定 创建执行上下文时,其 LexicalEnvironment 和 VariableEnvironment 组件最初具有相同的值。 == 参考 == === ECMAScript === 以下内容来自:《ECMAScript® 2015 Language Specification》的 [https://262.ecma-international.org/6.0/#sec-executable-code-and-execution-contexts 《Executable Code and Execution Contexts》] <references group="ECMAScript"> <ref group="ECMAScript" name="声明式环境记录"> <span style="color: green">声明式环境记录(Declarative Environment Record)</span>,定义/描述: <blockquote> 声明性环境记录用于定义 ECMAScript 语言语法元素的效果,如 FunctionDeclarations、VariableDeclarations 和 Catch 子句,这些子句将“标识符绑定”与“ECMAScript 语言值”直接关联。 </blockquote> ::* “ECMAScript 语言值(ECMAScript language values)”:由“ECMAScript语言类型(ECMAScript Language Types)”表征的值 ::* “ECMAScript语言类型(ECMAScript Language Types)”:包括 '''Undefined'''、'''Null'''、'''Boolean'''、'''String'''、'''Symbol'''、'''Number''' 和 '''Object''' <blockquote> 每个声明性环境记录都与 ECMAScript 程序范围相关联,该程序范围包含的 '''variable'''(变量), '''constant'''(常量), '''let''', '''class''', '''module''', '''import''', and/or '''function''' 等内容的声明。声明性环境记录绑定其范围内包含的声明所定义的标识符集。 </blockquote> </ref> <ref group="ECMAScript" name="对象式环境记录"> <span style="color: green">对象式环境记录(Object Environment Record)</span>,定义/描述: <blockquote> 对象环境记录用于定义 ECMAScript 元素(如 WithStatement)的效果,这些元素将“标识符绑定”与“某些对象的属性”相关联。 </blockquote> <blockquote> 每个对象“环境记录”都与一个称为其“绑定对象(binding object)”的对象相关联。对象环境记录将绑定与其“绑定对象”的属性名称直接对应的字符串标识符名称集。不是 IdentifierName 形式的字符串的属性键不包括在绑定标识符集中。无论 <nowiki>[[Enumerable]]</nowiki> 属性的设置如何,集合中都包含了自己的和继承的属性。因为可以从对象中动态添加和删除属性,所以由对象 Environment Record 绑定的标识符集可能会发生更改,这可能是任何添加或删除财产的操作的副作用。由于这种副作用而创建的任何绑定都被认为是可变绑定,即使相应属性的 Writable 属性的值为 false。对象“环境记录”不存在不可变绑定。 </blockquote> </ref> <ref group="ECMAScript" name="全局环境记录"> <span style="color: green">全局环境记录(Global Environment Record)</span>,定义/描述: <blockquote> 全局环境记录和函数环境记录是专门用于脚本全局声明和函数中的顶级声明的专门化。 </blockquote> <blockquote> 全局环境记录用于表示在 common Realm(公共领域)中处理的所有 ECMAScript 脚本元素共享的最外部作用域。全局环境记录为 built-in globals(内置全局变量)、properties of the global object(全局对象的属性)以及脚本中出现的所有顶级声明提供绑定。 全局环境记录在逻辑上是单个记录,但它被指定为封装“对象环境记录(Declarative Environment Record)”和“声明性环境记录(Object Environment Record)”的组合。“对象环境记录”将关联 Realm(领域)的全局对象作为其基础对象。这个全局对象是全局环境记录的 GetThisBinding 具体方法返回的值。全局环境记录的对象环境记录组件包含所有 built-in globals(内置全局变量)的绑定,以及全局代码中包含的 FunctionDeclaration、GeneratorDeclaration 或 VariableStatement 引入的所有绑定。全局代码中所有其他 ECMAScript 声明的绑定都包含在全局环境记录的“声明性环境记录”组件中。 可以直接在全局对象上创建属性。因此,全局环境记录的对象环境记录组件可能既包含由 FunctionDeclaration、GeneratorDeclassion 或 VariableDeclassing 声明显式创建的绑定,也包含作为全局对象的属性隐式创建的链接。为了识别哪些绑定是使用声明显式创建的,全局环境记录会维护一个使用其 CreateGlobalVarBindings 和 CreateGlobalFunctionBindings 具体方法绑定的名称列表。 </blockquote> </ref> </references> === 网络文章 === # [https://juejin.cn/post/6844903682283143181 《<nowiki>[译]</nowiki> 理解 JavaScript 中的执行上下文和执行栈》] # [https://juejin.cn/post/6945240902625394718 《你真的了解执行上下文吗?》]
返回至“
JS 执行过程:执行上下文、词法环境
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息