zoukankan      html  css  js  c++  java
  • 【原创】backbone1.1.0源码解析之Events

    最近在看些node的源代码,发现backbone的应用还是挺广泛的,但是之前的学习忘得一干二净了,后悔当时没做笔记啊。

    所以,无奈想用的更好,就是得把源代码看楚,所以还是把源代码的注释笔记留下来,供自己备忘,也供新手学习。

    首先这里是Backbone.Events的实现代码注释,这是backbone的实现mvc的主要保障之一,这种基于事件的机制无论是在前端还是在后端nodejs部分

    都有着很广的作用,更何况nodejs的异步机制更需要它来处理一些逻辑问题(当然有人会说promise会比你这种pubsub更适合异步,哈,我只是像表明事件机制的

    重要性嘛)

    下面是对代码的注释,如果有错误还望指出来

      1   // Backbone.Events
      2   // ---------------
      3 
      4   // A module that can be mixed in to *any object* in order to provide it with
      5   // custom events. You may bind with `on` or remove with `off` callback
      6   // functions to an event; `trigger`-ing an event fires all callbacks in
      7   // succession.
      8   //
      9   //     var object = {};
     10   //     _.extend(object, Backbone.Events);
     11   //     object.on('expand', function(){ alert('expanded'); });
     12   //     object.trigger('expand');
     13   //
     14   var Events = Backbone.Events = {
     15 
     16     // Bind an event to a `callback` function. Passing `"all"` will bind
     17     // the callback to all events fired.
     18     on: function(name, callback, context) {
     19       // 两种情况:
     20       // 1. 一次添加多个事件时,通过eventsApi一个一个添加,所以eventsApi返回false,那么直接return
     21       // 2. 回调函数没定义,没有意义,直接return
     22       if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
     23       // 因此这里往下事件是一个一个添加的,即name是一个事件名(如:click | custom)
     24       // 初始化私有变量,用于存储事件的信息
     25       this._events || (this._events = {});
     26       var events = this._events[name] || (this._events[name] = []);
     27 
     28       // 这里可以写出events的结构了
     29       // _events : {
     30       //     click : [{
     31       //           callback : cb1,
     32       //           context : ctx1,
     33       //           ctx : ctx1 || this
     34       //     },{
     35       //           callback : cb2,
     36       //           context : ctx2,
     37       //           ctx : ctx2 || this
     38       //     }, ...],
     39       //     blur : [{...}, {...}, ...],
     40       //     ...
     41       // }
     42       events.push({callback: callback, context: context, ctx: context || this});
     43       return this;
     44     },
     45 
     46     // Bind an event to only be triggered a single time. After the first time
     47     // the callback is invoked, it will be removed.
     48     once: function(name, callback, context) {
     49       if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
     50       var self = this;
     51       // 将callback进行包装,返回的新函数newCallback内部会调用calllback,
     52       // 不同的是,newCallback只会调用callback一次,之后只会返回callback执行的结果
     53       // 也就是说once实质上并没有去除掉事件监听函数,而是控制了callback只会执行一次
     54       var once = _.once(function() {
     55         self.off(name, once);
     56         callback.apply(this, arguments);
     57       });
     58       // 保留原callback,用于off操作
     59       once._callback = callback;
     60       // 实质调用.on()方法注册事件
     61       return this.on(name, once, context);
     62     },
     63 
     64     // Remove one or many callbacks. If `context` is null, removes all
     65     // callbacks with that function. If `callback` is null, removes all
     66     // callbacks for the event. If `name` is null, removes all bound
     67     // callbacks for all events.
     68     off: function(name, callback, context) {
     69       var retain, ev, events, names, i, l, j, k;
     70       // 两种情况:
     71       // 1. 根本没注册过事件,何谈删除事件,直接return
     72       // 2. 像上述所说支持多事件删除
     73       if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
     74       // 如果是obj.off()这样调用,那么删除该对象obj上所有的事件监听
     75       // 也就是是将_events清空了
     76       if (!name && !callback && !context) {
     77         this._events = {};
     78         return this;
     79       }
     80 
     81       // 如果name为空,像obj.off(undefined, cb1, ctx1)
     82       // 那么name就为所有注册过的事件(即_.keys(this._events))
     83       names = name ? [name] : _.keys(this._events);
     84 
     85       // 根据name遍历events
     86       for (i = 0, l = names.length; i < l; i++) {
     87         name = names[i];
     88         if (events = this._events[name]) {
     89           this._events[name] = retain = [];
     90 
     91           // 如果callback或者context有一个有值
     92           // 那么接下来将它们作为条件进行接下来事件的off操作
     93           // 实质其实是先清空_events,将不满足条件删除条件的事件监听器重新填入_events中
     94           if (callback || context) {
     95             for (j = 0, k = events.length; j < k; j++) {
     96               ev = events[j];
     97               // 这里对指定了callback或者context的情况,做了条件判断
     98               // 这里的_callback是因为.once方法会对原callback进行包装,这里的evn.callback就是包装后的,原callback保存在_callback中
     99               if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
    100                   (context && context !== ev.context)) {
    101                 retain.push(ev);
    102               }
    103             }
    104           }
    105           // 发现该事件的事件监听器被删光了,那么做了清理工作,删除_events对应的key
    106           if (!retain.length) delete this._events[name];
    107         }
    108       }
    109 
    110       return this;
    111     },
    112 
    113     // Trigger one or many events, firing all bound callbacks. Callbacks are
    114     // passed the same arguments as `trigger` is, apart from the event name
    115     // (unless you're listening on `"all"`, which will cause your callback to
    116     // receive the true name of the event as the first argument).
    117     trigger: function(name) {
    118       if (!this._events) return this;
    119       // 分离出传给callback的参数
    120       var args = slice.call(arguments, 1);
    121       // 同样支持多事件同时trigger
    122       if (!eventsApi(this, 'trigger', name, args)) return this;
    123       // 该对象该事件的信息
    124       var events = this._events[name];
    125       // 该对象all事件的信息(all事件是一个特殊的事件,all事件在其他事件发生时都会被触发)
    126       var allEvents = this._events.all;
    127       // 下面两部分别是触发当前事件的回调以及all事件的回调
    128       // 不同的是,all事件的回调会被多传递一个触发all事件的当前事件的名字作为参数
    129       if (events) triggerEvents(events, args);
    130       if (allEvents) triggerEvents(allEvents, arguments);
    131       return this;
    132     },
    133 
    134     // Tell this object to stop listening to either specific events ... or
    135     // to every object it's currently listening to.
    136     stopListening: function(obj, name, callback) {
    137       var listeningTo = this._listeningTo;
    138       if (!listeningTo) return this;
    139       // 
    140       var remove = !name && !callback;
    141       // 这里是兼容(obj, {click: cb1, change: cb2})这种形式
    142       // 保证第三个参数是作为context传入,这里是this
    143       if (!callback && typeof name === 'object') callback = this;
    144       // 如果有指定obj,那么解除对象只针对obj
    145       // 如果没有指定,则是解除监听的所有对象的事件绑定
    146       if (obj) (listeningTo = {})[obj._listenId] = obj;
    147       for (var id in listeningTo) {
    148         obj = listeningTo[id];
    149         obj.off(name, callback, this);
    150         // 两种情况下做清理工作
    151         // 1. 已经表明清除对obj的的所有事件监听(即name和callback都为空)
    152         // 2. obj对象自身都没有被绑定事件了,哪来的事件让你监听呢?
    153         if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
    154       }
    155       return this;
    156     }
    157 
    158   };
    159 
    160   // Regular expression used to split event strings.
    161   var eventSplitter = /s+/;
    162 
    163   // Implement fancy features of the Events API such as multiple event
    164   // names `"change blur"` and jQuery-style event maps `{change: action}`
    165   // in terms of the existing API.
    166   // ({}, 'on', 'click blur', [function () {}, undefined])
    167   var eventsApi = function(obj, action, name, rest) {
    168     if (!name) return true;
    169 
    170     // Handle event maps.
    171     // 支持映射关系
    172     // 如:(obj, 'on', {'click': function x () {}, 'blur': function xx () {}}, context)
    173     if (typeof name === 'object') {
    174       for (var key in name) {
    175         // 反复调用action(on | off | once), 每次添加一个事件监听,从而达到添加多个。
    176         obj[action].apply(obj, [key, name[key]].concat(rest));
    177       }
    178       return false;
    179     }
    180 
    181     // Handle space separated event names.
    182     // 支持空格分割事件(即多事件共享同一个函数)
    183     // 如:(obj, 'on', 'click blur', function () {}, context)
    184     if (eventSplitter.test(name)) {
    185       var names = name.split(eventSplitter);
    186       for (var i = 0, l = names.length; i < l; i++) {
    187         obj[action].apply(obj, [names[i]].concat(rest));
    188       }
    189       return false;
    190     }
    191 
    192     return true;
    193   };
    194 
    195   // A difficult-to-believe, but optimized internal dispatch function for
    196   // triggering events. Tries to keep the usual cases speedy (most internal
    197   // Backbone events have 3 arguments).
    198   // 这里做了个优化,就是如果arg参数在3个之类的话,用call进行调用,
    199   // 因为call要比apply的效率高(http://jsperf.com/function-versus-function-call-versus-function-apply)
    200   var triggerEvents = function(events, args) {
    201     var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
    202     switch (args.length) {
    203       case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
    204       case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
    205       case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
    206       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
    207       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
    208     }
    209   };
    210 
    211   var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
    212 
    213   // Inversion-of-control versions of `on` and `once`. Tell *this* object to
    214   // listen to an event in another object ... keeping track of what it's
    215   // listening to.
    216   // 添加listenTo和listernToOnce方法
    217   // 实质是:
    218   // 1. 给需要监听的对象obj赋予一个_listenId的随机id
    219   // 2. 再给监听者(调用对象)添加一个map就是listeningTo属性,添加上述的id和obj
    220   // 3. 给obj绑定被监听的事件被将this指向调用者
    221   // 这里实质就是调用obj的on或者once方法来添加事件监听,
    222   // 那么单独列出这样的一个方法的好处在于方便监听者,可以随时监听和解除监听,上述的1,2两不操作是为了以后解除监听做准备
    223 
    224   _.each(listenMethods, function(implementation, method) {
    225     Events[method] = function(obj, name, callback) {
    226       var listeningTo = this._listeningTo || (this._listeningTo = {});
    227       var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
    228       listeningTo[id] = obj;
    229       // 这里是兼容(obj, {click: cb1, change: cb2})这种形式
    230       // 保证第三个参数是作为context传入,这里是this
    231       if (!callback && typeof name === 'object') callback = this;
    232       obj[implementation](name, callback, this);
    233       return this;
    234     };
    235   });
    236 
    237   // Aliases for backwards compatibility.
    238   // 向上兼容
    239   Events.bind   = Events.on;
    240   Events.unbind = Events.off;
    241 
    242   // Allow the `Backbone` object to serve as a global event bus, for folks who
    243   // want global "pubsub" in a convenient place.
    244   // 将Backbone对像拥有事件绑定机制
    245   _.extend(Backbone, Events);

    各位晚安~~

  • 相关阅读:
    [AWS] Export and Import Data from DynamoDB Table 从 DynamoDB 数据库中导入和导出数据
    [LeetCode] 944. Delete Columns to Make Sorted 删除列使其有序
    [LeetCode] 943. Find the Shortest Superstring 找到最短的超级字符串
    [LeetCode] 942. DI String Match 增减DI字符串匹配
    [LeetCode] 941. Valid Mountain Array 验证山形数组
    [LeetCode] 940. Distinct Subsequences II 不同的子序列之二
    [LeetCode] 939. Minimum Area Rectangle 面积最小的矩形
    [LeetCode] 938. Range Sum of BST 二叉搜索树的区间和
    [LeetCode] 937. Reorder Data in Log Files 日志文件的重新排序
    [LeetCode] 936. Stamping The Sequence 戳印序列
  • 原文地址:https://www.cnblogs.com/lovesueee/p/3501156.html
Copyright © 2011-2022 走看看