[前端核心知识点]解读栈,堆,队列及宏任务,微任务的理解

533 阅读4分钟

栈,堆,队列的特点

  • 栈: 有结构,有序,先进后出
  • 队列: 有结构,有序,先进先出
  • 堆: 无结构,无序

栈,堆的定义与对比

  • 栈:可以存放有结构,有顺序的数据,可用于存放js的基本数据类型(Number/String/Boolean/null/undefined)和对象的引用,存放的数据大小和生命周期是确定的
  • 堆:存放的数据无结构,无顺序,可用于存在js的引用类型(数组对象,object对象)
  • 栈的寻址速度大于堆,这是由于栈中保存基本数据类型的key和value,而只保存堆中value所对应的key

栈堆在js中的应用

解读浏览器的任务队列

  1. 当浏览器执行js代码时,若遇到setTimout定时器,DOM操作,ajax等会交给浏览器的webAPIs,webAPIs会把这些异步任务根据类型放置在同源的宏任务队列中
  2. 当遇到promise的then回调,nextTick,await后的事件等会放被浏览器放置到同源的微任务队列中
  3. js只有一个主线程,主线程只能执行一个任务栈,当执行完任务栈中的任务后,会从任务队列中获取任务并执行(若任务队列中存在微任务,则按照顺序首先执行微任务,后执行宏任务),而任务队列会从webAPIs中不断获取任务,此过程称为事件循环

题目一

输出结果:1 3 5 4 2 6 7 8

解读:js自上而下执行,首先输出1,遇到定时器,丢给webAPIs(放入宏任务队列),往下遇到promise执行输出3,里面的定时器继续丢给webAPIS(放入第二个宏任务队列),promise1的then回调放到微任务队列中,输出5;此时栈中任务全部完成,然后再执行微任务队列,输出4,后执行宏任务队列的定时器事件,输出2,当宏任务队列执行完成后会执行下一个微任务队列,若没有,则执行下个宏任务队列,依次类推

题目二

输出结果:f1 f2 f3 f4 f5 s1 s2 s3 s4 s5

解读:当循环中遇到定时器,丢给WebAPIs同时传递此时的i值,并依次放入宏任务队列中,执行栈先打印for循环中的输出后,再读取宏任务队列并执行

题目三

输出结果:p1 p5 p2 p3 p6 p7 p4

解读:此题目主要考察微任务队列,需注意一点的是await后的事件会产生微任务,并非等待await执行完后立刻执行函数内剩余的事件

  • 宏任务队列:定时器,ajax,dom操作...
  • 微任务队列:nextTick,Promise.then(),await后的事件

设计一个循环队列

通常的任务队列,若队列已满,即便队列中任务执行完有空余,也不能插入新的任务,循环队列可以复用同一个队列,达到节约性能的作用

要求

  1. MyCircularQueue(k): 构造器,设置队列⻓度为 k 。
  2. Front: 从队⾸获取元素。如果队列为空,返回 -1 。
  3. Rear: 获取队尾元素。如果队列为空,返回 -1 。
  4. enQueue(value): 向循环队列插⼊⼀个元素。如果成功插⼊则返回真。
  5. deQueue(): 从循环队列中删除⼀个元素。如果成功删除则返回真。
  6. isEmpty(): 检查循环队列是否为空。
  7. isFull(): 检查循环队列是否已满。

实现

class MyCircularQueue1 {
    // 构造函数,唯一
    constructor (k) {
        this.capacity = k // 保存循环队列的长度
        this.head = 0 // 队列开头,队列移除任务则加一
        this.tail = 0 // 队列结尾,队列新增任务则加一
        this.data = [] // 队列数据
    }
    // 判断队列是否为空
    isEmpty () {
        return this.head === this.tail
    }
    // 判断队列是否已满
    isFull () {
        if (this.tail - this.head === this.capacity) {
            return true
        }
        return false
    }
    // 从队首获取元素,如果队列为空,则返回-1
    Front () {
        if (!this.isEmpty()) {
            return this.data[this.head % this.capacity]
        }
        return -1
    }
    // 从队尾获取元素,如果队列为空,则返回-1
    Rear () {
        if (!this.isEmpty()) {
            return this.data[(this.tail-1) % this.capacity]
        }
        return -1
    }
    // 向循环队列中插入一个元素,成功插入返回true
    enQueue (value) {
        if (!this.isFull()) {
            this.data[this.tail % this.capacity] = value
            this.tail++
            return true
        }
            return false
    }
    // 向循环队列中删除一个元素,成功删除返回true
    deQueue () {
        if (!this.isEmpty()) {
            this.head++
            return true
        }
        return false
    }
}