什么是事件?
事件就是
js
侦测到用户的操作或是页面上的一些行为。js控制页面的行为是由事件驱动的。
解释:在W3C
标准中:事件可以写在行内,但是因为结构和行为要分离,所以我们一半情况下用JS
的方法来绑定事件,只有在极少数情况下,才将事件写在行内。事件的绑定方法:浏览器中的节点(节点).on事件名 = function(){ 要干什么?(放在浏览器中,不执行,当事件发生的时候再执行) }
总结:事件就是给浏览器定义一个预处理函数,当事件触发的时候,执行函数,这就是事件。
在JS中常见的事件有?
- 鼠标点击--------
onclick
- 鼠标双击--------
ondblclick
- 页面或图片加载-----
onload
- 鼠标移入--------
onmouseover
- 鼠标离开--------
onmouseout
- 元素获得焦点------
onfocus
- 元素失去焦点------
onblur
touchstart
:手指触摸到屏幕会触发touchmove
:当手指在屏幕上移动时,会触发touchend
:当手指离开屏幕时,会触发touchcancel
:可由系统进行的触发,比如手指触摸屏幕的时候,突然alert
了一下,或者系统中其他打断了touch
的行为,则可以触发该事件
谈一谈js的事件循环机制?
解释:由于js
是单线程运行,js中的遇到函数调用时会将它压入调用栈中,当函数执行完成后,会从调用栈中弹出。当遇到setTimeout,setInterval,script
的时候,这些属于宏任务,会将它压入宏任务队列,当遇到promise.then,process.nextTick
等这些微任务时,会将他们压入微任务队列。当同步代码执行完成后,会查看当前的微任务队列是否为空,如果为空,就执行宏任务队列,如果不为空,就执行当前微任务。微任务执行完成后进入下一次事件循环执行当前宏任务,如果当前的宏任务队列中有同步,就执行同步,没有执行微任务,没有微任务,再进行下一次事件循环。
宏任务和微任务有那些?以及怎么执行的?
宏任务:script、setTimeOut、setInterval、setImmediate
微任务:promise.then,process.nextTick、Object.observe、MutationObserver
注意:Promise是同步任务
执行顺序:
1.执行宏任务script,
2.进入script后,所有的同步任务主线程执行
3.所有宏任务放入宏任务执行队列
4.所有微任务放入微任务执行队列
5.先清空微任务队列,
6.再取一个宏任务,执行,再清空微任务队列
7.依次循环
从 event loop 解释,为何微任务执行更早?
- 微任务是 ES6 语法规定的(被压入 micro task queue)。
- 宏任务是由浏览器规定的(通过 Web APIs 压入 Callback queue)。
- 宏任务执行时间一般比较长。
- 每一次宏任务开始之前一定是伴随着一次 event loop 结束的,而微任务是在一次 event loop 结束前执行的。
异步任务进入任务队列的过程
setTimeout(() => {
console.log("a");
}, 10000);
setTimeout(() => {
console.log("b");
}, 100);
解释:异步任务进入任务队列遵循着“先进先出”
的原则,同时由于js
是单线程的,但是浏览器不是单线程的,不同的浏览器线程对应处理不同的事件。当对应的事件可以执行的时候,对应的线程把把对应的事件推入到事件队列中,这也就解释了为什么第二个setTimeout
先被打印。
对应的线程
- js引擎线程:用于解释执行js代码、用户输入、网络请求等;
- GUI渲染线程:绘制用户界面,与JS主线程互斥(因为js可以操作DOM,进而会影响到GUI的渲染结果);
- http异步网络请求线程:处理用户的get、post等请求,等返回结果后将回调函数推入到任务队列;
- 定时触发器线程:setInterval、setTimeout等待时间结束后,会把执行函数推入任务队列中;
- 浏览器事件处理线程:将click、mouse等UI交互事件发生后,将要执行的回调函数放入到事件队列中。
案例一:
setTimeout(function () {
console.log("1");
});
new Promise(function (resolve) {
console.log("2");
resolve();
}).then(function () {
console.log("3");
});
console.log("4");
new Promise(function (resolve) {
console.log("5");
resolve();
}).then(function () {
console.log("6");
});
setTimeout(function () {
console.log("7");
});
function bar() {
console.log("8");
foo();
}
function foo() {
console.log("9");
}
console.log("10");
bar();
// 2,4,5, 10,8,9, 3,6,1,7
解析:
1.首先浏览器执行Js代码由上至下顺序,遇到setTimeout
,把setTimeout
分发到宏任务Event Queue
中
2.new Promise
属于主线程任务直接执行打印2
3.Promise
下的then方法属于微任务,把then
分到微任务 Event Queue
中
4.console.log(‘4’)
属于主线程任务,直接执行打印4
5.又遇到new Promise
也是直接执行打印5,Promise
下到then
分发到微任务Event Queue
中
6.又遇到setTimouse
也是直接分发到宏任务Event Queue
中,等待执行
7.console.log(‘10’)
属于主线程任务直接执行
8.遇到bar()
函数调用,执行构造函数内到代码,打印8,在bar
函数中调用foo
函数,执行foo
函数到中代码,打印9
9.主线程中任务执行完后,就要执行分发到微任务Event Queue
中代码,实行先进先出,所以依次打印3,6
10.微任务Event Queue
中代码执行完,就执行宏任务Event Queue
中代码,也是先进先出,依次打印1,7。
案例二:
setTimeout(() => {
console.log("1");
new Promise(function (resolve, reject) {
console.log("2");
setTimeout(() => {
console.log("3");
}, 0);
resolve();
}).then(function () {
console.log("4");
});
}, 0);
console.log("5");
setTimeout(() => {
console.log("6");
}, 0);
new Promise(function (resolve, reject) {
console.log("7");
resolve();
})
.then(function () {
console.log("8");
})
.catch(function () {
console.log("9");
});
console.log("10");
// 5 7 10 8 1 2 4 6 3