我们看看 jQuery 在针对事件的整个处理的过程,包括绑定执行到底都干了些什么,通过一个流程图我们大体的了解下。
jQuery事件的流程图,在绑定阶段与执行阶段:
(单击图片可放大)
以上是 jQuery 事件的整个结构流程图,右边是流程的一个简单实现,主要是用于理解,源码当然不是这么简单的,考虑代码量太多,有些机制是没有实现的,比如委托与原生事件的区分:
1. 通过 on 绑定事件,分析传递的数据,加工变成 add 能够识别的数据 2. 通过 add 把数据整理放到数据缓存中保存,通过 addEventListener 绑定事件 3. 触发事件执行 addEventListener 回调 dispatch 方法 4. 修正事件对象存在的问题,通过 fix 生成一个可写的事件对象 5. 引入 handlers 把委托和原生事件(例如"click")绑定区分对待 6. 执行数据缓存的事件回调,传入内部产生的事件对象
这样的好处:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://www.imooc.com/static/lib/jquery/1.9.1/jquery.js" type="text/javascript"></script> <title>模拟jQuery</title> </head> <body> <p>模拟jQuery的整个事件流程绑定到执行</p> <p>包括</p> <p>1. 通过on绑定</p> <p>2. 通过dispatch派发</p> <p>3. 通过fix修正事件对象</p> <p>当然这里还没有涉及最重要的一点事件的委托过滤机制,考虑代码太复杂了,后面会有分析</p> <p id="aarontest" style="color:red">点击执行</p> <script type="text/javascript"> //事件缓存 //用来代替jquery的data缓存 var eventCache = {}; var $$ = ajQuery = function(selector) { return new ajQuery.fn.init(selector); } ajQuery.fn = ajQuery.prototype = { name: 'aaron', init: function(selector) { this.selector = selector; this[0] = document.querySelectorAll(selector)[0] return this; }, constructor: ajQuery } ajQuery.fn.init.prototype = ajQuery.fn ajQuery.event = { //增加事件 add:function(elem, types, handler){ var eventHandle = function(e) { return ajQuery.event.dispatch.apply(elem, arguments) }; //把用户的回调,放到缓存中 eventCache['handler'] = handler if (elem.addEventListener) { elem.addEventListener(types, eventHandle, false); } }, //派发事件 dispatch:function(event){ //修正事件对象 event = ajQuery.event.fix(event); //var handlerQueue = ajQuery.event.handlers.call(this, event, eventCache['handler']); eventCache['handler'].call(this,event) }, //执行事件句柄 handlers: function(event, handlers) { var handlerQueue = []; handlerQueue.push({ elem: this, handlers: handlers.slice(delegateCount) }); }, //修正事件对象 fix:function(event){ //原始事件引用 var originalEvent = event; //鼠标事件对象的属性 var props= "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "); //创建事件对象 var event = new ajQuery.Event(originalEvent); //混入事件属性 i = props.length; while (i--) { prop = props[i]; event[prop] = originalEvent[prop]; } return event } } function returnTrue() { return true; } function returnFalse() { return false; } //模拟出事件对象 //增加调用判断方法 ajQuery.Event = function(src, props) { if (src && src.type) { this.originalEvent = src; this.type = src.type; this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && src.returnValue === false ? returnTrue : returnFalse; } else { this.type = src; } this.timeStamp = src && src.timeStamp || jQuery.now(); this[jQuery.expando] = true; }; ajQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if (e && e.preventDefault) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if (e && e.stopPropagation) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if (e && e.stopImmediatePropagation) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; ajQuery.fn.on = function(types, fn) { //参数过滤 //............... ajQuery.event.add(this[0], types, fn); } $$('#aarontest').on('click',function(){ alert('通过click绑定,慕课网') }) </script> </body> </html>