js事件机制模拟-附代码

事件机制概念

该机制 由事件源 排入 事件队列 通知 对应事件处理函数 构成,相对于传入回调函数的通知方法,设计上更为解耦。

事件循环机制原理

对于js这种单线程语言,如果只能同步做事,那遇到复杂计算或后端通信较慢的响应,不可能前端就一直等待。
这时js引擎就会将这部分当前不能立即返回结果的操作,放入一个任务队列,等主线程上可以立即完成的操作完成后,就去任务队列看看哪些不能立即完成的任务好了没。如果好了也就是他可以立即返回结果就将它放入主线程上执行获得结果。

这又与事件机制有什么关系呢

模拟事件机制

首先我们注册一个事件队列

var eventQueue = new Map();

然后我们注册一些列事件源

function addEventListenerX(eventObj,eventName, eventHandle){
        if(!eventObj.eventList){
                eventObj.eventList = [eventName]
        }else{
                eventObj.eventList.push(eventName);
        }
        if(!eventQueue.has(eventName)){
            var oneEventMap = new Map();
            oneEventMap.set(eventObj, eventHandle);
            eventQueue.set(eventName,oneEventMap)
        }else{
            eventQueue.get(eventName).set(eventObj, eventHandle);
        }
        
    }

var div1 = document.getElementById("div1");
    addEventListenerX(div1, "beClicked1",function(e){
        alert(e.currentTarget.innerHTML + " 我被点击了");
    })

var div2 = document.getElementById("div2");
    addEventListenerX(div2, "beClicked1",function(e){
        alert(e.currentTarget.innerHTML + " 我被点击了");
    })

触发事件

div1.addEventListener("click",commonHandle)
div2.addEventListener("click",commonHandle)

function commonHandle(e){
        e.currentTarget.eventList.forEach(item=>{
            eventQueue.get(item).get(e.currentTarget)(e);
        })
}
  • 最终实现可以为指定元素绑定多种事件并通过监听点击事件顺序触发

补充: 微任务与宏任务

异步任务(事件队列内的任务)也分为 *微任务 和 *宏任务

微任务 : 如Promise
宏任务 : 如SetTimeout
区别在于:
js引擎每次执行完所有的主线程同步任务后,会执行事件队列内任务。但如何执行事件队列内任务呢?采取一下规则

  • 微任务全部执行完毕 也就是清空微任务队列
  • 执行宏任务队列第一个任务
  • 为微|宏执行完毕后 又开始执行主线程同步任务
  • 往复循环就叫事件循环 Event Loop

在线代码

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<div id="div1">div1</div>    
<div id="div2">div2</div> 
</body>
<script>
    var eventQueue = new Map();
    function addEventListenerX(eventObj,eventName, eventHandle){
        if(!eventObj.eventList){
                eventObj.eventList = [eventName]
        }else{
                eventObj.eventList.push(eventName);
        }
        if(!eventQueue.has(eventName)){
            var oneEventMap = new Map();
            oneEventMap.set(eventObj, eventHandle);
            eventQueue.set(eventName,oneEventMap)
        }else{
            eventQueue.get(eventName).set(eventObj, eventHandle);
        }
        
    }

    var div1 = document.getElementById("div1");
    addEventListenerX(div1, "beClicked1",function(e){
        alert(e.currentTarget.innerHTML + " 我被点击了");
    })
    addEventListenerX(div1, "beClicked2",function(e){
        alert(e.currentTarget.innerHTML + " 我又被点击了");
    })

    var div2 = document.getElementById("div2");
    addEventListenerX(div2, "beClicked1",function(e){
        alert(e.currentTarget.innerHTML + " 我被点击了");
    })
    addEventListenerX(div2, "beClicked2",function(e){
        alert(e.currentTarget.innerHTML + " 我又被点击了");
    })
    addEventListenerX(div2, "beClicked3",function(e){
        alert(e.currentTarget.innerHTML + " 我又又被点击了");
    })

    div1.addEventListener("click",commonHandle)
    div2.addEventListener("click",commonHandle)

    function commonHandle(e){
        e.currentTarget.eventList.forEach(item=>{
            eventQueue.get(item).get(e.currentTarget)(e);
        })
    }
</script>
</html>

おすすめ

転載: blog.csdn.net/weixin_42043407/article/details/120266237