哈喽!大家好!我是木瓜太香,我又来嘞,明天来说说前端面试中常常别问到的 JS 事件环问题。
JS 事件环
JS 程序的运行是离不开事件环机制的,这个机制保障在产生某些事件的时候咱们有机会执行一个咱们当时预约好的函数,事件产生的时候 JS 会将相应的函数入栈执行而后出栈,然而对于事件环咱们还有一些未知的货色,例如,setTimeout 咱们习惯称他为定时器,然而可能很多人没有意识到,这个货色和咱们罕用的一些事件没什么不同,只不过咱们通常所说的事件大多须要用户触发,而 setTimeout 不必用户本人触发,而是指定工夫之后触发;那么问题来了,如果咱们将工夫设置为 0 会产生什么?会立刻执行么?
setTimeout、DOM 或者 HTTP 申请这部分其实并不在 v8 引擎中,这些属于 web API,javascript 是一个单线程的语言,也就意味着一次只能做一件事件,这个事实从未扭转
执行原理
JS 中所有的办法都会被推入栈中执行,执行实现被弹出,在遇到异步代码的时候,例如 setTimeout MutationObserver Promise 异步的局部会由其余主持 webAPI 的中央执行,等异步有后果之后,回调函数会进入相应的队列,Promise MutationObserver 回调进入微工作队列,setTimeout setInterval requestAnimationFrame 进入宏工作队列。期待主线程的执行栈空了,微工作队列立即被推入栈中执行,执行结束开始执行宏工作队列
一个经典的例子
html
<div class="outer">
<div class="inner"></div>
</div>
js
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
// Let's listen for attribute changes on the
// outer element
new MutationObserver(function () {console.log('mutate');
}).observe(outer, {attributes: true,});
// Here's a click listener…
function onClick() {console.log('click');
setTimeout(function () {console.log('timeout');
}, 0);
Promise.resolve().then(function () {console.log('promise');
});
outer.setAttribute('data-random', Math.random());
}
// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
以上代码在,手动点击 inner
元素的时候会有如下输入
click
promise
mutate
click
promise
mutate
timeout
timeout
截止 2020 年 8 月份 chrome edge opera firefox 的后果是对立的,然而在此之前的版本可能会有不同的输入。
一个奇怪的景象
上述代码咱们不应用手动触发点击,而是应用 inner.click()
触发点击,其后果会有很大的不同
click
click
promise
mutate
promise
timeout
timeout
造成以上微小差别的起因是,手动点击,不是通过函数进入执行栈的形式触发点击事件的回调,所以 inner 的回调执行完了主线程中的执行栈就是空的能够间接执行队列中工作,而后事件冒泡导致的回调函数才被推入栈运行;而 click 办法的点击则是通过将 click 推入栈中执行来达到的,inner 的点击回调执行完了之后 click 办法并没有被弹出栈,而是间接执行冒泡的下一个回调,因为下一个回调有一个反复的 属性设置 这是不会反复触发 MutationObserver 的所以 mutate 的输入只会有一个。等所有的冒泡回调被执行结束 click 函数才会被弹出栈。
最初留神,浏览器会尽量事后执行较为敏感的操作。
我本人建了一个 web 前端的交换裙有趣味的能够退出进来交换哦:237871108。当然你也能够通过哔哩哔哩搜寻木瓜太香找到我。