事件
-
什么是宏任务和微任务?
-
宏任务是由宿主发起的任务,包括:script(外层同步代码)、
window.setTimeout()
、window.setInternal()
、window.requestAnimationFrame()
(要求浏览器在下次重绘之前调用指定的回调函数更新动画)、setImmediate ()
(Node.js) ,最常见的是script和定时器。 -
微任务是由JavaScript引擎发起的任务,包括:Promise状态改变后的回调函数、MutationObserver(监视DOM树的变化,变化时调用回调函数) 等等,最常见的微任务是Promise状态改变后的回调函数。
微任务产生于宏任务的执行期间,只有在当前JS执行栈为空时才会执行微任务。
当前宏任务产生的微任务永远先于下一个宏任务前执行,执行微任务期间,当前宏任务并未完全结束。
举个例子:小明去银行办理业务,叫号轮到小明时,柜员开始为小明办理他的主要业务(宏任务),主要业务办理完毕后(执行栈为空),柜员询问小明是否还有其他业务需要办理(查询微任务队列是否有微任务),柜员为小明办理其他业务(微任务),全部办理完毕后才会轮到下一位叫号(下一宏任务)。
-
-
什么是事件循环(event loop)?
event loop是JavaScript执行机制中的一环,它的作用在于调控执行机制,确定下一个需要执行的任务。
它的每一次循环称为一次
tick
,循环过程如下:- 当前宏任务执行完毕后,若执行栈为空,判断微任务队列中是否存在需要执行的微任务
- 执行所有微任务
- 如果宿主为浏览器,判断是否有必要渲染页面
- 执行下一个宏任务,开始新一轮
tick
要注意,真正执行任务的是JavaScript的主线程执行栈,event loop只是起到一个发号施令的作用。
-
JavaScript的执行机制是什么?
-
JavaScript引擎是单线程的,同一时间段只能执行一个任务。
而浏览器是多线程的,其主要线程包括:
-
GUI渲染线程
负责渲染页面,解析HTML、CSS,构建DOM树,绘制页面等等;
页面重绘和回流;
与JS引擎互斥,即JS执行时会阻塞页面更新。
-
JS引擎线程
是JavaScript的内核,负责JavaScript代码的执行。
-
事件触发线程(event table)
用于控制事件循环,管理任务队列;
当遇到事件绑定或者异步操作时,事件触发线程会将它们添加到对应的线程中进行处理,有了处理结果以后,事件触发线程会将回调函数添加到任务队列,等待JS引擎线程处理;
事件触发线程并不执行任务,只是负责跟踪任务,并及时将它们推进任务队列。
-
定时器线程
setTimeout()
和setInterval()
所处的线程就是定时器线程,由于JavaScript引擎线程是单线程的,所以需要单独线程来计时并触发定时。 -
异步http请求线程
负责执行异步请求。
-
-
首先,最外层的script会作为第一个宏任务开始执行。
当遇到同步代码时,推入主线程执行栈执行;遇到异步代码时,判断其属于宏任务还是微任务;
将异步任务推给事件触发线程(event table),即在主线程内,这些任务属于被挂起的状态,而在事件触发线程内,异步任务会被推进相应线程处理,并注册回调函数;
异步任务有了处理结果后,事件触发线程会将它们推进任务队列(event quene),宏任务推进宏任务队列(tasks quene),微任务推进微任务队列(microtasks quene);
第一个宏任务执行完毕后,会进入事件循环(event loop),JS引擎会执行事件循环指示的任务,直到执行栈和所有队列都为空。
-
-
什么是事件冒泡和事件捕获?
事件流描述了页面接收事件的顺序,IE和Netscape提出了两种完全相反的事件流模型,分别是事件冒泡和事件捕获。
- 事件冒泡:事件被定义为由最具体的那个元素触发,逐级向上传播至没有那么具体的元素(文档)。现代浏览器都支持事件冒泡,且事件会一直冒泡到window对象。
- 事件捕获:最不具体的节点最先收到事件,而最具体的节点最后收到事件。事件捕获是为了在事件到达最终目标之前拦截事件。
DOM2规范中规定了事件流包括三个阶段:事件捕获、到达目标和事件冒泡。
DOM2的事件处理程序方法
element.addEventListener(event, function, useCapture)
的第三个参数useCapture
是一个布尔值,为true
时表示在捕获阶段调用事件处理程序,为false
时表示在冒泡阶段调用事件处理程序,默认值为false
. -
什么是事件委托(事件代理)?
-
首先要弄明白什么是事件对象:
事件对象是能够传给事件处理程序的唯一参数,其常见的属性和方法包括:
type
(返回被触发事件类型的字符串)target
(事件目标)currentTarget
(当前事件处理程序的所在元素)stopPropagation()
(阻止事件冒泡)preventDefault()
(阻止默认事件)- 等等。
-
事件委托的原理:不是给每个子节点都设置事件监听器,而是给父节点设置事件监听器,利用冒泡原理影响每个子节点。
-
使用事件委托可以减少事件处理函数来做到性能优化,但是事件委托一定涉及对子节点和父节点的控制,因此在事件处理函数中要善用事件对象的各个属性和方法。
-