带你读Backbone源码解读之Events完成

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构造。

  1. 依据2015年4月 稳固版本Backbone.js 1.1.2的解释版本。Master上的代码和解释版本稍有相差,哪位大神晓得为何吗?? 

    原文作者:yeelan0319
    原文地址: https://segmentfault.com/a/1190000002642171
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