之因此忽然想寫這個文章,主要是以前看到一篇有意思的博文,《探究點擊事件在JavaScript事件循環中的表現》,有趣的地方在於JS點擊事件加入回調的並非點擊事件的回調方法,而是點擊事件描述(點擊位置等描述點擊的)
。es6
<body style="height: 2000px">
<button class="a">打開控制檯,快速點擊三次</button>
<br />
<button class="b" style="height: 1000px;width: 1000px">按鈕b</button>
<script>
var btn = document.querySelector(".a");
btn.onclick = function() {
console.log("click a");
setTimeout(function() {
//頁面失去響應2s。
var time = new Date().getTime();
while (new Date().getTime() - time < 2000) {}
window.scrollTo(100, 500); //滾動到第二個按鈕上。
console.log("timeout");
});
};
var btn1 = document.querySelector(".b");
btn1.onclick = function() {
console.log("click b");
};
</script>
</body>
複製代碼
當咱們連續點擊button.a
兩次的時候,結果倒是bash
click a
timeout
click b
複製代碼
明明是點擊的button.a
,爲何會觸發button.b
的事件?dom
可見,點擊並無直接把回調推入事件循環(
對事件循環不瞭解,能夠先去了解一下
)的,而是推入了點擊事件的描述(推測就是點擊位置、事件類型...
),當咱們觸發點擊的時候,js會去點擊位置尋找相應的事件,在上面的例子中,咱們在點擊button.a
以後執行了一個setTimeout
滾動頁面到button.b
的位置,以後執行第二次點擊的事件,在該位置button.a
已經不見了,而是button.b
,因此會執行button.b
的回調事件。測試
不管經過onclick
仍是addEventListener
實現事件綁定,我懷疑綁定機制是同樣的,由於在實際測試中,我發現onclick
執行順序和addEventListener
是同樣的,也就是何時綁定,那麼就在第幾個執行。 addEventListener
和onclick
不一樣,不是直接給dom
綁定屬性,而且我在dom
節點上也沒有看到任何相應的對象用於保存事件,可見addEventListener
是不一樣的機制,參考EventEmitter
,試着去實現一個addEventListener
ui
// es6的Map可使用對象做爲鍵值對的鍵值
var listeners = new Map(); // 保存dom和其對應的事件
function addEventListener(dom, type, callback) {
if(listeners.has(dom)) {
listeners.get(dom)[type].push(callback);
} else {
listeners.set(dom, {
[type]: [callback]
})
}
}
// 點擊以後作了什麼
// 1. 保存事件(事件類型: 'click', 觸發位置)
...
// 2. 事件循環到觸發的時候,根據點擊
複製代碼
經過上面分析,總結一下spa
點擊一個
dom
以後發生了什麼?.net
- 保存事件 向事件循環隊列中添加事件描述對象,主要是(
事件類型: 'click', 觸發位置: {x:,y:},...
)- 事件循環到觸發 根據點擊事件描述對象去
dom
樹中找到該dom
(不在意是否是點擊的那個,只是當前該位置的dom
)- 執行 經過
listeners
找到該dom
對應的回調,而且執行