0

0

了解JS中的执行上下文、 执行栈、事件循环

青灯夜游

青灯夜游

发布时间:2020-07-06 10:39:17

|

2297人浏览过

|

来源于csdn

转载

本篇文章带大家了解一下javascript中的执行上下文和执行栈、事件循环。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

了解JS中的执行上下文、 执行栈、事件循环

下面的这些概念,无论是执行上下文、 还是执行栈,它在规范中的概念都很抽象,很多内容的理解实际靠的都是想象力,若有错误之处,还请指正。

执行上下文

简而言之,执行上下文(Execution Context)是正在运行的可执行代码所处环境的抽象,用于追踪一段代码中变量的求值。这是我总结过来的概念,可能有些不准确,也可以参考真正的规范定义。

不过总的来说,有三个关键点:

  • 只有可执行代码才会有执行上下文

  • 执行上下文是有状态的:运行状态(Perform)、挂起状态(Suspend)以及恢复(Resume)。处于Perfrom状态的执行上下文称之为运行时执行上下文(Running Execution Context)

  • 执行上下文完全不等价于词法环境,硬说关系,也只是前者引用了后者而已。

  • 执行一个JS脚本时,可以有多个执行上下文存在,但是 运行时上下文只有唯一一个(异步也是如此,至于为什么提了四个……三大天王有四个不是常识么……)。

并且ES规范中规定,可执行代码有下面几个:

  • 全局代码

  • 函数代码

  • Eval语句

  • 模块代码

换言之,看以下代码:

  
  var g=111
  function f(){
      
      console.log(g);
      for(let i =0; i < 10; i++){
          console.log(i);
      }
  }
  f();  /// (*)

如果上面的代码运行,只会产生两个执行上下文:

  • 全局

  • 函数f

但是如果将标注(*)的行注释掉,那么最终只有一个执行上下文,因为函数f根本就不会执行,自然就不会有相应的执行上下文了。里面唯一一个迷惑的是,就是for-loop了,但它根本不是可执行代码,所以它是函数执行上下文的一部分

执行上下文的重要组成部分

一个执行上下文,可以抽象为:

ExecutionContext = {
    State: <>
    LexEnv = {
        This:< ... > ,
        OuterEnv:< ... > ,
        DecRec:{
            //... identifiername-variable
        }      
    }
    VaEnv = {
        This:< ... > ,
        OuterEnv:< ... > ,
        VarRec:{
            //... identifiername-variable
        }      
    }
}

事实上,在一个执行上下文中有两个相当重要的组件:LexicalEnvironmentComponent(词法环境组件)和VariableEnvironmentComponent(变量环境组件)。词法环境组件指向当前代码中的词法环境LexEnv), 变量环境组件指向当前代码变量环境VarEnv)。

关于执行上下文不得不说的二三事中,有一个很重要的部分就是作用域链,但是在执行上下文中并没有看到相关内容。不过作用域链的确存在,它就在[[Scope]]内部属性中,通过浏览器可以直接看到。

不过也可以这样理解,在一个执行上下文被创建时,不仅会创建当前词法环境的LexEnv,也会创建LexEnv.OutEnvLexEnv.OutEnv.OutEnv…,直至延伸到全局为止。

执行上下文的创建与销毁

1、创建一个新执行上下文(ExecutionContext , EC)

2、创建当前词法环境(LexEnv 和 VarEnv )

3、将该执行上下文的 LexicalEnvironmentComponent 和 VariableEnvironmentComponent 指向当前环境下的 LexEnvVarEnv中。

4、将新执行上下文推入 执行栈中,并成为运行时执行上下文

5、对可执行代码块内的标识符进行实例化和初始化:

  • 收集当前词法环境内所有声明的标识符归入DecRec中,所有var声明的标识符归入VarNames集合中,在此阶段会进行标识符名检测,若与let/const/...声明的标识符与VarNames中的标识符重复, 报错。

  • DecRec中的标识符进行实例化,并设为uninitializedVarNames中的标识符绑定在ObjRec中,实例化后直接初始化为undefined

  • 对于function声明的函数,将直接指向函数对象,并也会绑定到ObjRec中,这是浏览器默认行为

    Shell脚本编写基础 中文WORD版
    Shell脚本编写基础 中文WORD版

    Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。它虽然不是Linux系统核心的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Linux系统

    下载

6、运行代码。

  • 非var声明的标识符会在声明处进行初始化(默认为undefined)。

  • 完成所有变量的赋值,并可能会一直在变化。

7、运行完毕从 执行栈 中弹出。

备注:

  • 关于This绑定,大部分情况可以用过去的说法解释,然而某些情况下却不尽然。
  • 闭包我会在下一篇介绍。
  • 执行上下文,我个人认为并不如何重要,但是却能在许多情形下起到极为关键的作用,所以还是有必要去深入认识一下。
  • 关于执行上下文和词法环境的关系,最多是前者引用了后者,仅此而已。诚然,有许多情况没必要用执行上下文来说明,但是永远避免不了违和感

