本文主要讲下Bacbone中的事件系统,先声明一个工具类:
1 var slice = Array.prototype.slice; 2 _ = (function () { 3 var _ = function () { }; 4 _.extend = function (target) { 5 var sources = slice.call(arguments, 1); 6 sources.forEach(function (source) { 7 if (source) { 8 for (var prop in source) { 9 target[prop] = source[prop]; 10 } 11 } 12 }); 13 return target; 14 } 15 16 var idCounter = 0; 17 _.uniqueId = function (prefix) { 18 var id = ++idCounter + ''; 19 return prefix ? prefix + id : id; 20 }; 21 return _; 22 })();
事件系统的大体框架:
1 jass=(function(){ 2 var bone={}; 3 bone.Events={ 4 on: function (event, callback, context) { 5 }, 6 off: function (event, callback, context) { 7 }, 8 listenTo: function (otherObj, event, callback) { 9 }, 10 stopListening: function (otherObj, event, callback) { 11 }, 12 trigger: function (event) { 13 } 14 15 } 16 return bone; 17 })();
on/off方法:
1 on: function (event, callback, context) { 2 this._events || (this._events = {}); 3 var events = this._events[event] || (this._events[event] = []); 4 events.push({ callback: callback, context: context, ctx: context || this }); 5 return this; 6 }, 7 off: function (event, callback, context) { 8 if (!event && !callback) { 9 this._events = {}; 10 return this; 11 } 12 delete this._events[event]; 13 }
这两方法的原理和zepto中的on/off两方法差不多,不同的是,这里不再处理DOM元素上的事件:
listenTo/stopListening方法:
1 listenTo: function (otherObj, event, callback) { 2 var listeners = this._listeners || (this._listeners = {}); 3 var id = otherObj._listenerId || (otherObj._listenerId = _.uniqueId('1')); 4 listeners[id] = otherObj; 5 var onlyListener = !event && !callback; 6 if (onlyListener) return this; 7 8 if (typeof event === 'object') callback = this; 9 10 otherObj.on(event, callback, this); 11 12 return this; 13 }, 14 stopListening: function (otherObj, event, callback) { 15 var listeners = this._listeners; 16 if (!listeners) return; 17 var deleteListener = !event && !callback; 18 if (typeof name === 'object') callback = this; 19 if (otherObj) (listeners = {})[otherObj._listenerId] = otherObj; 20 for (var id in listeners) { 21 listeners[id].off(event, callback, this); 22 if (deleteListener) delete this._listeners[id]; 23 } 24 return this; 25 }
我们看到,listenTo是通过本对象给另外一个对象注册事件的.其内部调用了on方法,但是把另外的对象记录在其listeners属性中:这样做的好处可以从stopListening方法就能看到,通过listenTo方法注册的事件,都能通本对象的stopListening方法移除掉其他对象上的事件,消除对其他对象的影响.
trigger方法:
1 trigger: function (event) { 2 if (!this._events) return this; 3 var args = slice.call(arguments, 1); 4 var events = this._events[event]; 5 6 if (events) { 7 events.forEach(function (ev) { 8 ev.callback.apply(ev.ctx, args); 9 }); 10 } 11 //处理all事件 12 var allEvents = this._events.all; 13 if (allEvents) { 14 allEvents.forEach(function (ev) { 15 ev.callback.apply(ev.ctx,args); 16 }); 17 } 18 }
我们知道,在MVC中,视图层是会被频繁创建的,有创建就会有销毁。那一个视图关闭后,我们是不是要做些什么呢?看一个熟悉的场景:
1 MyView = Backbone.View.extend({ 2 initialize: function(){ 3 this.listenTo (this.model,’change’, this); 4 }, 5 render: function(){ ... } 6 }); 7 8 Backbone的view类有个remove方法:
9 remove: function() { 10 this.$el.remove(); 11 this.stopListening(); 12 return this; 13 }
看到这个方法,是不是有点要清理view的感觉呢?但是遗憾的是,Backbone是不会调用它的,我们要自己添加一个(dispose/destroy)清理方法来封装这个remove,来帮我们处理销毁一个视图后的善后工作.
想想这里为何用listenTo,而不用on !