D3原始碼解讀系列之Dispatches

arlendp2012發表於2019-11-01

d3的dispatch模組是對原生事件處理的封裝,通過該模組可以註冊自定義的事件並繫結回撥函式。

d3.dispatch

該模組用於註冊自定義名稱的回撥函式,並且可以呼叫這些函式。

function dispatch() {
    /*將傳入的引數作為鍵值存入Dispatch物件中,引數以陣列的形式傳入並且不能有重複的元素
     *初始化時引數只包含型別,不應包含名稱,併入初始化時可以傳入['click', 'drag'],在之後呼叫on方法時可以通過on('click.my1 drag.my2 hover', callback)這種方式來繫結回撥函式
     *在dispatch中是以如下方式儲存:
     *
     *  {
     *    'click': [
     *      {
     *        name: 'my1',
     *        value: callback
     *      }
     *    ],
     *    'drag': [
     *      {
     *        name: 'my2',
     *        value: callback
     *      }
     *    ],
     *    'hover': [
     *      {
     *        name: '',
     *        value: callback
     *      }
     *    ]
     *  }
     */
    for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
        if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t);
        _[t] = [];
    }
    return new Dispatch(_);
}
//Dispatch建構函式
function Dispatch(_) {
    this._ = _;
}

複製程式碼

將傳入的事件型別存入dispatch物件中。

dispatch.on(typenames[, callback])

用於將事件和回撥函式進行繫結。

//繫結事件型別和回撥函式
  on: function(typename, callback) {
      var _ = this._,
          T = parseTypenames(typename + "", _),
          t,
          i = -1,
          n = T.length;

      // 如果沒有callback引數,則返回以指定type和name註冊的callback函式。
      if (arguments.length < 2) {
        while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
        return;
      }

      // 如果傳入了callback函式,則對指定的type和name設定該回撥函式。
      // 如果callback為null,則可以移除指定的回撥函式
      if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
      while (++i < n) {
        if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback);
        else if (callback == null) for (t in _) _[t] = set$1(_[t], typename.name, null);
      }

      return this;
  },
  
  //獲取回撥函式
  function get(type, name) {
    for (var i = 0, n = type.length, c; i < n; ++i) {
      if ((c = type[i]).name === name) {
        return c.value;
      }
    }
  }
  //設定回撥函式
  function set$1(type, name, callback) {
    for (var i = 0, n = type.length; i < n; ++i) {
      if (type[i].name === name) {
        //如果type中已有指定的name,則將其從type陣列中移除
        type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1));
        break;
      }
    }
    if (callback != null) type.push({name: name, value: callback});
    return type;
  }
複製程式碼

其他方法:

//對dispatch進行拷貝,對拷貝後的內容進行修改不會影響之前的內容
copy: function() {
    var copy = {}, _ = this._;
    for (var t in _) copy[t] = _[t].slice();
    return new Dispatch(copy);
},
call: function(type, that) {
    //第二個引數之後的引數會傳入callback函式中
    if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
    //會呼叫type下的所有回撥函式
    for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
},
apply: function(type, that, args) {
    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
    for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
}
複製程式碼

相關文章