Backbone源码解读
Backbone在盛行的前端框架中是最轻量级的一个,悉数代码完成一共只要1831行1。从前端的入门再到Titanium,我虽然频频和Backbone打交道然则却对它的构造知之甚少,也促成了我想读它的代码的原始动力。这个系列的文章重要目标是分享Backbone框架中可以用于一样平常开辟的实践参考,力图可以简明扼要的展示Backbone Modal, Controller和Sync这些中心内容,愿望可以对人人进修和运用Backbone有一些协助。
这个系列的文章将包括以下3篇内容:
- Backbone源码解读之Events完成
- Backbone源码解读之Router, History完成
- Backbone源码解读之Model, Collection, Sync完成
本文是它的第一篇《Backbone源码解读之Events完成》
Backbone Events完成
Backbone的Event是全部框架运转的齿轮。它的幽美的地方在于它是Backbone的一个基本要领,经由过程_.extend的要领Mixin到Backbone的每一个模块中。
//Model
_.extend(Model.prototype, Events, {
changed: null,
//other Model prototype methods
//...
}
Event的基本观点
事宜的治理是一个绑定在Events这个定名空间中的_events对象来完成的。
它的构造是name: [callback functions]的key-callback_array键值对。
this._events = {
change: [callback_on_change1, callback_on_change2, ....],
....
}
当事宜发作的时刻,event从这个对象中依据事宜的称号获得回调函数数组,然后轮回执行每一个回调函数,也就说清楚明了为何屡次绑定会反复触发屡次事宜。
Event包括on, off, trigger三个基本要领,其他的一切要领均是对它们的扩大。
on(name, callback, context)
on接收3个参数,包括事宜的称号,回调函数和回调函数执行的上下文环境。个中context是可选参数,假如你不是很熟悉JS的执行上下文环境可以临时不必管它。
抛开一切Backbone的花梢的搜检,执行on的操纵实质就是向_events中name对应的回调函数数组[callback functions]中Push新的函数。
简朴来说代码完成就是这个模样:
Events.on = function(name, callback, context) {
if (callback) {
var handlers = events[name] || (events[name] = []);
handlers.push({callback: callback, context: context, ctx: context || this});
}
return this;
};
至于你在看源代码的时刻会长许多,那是因为一方面Backbone要处置惩罚关于_events以及_events[name]未初始化的两种特殊情况。另一方面eventsApi,onApi这些要领是为了处置惩罚on时刻你传入的不是一个string范例的称号和一个callback函数所做的前提处置惩罚。
比方下面两种要领都是正当的:
//传入一个称号,回调函数的对象
model.on({
"change": on_change_callback,
"remove": on_remove_callback
});
//运用空格支解的多个事宜称号绑定到同一个回调函数上
model.on("change remove", common_callback);
然则中心实在都是同一个绑定函数。
值得注重的一点是因为Backbone接收all作为name的参数,而且将回调函数保留在_events.all中,关于它的执行细致可以参考trigger。
off(name, callback, context)
与on差别,off的3个参数都是可选的。
假如没有任何参数的时刻,off相当于把对应的_events对象团体清空。
if (!name && !callback && !context) { this._events = void 0; return this; }
假如有name参数然则没有详细消灭哪一个callback的时刻,则把_events[name]对应的内容悉数清空。
if (!callback && !context) { delete this._events[name]; continue; }
假如另有进一步细致的callback和context的情况下,则进入[callback functions]中举行搜检,移除详细一个回调函数的前提异常严苛,必需要求上下文和函数与本来完全一致,也就是说假如传入的callback或许context不是原有on对象的援用,而是复制的话,这里的off是无效的。
var remaining = []; if( callback && callback !== handler.callback && callback !== handler.callback._callback || context && context !== handler.context ){ //保留回调函数在数组中 }
trigger(name)
trigger掏出name对应的_events[name]以及_event.all中的callback函数。
须要注重的一点是,触发对应称号的callback和all的callback运用了不一样的参数,all的参数中还包括了当前事宜的称号。
//当绑定3个以下回调函数的时刻Backbone会做以下优化处置惩罚,听说如许是可以进步执行效力的。
var triggerEvents = function(events, args) {
var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
switch (args.length) {
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
}
};
最简朴的Backbone事宜运用
从运用上来说,对一个对象举行事宜的on绑定。然后在同一个对象的其他函数执行过程当中,或许其他的对象中,触发该对象的trigger要领和对应的事宜称号来执行其上绑定的callback函数。
其他辅佐函数
接下来再看看Event中进一步定义了哪些其他辅佐函数
once(name, callback, context)
结果雷同与on,不过对应的callback函数仅执行一次。固然,你一样可以在once绑定的回调函数执行前手动经由过程off将其移除。
来看看Once的完成,因为添加了执行过程当中的移除要领,once在现实执行on的时刻运用了以下匿名函数:
var once = _.once(function() {function(){
self.off(name, once);
callback.apply(this, arguments);
});
return this.on(name, once, context);
然则仔细的你肯定发明,保留在_event数组中的函数是once这个匿名函数了。然则用户并不晓得Backbone的这些操纵,在作废绑定时仍然会运用本来的回调函数来试图消除绑定。上面我们也提到,必需运用完全一致的函数才可以作废绑定,那末为何还可以胜利呢?
这里Backbone做了一个小小的操纵,不晓得你有无注重到上面off函数中有如许一行内容?
callback !== handler.callback._callback
既然callback是我们传入的回调函数,那末哪里来的_callback这个属性呢?答案就在once内里。
var once = _.once(function() {...作废绑定,执行callback);
once._callback = callback;
也就是Backbone在返回之前偷偷的为once这个函数添加了一个_callback的属性,用来保留本来的回调函数,如许用户在传入本来的回调函数作废绑定的时刻,off会搜检函数时刻有_callback这个属性和用户传入的函数婚配,一样可以作废绑定。
listenTo(obj, name, callback)、listenToOnce(obj, name, callback)和stopListening(obj, name, callback)
除了将对象本身expose给另一个对象,让另一个对象执行trigger要领触发该对象上绑定的event之外。Event还进一步供应了listenTo系列的要领,执行逻辑正好与on相反。
比方有以下要求,当B对象上发作事宜b的时刻,触发A对象的callbackOnBEvent函数。
// 运用on的情况下
B.on(“b”, A.callbackOnBEvent)
// 运用listenTo的情况下
A.listenTo(B, “b”, callbackOnEvent);
从完成上看,它门的区分就在于谁担任治理这个事宜。第一个模子中,B就像是全部体系的master,担任事宜抵达的时刻的分发,让差别的对象(如A)执行对应的要领。第二个模子中,B更像是一个信息栈,A监听B上发作的事宜,而且在对应事宜抵达的时刻触发本身响应的回调函数。二者并没有优劣之分,然则从体系架构上来说因为本身回调函数的上下文环境就是A,所以listenTo的体式格局能够会来的越发天然,而且由A本身来掌握什么时刻移除回调的执行,也可以让代码的解耦水平更高。
逾越Backbone
运用Event要领来处置惩罚异步要求让代码的可读性大大增添。假如你的单页面运用正好运用了Backbone作为前端框架,将Event经由过程Backbone.Events这个变量暴露出来,你可以运用相似Model扩大的要领
//Your object
_.extend(your_object.prototype, Backbone.Events, {
//other prototype methods
//...
}
如许你的Object也就具有了相互绑定事宜、触发事宜的才能。
即使你的前端并没有运用Backbone,因为Events并不依靠Backbone的其他部份完成,你完全可以将它放到本身的代码lib中,作为一个基本要领来运用。
相似的体式格局你也可以经常在Node的后端看到
var util = require("util");
var events = require("events");
function MyStream() {
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);
总之,我个人是异常引荐多多运用Event来替换层级的Callback构造。
依据2015年4月 稳固版本Backbone.js 1.1.2的解释版本。Master上的代码和解释版本稍有相差,哪位大神晓得为何吗?? ↩