前端八股文之数据类型、JavaScript执行机制(EventLoop)

221 阅读4分钟

数据类型

最新的 ECMAScript 标准定义了8种数据类型。

基础类型:Number, String, Boolean, undefined, null, Symbol(ES6新增), BigInt(ES10新增)

复杂类型:Object

特别注意

  • Symbol,表示独一无二的值。

    Symbol 值通过Symbol()函数生成。 Symbol 类型的作用是,表示数据为独一无二的,可以保证不会与其他属性名产生冲突。

    Symbol函数前不能使用new命令,否则会报错。因为symbol是原始数据类型。

  • Bigint 是用来安全地存储和操作大整数,且可以超过数字的安全整数限制。

    BigInt,是通过在整数末尾附加 n或调用构造函数来创建的。

    BigInt,不能与数字互换操作。

  • Object包括: ArrayfunctionSetMapWeakMapWeakSetRegExpDateMath…(instanceof Object 返回都为true)

JavaScript执行机制(Event-Loop)

Javascript是一门单线程语言,只有前一个任务执行完毕才会运行下一个任务

所以为了规避掉资源分配的浪费,这就需要任务队列事件循环

任务队列

任务队列:是一种数据结构,存放要执行的任务。然后事件循环系统再以先进先出原则按顺序执行队列中的任务。

为了处理高优先级的任务,和解决单任务执行过长的问题

所以将任务划分:同步和异步;异步任务里又被分为:微任务和宏任务

同步和异步

同步任务:会一个一个立即执行的任务

异步任务:需要一段时间才能返回,不会立即执行的任务

微任务和宏任务

JavaScript执行时,浏览器V8引擎会在内部创建一个微任务队列和宏任务队列

宏任务执行过程中,如果有新的微任务产生,就添加到微任务队列中

当前宏任务里的微任务全部执行完,才会执行下一个宏任务

微任务包括: promise.then、MutationObserver(监听DOM)、node 中的 process.nextTick等

宏任务包括: 渲染事件、请求、script、setTimeout、setInterval、Node中的setImmediate、I/O 等

事件循环

简单一句话概括可以为:一个宏任务,所有微任务,渲染,一个宏任务,所有微任务,渲染.....

  1. 所有同步任务都在主线程上依次执行,形成一个执行栈(调用栈),异步任务处理完后则放入一个任务队列
  2. 当执行栈中任务执行完,再去检查微任务队列里的微任务是否为空,有就执行,如果执行微任务过程中又遇到微任务,就添加到微任务队列末尾继续执行,把微任务全部执行完
  3. 微任务执行完后,有需要的话会渲染页面,再到任务队列检查宏任务是否为空,有就取出最先进入队列的宏任务压入执行栈中执行其同步代码
  4. 然后回到第2步执行该宏任务中的微任务,如此反复,直到宏任务也执行完,如此循环

async / await

async/await 是 ES7 引入的重大改进的地方,可以在不阻塞主线程的情况下,使用同步代码实现异步访问资源的能力

function bar () {
    console.log(2)
}
async function fun() {
    console.log(1)
    await bar()
    console.log(3)
}
console.log(4)
fun()
console.log(5)

输出结果:4 1 2 5 3

因为await的意思就是等,等await后面的执行完。所以await bar(),是从右向左执行,执行完bar(),然后遇到await,返回一个微任务(哪怕这任务里没东西),放到微任务队列让出主线程。

上面说了 async/await 就是把异步以同步的形式实现,同步就是一步一步一行一行来嘛,await在微任务队列里都没回来,那在await下面的自然不能执行,导致 3 最后打印

完成下面题目

答案我就不写啦,留给你们自行思考~~~

console.log('start')

async function async1() {
  await async2()     
  console.log('async1 end') 
}
async function async2() {
  console.log('async2 end')
}

async1()

setTimeout(function () {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise1')
  resolve()
})
  .then(function () {
    console.log('promise2')
  })
  .then(function () {
    console.log('promise3')
  })

console.log('end')

进阶:

const myPromise = Promise.resolve(Promise.resolve('Promise'));

function funcOne() {
  setTimeout(() => console.log('Timeout 1!'), 0);
  myPromise.then(res => res).then(res => console.log(`${res} 1!`));
  console.log('Last line 1!');
}

async function funcTwo() {
  const res = await myPromise;
  console.log(`${res} 2!`)
  setTimeout(() => console.log('Timeout 2!'), 0);
  console.log('Last line 2!');
}

funcOne();
funcTwo();