执行栈与事件循环

执行栈(Execution Stack)就是由执行上下文构成的堆栈,类似于Call Stack

1、当Javascript引擎遇到一段可执行代码时,新建一个执行上下文。

2、将它推入执行栈中。并设置为运行时执行上下文。

  • 如果存在其他执行上下文。
  • 那么将当前执行上下文挂起
  • 然后再将新执行上下文推入执行栈中。

3、执行上下文运行完毕,弹出销毁恢复并将原执行上下文设为运行时。

总觉得这些没什么好说的,但是水一下吧

执行栈最重要的部分并非是执行栈概念本身,而是与任务队列的关系,它是事件循环的入门关键概念之一

众所周知,Javascript语言是单线程的,此处的执行栈就相当于主线程的调用栈,也是唯一一个调用栈,至于什么是主线程可以查阅相关资料,这里有些超纲了……

那么javascript是如何实现异步的

确切来说,这不是Javascript核心的部分,它是结合浏览器API(如Web Worker, Browser-context了解一下)实现的

事件循环中(事件处理过程),有两个极其重要的概念:

  • 任务序列: Task Quenue
  • 事件: Event

这两个概念,是抽象滴。

在Javascript中,一个任务也可以称之为事件,通常是一个函数回调,由许多任务组成的队列,就是所谓的任务序列了。任务序列有很多分类,例如:作业序列(Job Quenue)、消息序列(Message Quenue),本质没区别。

不必再深入了解,现在需要记住的是:一个任务序列中的任务如果想要被执行,就必须将它取出放入执行栈中。

举一个抽象点的例子:

例如下面的代码:

  
      var temp = 10;
      
      console.log('push task1');
      setTimeout(function task1(){
        temp+=10;
        console.log(temp+'task1 okay! ');
      },1000)
      
      console.log('taskquenue=[task1]; push task2');
      setTimeout(function task2(){
        temp*=10;
        console.log(temp+'task2 okay! ');
      },500) 
     
      console.log('taskquenue=[task1,task2]; push task3');
      setTimeout(function task3(){
        temp*= -0.2;
        console.log(temp+'task3 okay! ');
      },1500)
      console.log('taskquenue=[task1, task2,task3]');

输出如下:

push task1
taskquenue=[task1]; push task2
taskquenue=[task1,task2]; push task3
taskquenue=[task1, task2,task3]
100task2 okay! 
110task1 okay! 
-22task3 okay!

setTimeout是一个定时器,它能够将任务放到任务队列中。如图:

  • 添加作业task1
    在这里插入图片描述
  • 添加作业task2
    在这里插入图片描述
  • 添加作业task3
    在这里插入图片描述

执行到此处, task1task2task3都被放入了任务队列; 然后执行栈全部执行完毕后,开始处理任务队列中的任务。

为什么任务队列中的任务必须在执行栈空时后执行呢?

  • 这里我也不清楚,这毕竟真正涉及了底层内容;只是有些明白为何这样,不过担心误导他人,就算了。
  • 一般而言, 关于任务序列的相关概念没有如此简单,还涉及了很多东西,例如阻塞调度(Schedule)等,这些方面可以参考其他诸如C++Java这类多线程的语言或是看看操作系统这方面的内容。
  • 如果只是简单的业务实现,不需要了解这么多底层的东西。

现在开始处理任务吧:

  • 处理task2
    在这里插入图片描述
  • 处理task1
    在这里插入图片描述
  • 处理task3
    在这里插入图片描述

好了,一个事件循环就这么结束了。
然后Javascript引擎进入休眠阶段Javascript引擎永不结束!),等待有新的任务执行,然后开始下一个事件循环。

备注:
  • 这里只是一个简单例子
  • 事件循环可以有多个任务队列
  • 任务序列共分为两种: 微任务序列和宏任务序列
  • 我们的script代码,就是宏任务序列之一。

最后: Javascript引擎

这是我精读Javascript系列第三篇,猝不及防的就到了事件循环,看起来一下子就深入好多好多…… 但是我觉得这才是最合理程安排,大多的文档都把任务序列调用栈给分开了,但是在~~_____~~,它们本应该就是一体,不应该以任何方便的理由将它们分开

深入Javascript, 除了看规范,最好也看下JS引擎的实现文档,有些进阶内容,不在规范中,而是在这些文档里(请自己Google查找,百度也能搜索到)。

如果对Javascript引擎比较感兴趣的,可以参考:

  • MDN About_Javascript
  • Chromium V8参考
  • Github V8

虽然,极其不建议新手一下子看V8源代码,那种头痛欲裂又浑身颤抖不止的感觉实在是过瘾之极啊……

相关教程推荐:JavaScript视频教程

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

842

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

739

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

html编辑相关教程合集
html编辑相关教程合集

本专题整合了html编辑相关教程合集,阅读专题下面的文章了解更多详细内容。

38

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.9万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号