javascript的执行上下文
https://juejin.im/post/5ba32171f265da0ab719a6d7
https://juejin.im/entry/599867b2518825241e220eaa
我只是搬运工,看了他们的文章后深有启发,于是把他们的精华汇总然后加入自己的理解整理了这一篇文章。
这是一个非常抽象的概念,你无需彻底的弄明白它的意思,你只需要明白它做了什么。
在充分理解他做了什么之前还是要了解一下它到底是什么
Execution Context(执行上下文)是 ECMA-262 标准中定义的一个抽象概念,用于同 Executable
Code(可执行代码)进行区分。
1:什么是执行代码----Executable Code
合法的,可以被解释器解析执行的代码。
分为三类
- Global Code:全局代码
- Function Code:函数体内的代码
- Eval Code:使用 eval() 函数执行的代码
2:什么是执行上下文----Execution Context
执行上下文 是 ES 用来跟踪代码运行状态和相关资源集合的特殊机制。它决定了执行代码执行的过程中可以访问的数据。
每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。
分为三类
- Global Execution Context:全局执行上下文
这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中执行。它会执行两件事:创建一个全局的 window
对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
- Function Execution Context:函数执行上下文
每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数被调用时都有它自己的执行上下文。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
- Eval Execution Context:eval() 函数执行上下文
由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。
3:执行上下文的基本工作方式
先理解两个名词:执行上下文栈(Execution Context Stack)、运行执行上下文(Running Execution Context)
执行上下文栈( Execution Context Stack ):用来保存所有执行上下文的栈,是一种拥有 LIFO(后进先出)数据结构的栈。 当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。
运行执行上下文( Running Execution Context ):正在使用的执行上下文。在任意时间,最多只能有一
个正在运行代码的执行上下文。
4:基本工作方式
运行执行上下文总是在执行上下文栈的顶部,全局执行上下文总在执行上下文栈的底部。无论什么时候,只要控制权从与当前运行执行上下文相关的可执行代码上切换到另一部分与当前运行执行上下文不相关的可执行代码上,一个新的执行上下文就会被创建,新创建的执行上下文会被放在当前的运行执行上下文的上面,成为新的运行执行上下文。
5:具体工作流程
如前言中提到的,ES 标准中并没有从技术实现的角度定义执行上下文准确类型和结构,为了更方便地解释
执行代码和执行上下文之间的关系,暂且用数组表示执行上下文栈,然后用伪代码来操作执行上下文栈:
DCStack = [] // 执行上下文栈
<1:开始执行代码:全局执行代码与全局执行上下文
解析器在解析执行代码时首先执行全局代码,为其创建对应的执行上下文,全局上下文被压入执行上下文栈
ECStack = [globalContext // 全局执行上下文
]
<2:开始执行函数:函数代码与函数执行上下文
注意:函数代码中不包括内部函数的代码
运行下面的函数(function foo(bar) {if (bar) {return}foo(true);
})()我们用伪代码还原一下执行栈中发生了什么??// 第一次调用 fooECStack = [<foo> functionContext,globalContext
]// 第二次调用 fooECStack = [<foo> functionContext – recursively(递归),<foo> functionContext,globalContext
]
我们看一个实际的例子
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');
首先执行这段代码,解析器解析到了这段代码,于是先创建了一个全局上下文,并把全局上下文压入执行栈
ECStack = [Global Context
]
然后解析器检测到了 first(),开始调用first函数,于是创建了一个first函数上下文,并把这个函数向下文压入到执行栈的顶部(一般执行栈的顶部都是正在运行的上下文,现在正在调用first函数,所以顶部就是他的上下文)
ECSstack= [First Function Context-----(顶部是正在执行的上下文)Global Context
]
在first() 函数内部又调用了second()函数,于是JavaScript 引擎为second()函数创建了一个属于他的执行上下文,并把它压入执行栈的最顶部。(因为现在执行second()函数,所以他的执行上下文就在最顶部,因为first()函数没有执行完所以他的执行上下文依然在执行栈的队列中)
ECSstack = [Cecond Function Context-----(顶部是正在执行的上下文)First Function ContextGlobal Context
]
执行完second()函数之后,它的执行上下文会自动从执行栈弹出,并且控制流程执行下一个执行上下文,即 first() 函数的执行上下文。
ECSstack= [First Function Context-----(顶部是正在执行的上下文)Global Context
]
当 first() 执行完毕,它的执行上下文自动从栈弹出,控制流程按顺序到达全局执行上下文。一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文。
ECStack = [Global Context
]
6:JavaScript 引擎是怎么创建执行上下文?
创建执行上下文有两个阶段:1>:创建阶段 和 2>:执行阶段。
1>:创建阶段–(The Creation Phase)
在创建阶段会发生三件事
ExecutionContext = {ThisBinding = <this value>, // this绑定LexicalEnvironment = { ... }, // 词法环境VariableEnvironment = { ... }, // 变量环境}
- This 绑定。
在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)。例如:
let foo = {baz: function() {console.log(this);}
}
foo.baz(); // 'this' 引用 'foo', 因为 'baz' 被对象 'foo' 调用let bar = foo.baz;bar(); // 'this' 指向全局 window 对象,因为没有指定引用对象
- 创建词法环境组件。
词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用外部词法环境的空值组成。
有点没明白
简单来说词法环境是一种定义标识符以及变量的嵌套结构。(这里的标识符指的是变量/函数的名字,而变量是对实际对象[包含函数类型对象]或原始数据的引用)。
在词法环境的内部有两个部件组成:
1:环境记录器:是存储变量和函数声明的实际位置。
:2: 外部环境的引用:意味着它可以访问其父级词法环境(作用域)。
词法环境有两种类型:
1:全局环境:(在全局执行上下文中)是没有外部环境引用的词法环境。全局环境的外部环境引用是 null。它拥有内建的
Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且
this的值指向全局对象。
2:函数环境:函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数。
环境记录器也有两种类型:
1:声明式环境记录器存储变量、函数和参数。
2:对象环境记录器用来定义出现在全局上下文中的变量和函数的关系。
简而言之,
环境记录器在全局环境中,环境记录器是对象环境记录器。 在函数环境中,环境记录器是声明式环境记录器。
注意
函数环境,声明式环境记录器还包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射和传递给函数的参数的length)
抽象地讲,词法环境在伪代码中看起来像这样:
GlobalExectionContext = { // 全局执行上下文LexicalEnvironment: { // 词法环境组件EnvironmentRecord: { // 环境记录器 ---对象环境记录器Type: "Object",// 在这里绑定标识符}outer: <null> // 外部环境引用, 是没有外部环境引用的词法环境。全局环境的外部环境引用是 null。}
}FunctionExectionContext = { // 函数执行上下文 LexicalEnvironment: { // 词法环境组件EnvironmentRecord: { // 环境记录器 ---声明式环境记录器Type: "Declarative",// 在这里绑定标识符}outer: <Global or outer function environment reference> //外部环境引用 函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数。}
}
- 创建变量环境组件。
变量环境也是一个词法环境。所以它有着上面定义的词法环境的所有属性,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系。
在 ES6 中,词法环境组件和变量环境组件的一个不同就是前者被用来存储函数声明和变量(let 和 const)绑定,而后者只用来存储 var 变量绑定。
来个栗子
let a = 20;
const b = 30;
var c;function multiply(e, f) {var g = 20;return e * f * g;
}c = multiply(20, 30);
执行上下文用伪函数这么表示
// 全局执行上下文
GlobalExectionContext = { 1:ThisBinding: <Global Object>, //this绑定2: LexicalEnvironment: { // 词法环境 --全局的词法环境EnvironmentRecord: { //环境记录器Type: "Object", // 在这里绑定标识符a: < uninitialized >, // 变量a的绑定(let)b: < uninitialized >, // 变量b 的绑定(const)multiply: < func > // 函数声明}outer: <null> // 外部环境的引用nul},3: VariableEnvironment: { // 变量环境 --全局的词法环境EnvironmentRecord: { //环境记录器Type: "Object",// 在这里绑定标识符c: undefined, // 变量c 的绑定(var)}outer: <null> // 外部环境的引用nul}
}// 函数的执行上下文-----(只有遇到调用函数 multiply 时,函数执行上下文才会被创建)
FunctionExectionContext = { 1:ThisBinding: <Global Object>, // this 绑定 2:LexicalEnvironment: { //词法环境 --函数的词法环境EnvironmentRecord: { // 环境记录器Type: "Declarative",// 在这里绑定标识符Arguments: {0: 20, 1: 30, length: 2}, // 声明式环境记录器还包含了一个传递给函数的 arguments 对象(此对象存储函数参数键值对和传递给函数的参数的length)。},outer: <GlobalLexicalEnvironment> // 外部环境的引用是全局环境},3:VariableEnvironment: { //变量环境EnvironmentRecord: { // 环境记录器Type: "Declarative",// 在这里绑定标识符g: undefined // 变量g的绑定(var)},outer: <GlobalLexicalEnvironment> // 外部环境的引用是全局环境}
}
可能你已经注意到 let 和 const 定义的变量并没有关联任何值,但 var 定义的变量被设成了 undefined。
这是因为在创建阶段时,引擎检查代码找出变量和函数声明,虽然函数声明完全存储在环境中,但是变量最初设置为 undefined(var
情况下),或者未初始化(let 和 const 情况下)。 这就是为什么你可以在声明之前访问 var 定义的变量(虽然是
undefined),但是在声明之前访问 let 和 const 的变量会得到一个引用错误。 这就是我们说的变量声明提升。
javascript的执行上下文相关推荐
- 深入理解Javascript之执行上下文(Execution Context)
在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念.相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数 ...
- JavaScript:执行上下文执行上下文栈
在JavaScript概念中,有一个概念比较难以理解,它就是执行上下文和执行栈.最近在网上查阅了很多资料,现在把我的一些理解写出来,希望对各位有些帮助. 一.执行上下文 什么是执行上下文?是不是我们平 ...
- javascript之执行上下文堆栈
执行上下文堆栈 有三种类型的ECMAScript代码:全局代码,函数代码和eval代码.代码执行在它的执行上下文里. 有唯一的全局上下文,以及可能有多个函数和eval上下文.每一个函数调用,进入到函数 ...
- JavaScript学习系列之执行上下文与变量对象篇
一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...
- 【前端圭臬】十:从规范看 JavaScript 执行上下文(上)
前言 接下来我们来啃一个硬骨头,JavaScript 的 执行上下文(Execution Context). 与执行上下文相关的知识有很多弯弯绕绕,不过没关系,我们只需要以两个主要问题为线索展开.第一 ...
- 以及其任何超类对此上下文都是未知的_浏览器原理系列 - JS执行上下文详解(一):作用域
本文主要介绍JS执行上下文相关的内容,理解了JavaScript的执行上下文才能更好地理解JavaScript语言本身以及该语言一些特性,如变量提升.作用域和闭包. 一.作用域 1.1 作用域 作用域 ...
- 聊一聊javascript执行上下文
跟大家聊聊js的执行上下文 一,相关概念 EC : 执行上下文 ECS : 执行环境栈 VO : 变量对象 AO : 活动对象 scope chain :作用域链 二,执行上下文 javascript ...
- JavaScript深入之执行上下文栈
顺序执行? 如果要问到 JavaScript 代码执行顺序的话,想必写过 JavaScript 的开发者都会有个直观的印象,那就是顺序执行,毕竟: var foo = function () {con ...
- javascript --执行上下文,作用域
执行上下文 顾名思意就知道他是动态的,只在代码运行的时候产生 作用域 顾名思意就知道它是一个"领域",并且这个"领域"在一开始就规划好, 不会在改, var d ...
最新文章
- MIT录取不再看SAT科目成绩:曾是华裔传统优势,数学等学科测验更是中国留学生强项...
- 学php为什么要学linux,为什么 PHP 程序员应该学习使用 Swoole
- indesign选中不了图片删除_(54)批量给图片加上说明文字
- java源程序加密解决方案(基于Classloader解密)
- 使用Guava MapSplitters配置Hadoop
- 不使用杀毒软件就上网也不会感染计算机病毒,【判断题】不使用杀毒软件就上网也不会感染计算机病毒。
A. 正确
B. 错误...
- java 数据字典 spring_springboot+redis+切面实现数据字典功能
- Golang 方法接收者为值与指针的区别
- 使用cl编译器,将记事本写的c文件编译成dll和exe 步骤如下(记事本保存成.c)
- 在maya中安装numpy
- linux 类似迅雷下载软件,推荐几个可以替代迅雷的下载软件
- vue对table的某一行的数据进行编辑,删除,查看详情操作
- 软件设计师近10年上午真题解析知识点(并非绝对完整版)
- 关于WEB页面处谷歌验证的接入
- z变换判断稳定性和因果性_LTI系统判断因果性稳定性.PPT
- because it violates the following Content Security Policy directive: “default-src ‘none‘“
- mx150 宏碁swift3_大众化的轻薄本-宏碁蜂鸟Swift 3评测报告
- 土地利用覆盖数据(欧空局CCI300m)数据分享
- 搜狗输入法导入本地txt为个人词库
- 多元线性回归matlab代码例题_matlab多元线性回归
热门文章
- linux每日命令(30):Linux 用户及用户组相关文件、命令详解
- 第一部分 Mysql的基础
- CMD安装/删除服务
- mongodb 启动时的警告问题
- 自己实现简单的AOP(三) 实现增强四项基本功能
- HDU 2757 Ocean Currents
- iPhone开发之BASE64加密和解密
- 19/100. Subtree of Another Tree
- SAwUML – UML-based, contractual software architectures and their formal analysis using SPIN
- hdfs 操作 入门api