事件循环、事件监听、事件委托
一、事件循环
JavaScript引擎是干什么用的?其实很简单--它的任务就是遍历应用中的每一行JavaScript代码,并且一次执行一行,意味着JavaScript是单线程的。这里最大的影响是:如果在JavaScript代码中有地方会占用大量的时间,那后面的代码都会被block住。
那么JavaScript引擎怎么知道如何一次处理一行JavaScript代码?它使用的是一个调用栈call stack。你可以把调用栈比作电梯--第一个进电梯的会最后一个出电梯,最后进电梯的会最先出。
/* Within main.js */
var firstFunction = function () {
console.log("I'm first!");
};
var secondFunction = function () {
setTimeout(firstFunction, 5000);
console.log("I'm second!");
};
secondFunction();
/* Results:
* => I'm second!
* (And 5 seconds later)
* => I'm first!
*/
下边模拟调用栈
1、...
2、secondFunction调用setTimeout,setTimeout入栈:
3、setTimeout执行后,浏览器会把setTimeout的回调函数(在这个栗子中是firstFunction)放到Event Table中。Event Table 就是个注册站:调用栈让Event Table注册一个函数,该函数会在5秒之后被调用。当指定的事情发生时,Event Table会将这个函数移到Event Queue。Event Queue其实就是个缓冲区域,这里的函数等着被调用并移到调用栈。
问题来了,什么时候函数会从Event Queue移到调用栈咧?JavaScript引擎依据一条规则:有一个monitoring process(不知翻译成啥好)会持续不断地检查调用栈是否为空,一旦为空,它会检查Event Queue里边是否有等待被调用的函数。如果存在,它就会调用这个Queue中第一个函数并将其移到调用栈中。如果Event Queue为空,那么这个monitoring process会继续不定期的检查。这一整个过程就是Event Loop
4、一旦回调函数加入到Event表中,代码不会被block住,浏览器不会等待5秒之后再继续处理接下去的代码,相反,浏览器继续执行secondFunction的下一行代码,console.log。
5、在background,Event Table会持续地监测是否有事件触发,将函数移到Event Queue中。在这个栗子中,secondFunction执行完毕,接着main.js也执行完毕。
6、从回调函数被放入Event Table后5秒钟,Event Table把firstFucntion移到Event Queue中。
7、由于事件循环持续地监测调用栈是否已空,此时它一注意到调用栈空了,就调用firstFunction并创建一个新的调用栈。
8、一旦firstFunction执行完毕,调用栈空了,Event Table里也没有注册函数,Event Queue也为空。
二、事件监听
事件机制:捕获阶段、目标阶段、冒泡阶段
w3c规范
语法:element.addEventListener(event,funvtion,useCapture)
event:(必需)事件名,支持所有dom事件
function:(必需)指定事件触发是执行的函数
useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。ture,捕获。false,冒泡。默认false
document.getElementById("btn1").addElementListener("click",hello); function hello() { alert("hello world!") }
IE标准
语法:element.attachEvent(event,function)
event:(必需)事件类型,需要加“on”,如:onclick
function:(必需)指定要事件触发时执行的函数
document.getElementById("btn2").attachEvent("onclick",hello); function hello() { alert("hello world") }
可以绑定多个事件,常规的事件绑定值执行最后绑定的事件。可以解除绑定事件removeEventListener()
封装事件监听
//绑定监听事件 function addEventHandler(target,type,fn) { if(target.addEventListener) { target.addEventListener(type,fn); }else{ target.attachEvent("on"+type,fn); } } //移除监听事件 function removeEventHandler(target,type,fn){ if(target.removeEventListener){ target.removeEventListener(type,fn); }else{ target.datachEvent("on"+type,fn) } }
三、事件委托
事件委托就是利用冒泡的原理,把事件加在父元素或者祖先元素上,触发执行结果
var btn = document.getElementById("btn") ducument.onclick = function() { event = event || window.event; var target = event.target || event.srcElement; if(target == btn ) { alert(btn1.value) } }
可以用jq的live(),delegate(),bind(),on()
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ alert(123); alert(target.innerHTML); } } }