事件委托,事件代理,pub/sub 是工作常用的工作模式,也许只是你没有注意到而已。
事件委托
事件委托:当你通过动态添加元素的时候,如果想要为这个元素绑定事件不能够直接使用诸如onclick
方法,只能在其父元素绑定事件,常见于通过 ajax
请求数据。
简单地写如下示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // 通过target来实现来辨别是不是那个元素 function delegate(dom, eventType, selector, fn) { dom.addEventListener(eventType, function (e) { // var target = e.target; // for (target !== dom; target = e.target;) { // // } var cur = e.target, target = e.target, cur; for (; cur != this; cur = target.parentNode || this) { if (cur.classList.contains(selector)) { fn.call(cur, e); break; } } }, false); }
|
即监听事件的 target
属性来和 selector
进行比对,若是则应用回调函数。
事件代理
事件代理: 即为改变函数的上下文。
使用场景:常见于 DOM 事件的绑定上下文的改变。之前有在用 Backbone
的时候经常会这样用:
this.$el.on('click', '.item', $.proxy(this, this.itemClick))
。
用于在 itemClick
绑定的事件中让其上下文为对象。而不是 DOM
元素。翻看 jQuery
源码可见如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| proxy: function( fn, context ) { var tmp, args, proxy; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }
|
还是很容易理解的, 说到改变函数上下文还可以使用 bind
。
具体可查看 这里。
发布者订阅者模式
发布进行订阅模式是常用的模式比如在 Dom
事件监听的时候就是这种模式。
1 2
| $dom.on('click', fn); $dom.trigger('click', args);
|
以上 on
即为订阅,trigger
即为发布。这个对于理解 JavaScript 框架中的双向监听是有益处的。可以手写一个如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var pubSub = { subscribers: [], publish: function (eventType) { var slice = Array.prototype.slice; var args = slice.apply(arguments); this.subscribers.forEach(function (subscriber) { if (subscriber.eventType === eventType) { subscriber.cb.apply(undefined, args.slice(1)) } }) }, subscribe: function (eventType, cb) { var isSubscribed = false; for (var i = 0; i < this.subscribers.length; i++) { if (this.subscribers.eventType === eventType) { isSubscribed = true; this.subscribers[i].cb = cb; break; } } if (!isSubscribed) { this.subscribers.push({ eventType: eventType, cb: cb }) } } }
|
使用:
1 2 3 4 5 6 7
| // 不带参数 pubSub.subscribe('event1', function () { console.log('订阅者执行'); }); pubSub.publish('event1'); // 带参数 pubSub.subscribe('event2', function (p1, p2) { console.log('p1', p1); }); pubSub.publish('event2', 2, 3);
|
总结
以上为自己的对于常用的事件委托,事件代理的理解及发布者订阅进行的理解。
Todolist: