JavaScript底层原理(栈、堆、主线程、任务队列、事件循环机制)

news/2024/10/21 10:12:46/

1. 栈(heap)和堆(stack)

栈是栈内存的简称,堆是堆内存的简称。顾名思义,内存是干啥的?内存就是用来存放数据的。

栈只有一个入口,同时也是出口,数据遵循先进后出、后进先出的原则。
栈用于存放基本类型数据和引用类型数据的指针,基本数据类型包含number,string,boolean,null,undefined,symbol,bigint等结构简单的数据,指针就是对象的地址而不是对象本身。因为栈中储存的数据结构简单,所以存储速度就会很快。

堆用于存放引用类型的数据,引用类型数据包含对象、数组、函数、Date等等,引用数据类型比较复杂,涉及原型、嵌套、方法等等。我们将引用类型数据赋值给变量a的时候,实际上复制的是引用类型的指针(地址),并不是复制的值,他们在内存中还是指向同一个数据源,所以当我们修改这个变量a的时候,我们的数据源也会随之发生变化。

2. 主线程和任务队列

背景

JavaScript是一门单线程语言,单线程意味着所有的任务都需要排队,只有前一个任务执行完成之后,才能执行下一个任务。

这种情况显然是不合理的,比如说我触发一个30秒的延时器,延时器被挂起就阻塞了主线程,导致浏览器CPU空闲且无法执行其他任务。所以这些阻塞进程的任务就被存放在任务队列(分线程)中。

同步任务和异步任务

于是js中的任务被分为了两种,一种是同步任务,另外一种是异步任务。

  • 同步任务(synchronous)指的是,在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。比如:普通函数、new Promise()、console.log()等等
  • 异步任务(asynchronous)指的是,不进入主线程,而进入“任务队列”(task queue)的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。比如:DOM 事件回调、定时器回调、网络请求的回调、promise.then()、promise.catch()的回调。

宏任务和微任务

异步任务又分为宏任务和微任务

  • 宏列队 macrotask queue:用来保存待执行的宏任务(回调),比如:DOM 事件回调、定时器回调、网络请求的回调
  • 微列队 microtask queue:用来保存待执行的微任务(回调),优先级高于宏任务,比如:promise.then()、promise.catch()的回调、MutationObserver 、queueMiscrotask()的回调

参考:JavaScript的堆、栈、队列、事件循环机制_js 哪些是栈和队列-CSDN博客

 3. 事件循环机制

因为js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数

整体会把所有代码分为两个部分:‘同步任务’,‘异步任务’

所有同步任务都在主线程上执行,形成一个执行栈

主线程之外还存在一个任务队列,专门存放异步任务(宏任务和微任务)

  • 宏任务进入到事件表(Event Table)中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列(Event Queue)中
  • 微任务也会进入到另一个事件表(Event Table)中,并在里面注册回调函数,每当指定的事件完成时,事件表(Event Table)会将这个函数移到事件队列(Event Queue)中
  • 整体script作为第一个宏任务进入主线程,当主线程的任务执行完毕,主线程为空时,会检查微任务的事件队列(Event Queue),如果有任务,就会全部执行,如果没有就执行下一个宏任务
  • 主线程不断重复上面的步骤,这就是Event Loop事件循环,只要主线程空了,就会去读取"任务队列"。这个过程会不断重复。


http://www.ppmy.cn/news/1442236.html

相关文章

ROS 2边学边练(34)-- 写一个广播(C++)

前言 上一篇我们体验了一下静态广播的例子流程,通过命令行方式传入所需的6D参数(死数据称之为静态)并广播给tf2系统,实际使用中,这些参数可是实打实实时生成的,所以需要动态处理这些实时数据,本…

【Docker】docker部署lnmp和wordpress网站

环境准备 docker:192.168.67.30 虚拟机:4核4G systemctl stop firewalld systemctl disable firewalld setenforce 0 安装docker #安装依赖包 yum -y install yum-utils device-mapper-persistent-data lvm2 #设置阿里云镜像 yum-config-manager --add…

Python爬虫--用户代理IP池

前面一篇讲了用户 UA 代理池,现在这篇来讲下 IP 代理, 相对于 UA 来说,IP 更容易被封, 这里讲两种方法。 方法一:本地ip池 方法一 就是将 IP 拿下来本地,然后通过随机选取或者其他来调用 这就跟之前使用…

go 安装软件报go.mod file not found

执行 go get -u github.com/go-sql-driver/mysql 下载mysql 报错 解决方法: 控制台:输入go env 返回如下: 红圈值为NUL,需要设置GOMOD的值, 然后再控制台执行 (1)mkdir mod (2)go mod init mod 然后再执行下载&…

【C语言】编译与链接

1.翻译环境与运行环境 在ANSI C的任何一种实现中,存在两个不同的环境。 1.翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令) 2.执行环境,它用于实际执行代码 2.翻译环境 那么翻译环境是怎么将源代码…

论文解读:(UPL)Unsupervised Prompt Learning for Vision-Language Models

文章汇总 存在的问题 之前的来自目标数据集的标记数据(有监督学习)可能会限制可伸缩性。 动机 通过无监督提示学习(UPL)方法,以避免提示工程,同时提高类clip视觉语言模型的迁移性能。 方法解读 主张top-k而不是top-p 注:top-k是指挑选…

深入探索 Apache Flink:流式处理框架的奥秘

在大数据与实时分析的时代,流式处理框架已经变得至关重要。Apache Flink 作为其中的佼佼者,以其独特的架构和强大的功能,吸引了全球范围内的开发者与数据科学家的目光。本文将详细剖析 Flink 的核心特性、应用场景、最佳实践,并展…

线程间为什么要枷锁?

首先,在多线程编程中,通常可以将线程中的和数据和资源分为私有的和共享的两类,一类是私有的,还有一类是共享的。 私有主要包括: 线程栈:每个线程都有自己的线程栈,用于存储局部变量、函数调用…