成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Arale源碼解析(2)——Events

adie / 3405人閱讀

摘要:帶注釋源碼用于分割事件名的正則,識別空格介紹使用方法,這個模塊可以混入任何對象之中,實現(xiàn)對自定義事件的資瓷將空格分割的事件綁定給對象,事件名為的話,事件回調(diào)函數(shù)在任何事件被觸發(fā)時都會調(diào)用。

帶注釋源碼
// Regular expression used to split event strings
// 用于分割事件名的正則,識別空格
var eventSplitter = /s+/


// A module that can be mixed in to *any object* in order to provide it
// with custom events. You may bind with `on` or remove with `off` callback
// functions to an event; `trigger`-ing an event fires all callbacks in
// succession.
//
//     var object = new Events();
//     object.on("expand", function(){ alert("expanded"); });
//     object.trigger("expand");
//
// 介紹使用方法,這個模塊可以混入任何對象之中,實現(xiàn)對自定義事件的資瓷~
function Events() {
}


// Bind one or more space separated events, `events`, to a `callback`
// function. Passing `"all"` will bind the callback to all events fired.
// 將空格分割的事件綁定給對象,事件名為all的話,事件回調(diào)函數(shù)在任何事件被觸發(fā)時都會調(diào)用。
Events.prototype.on = function(events, callback, context) {
  var cache, event, list
  // 回調(diào)函數(shù)不存在,直接返回
  if (!callback) return this

  // 將對象的`__events`屬性緩存,`__events`屬性不存在則初始化為空對象
  cache = this.__events || (this.__events = {})
  // 將參數(shù)中的事件字符串進(jìn)行分割,得到事件名數(shù)組
  events = events.split(eventSplitter)

  // 循環(huán)遍歷`events`中的事件
  while (event = events.shift()) {
    // 查詢cache中是否緩存了事件,如果有,取得這個事件的回調(diào)函數(shù)隊列的引用,如果沒有,初始化為空數(shù)組
    list = cache[event] || (cache[event] = [])
    // 將回調(diào)和上下文存入回調(diào)函數(shù)隊列
    list.push(callback, context)
  }

  return this
}

// 綁定只執(zhí)行一次就銷毀的事件回調(diào)
Events.prototype.once = function(events, callback, context) {
  var that = this
  // 對傳入的`callback`進(jìn)行一次封裝,`cb`內(nèi)調(diào)用`off`方法,調(diào)用一次就解綁
  var cb = function() {
    that.off(events, cb)
    callback.apply(context || that, arguments)
  }
  // 將封裝后的`cb`進(jìn)行綁定
  return this.on(events, cb, context)
}

// Remove one or many callbacks. If `context` is null, removes all callbacks
// with that function. If `callback` is null, removes all callbacks for the
// event. If `events` is null, removes all bound callbacks for all events.
// 移除一個或多個回調(diào),如果`context`為空,移除所有同名的回調(diào)。
// 如果`callback`為空,移除該事件上所有回調(diào)。
// 如果`events`為空,移除所有時間上綁定的所有回調(diào)函數(shù)。
Events.prototype.off = function(events, callback, context) {
  var cache, event, list, i

  // No events, or removing *all* events.
  // 如果沒有任何已綁定事件,直接返回
  if (!(cache = this.__events)) return this
  // 如果三個參數(shù)都沒傳,則刪除對象上的`__events`屬性,并返回對象
  if (!(events || callback || context)) {
    delete this.__events
    return this
  }

  // 對傳入的`events`進(jìn)行分割處理,如果沒有傳入`events`,取得緩存中的所有事件
  events = events ? events.split(eventSplitter) : keys(cache)

  // Loop through the callback list, splicing where appropriate.
  // 循環(huán)遍歷events
  while (event = events.shift()) {
    // 保存事件回調(diào)隊列
    list = cache[event]
    // 如隊列為空,跳過
    if (!list) continue

    // 如果`callback`和`context`都沒傳,則刪除該事件隊列
    if (!(callback || context)) {
      delete cache[event]
      continue
    }

    // 遍歷回調(diào)隊列,注意每個回調(diào)和其調(diào)用上下文是間隔排列的,步長為2
    // 和傳入的`callback`以及`context`比較,都符合的則將回調(diào)和調(diào)用上下文從數(shù)組中移除
    for (i = list.length - 2; i >= 0; i -= 2) {
      if (!(callback && list[i] !== callback ||
          context && list[i + 1] !== context)) {
        list.splice(i, 2)
      }
    }
  }

  return this
}


// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// (unless you"re listening on `"all"`, which will cause your callback to
// receive the true name of the event as the first argument).
Events.prototype.trigger = function(events) {
  var cache, event, all, list, i, len, rest = [], args, returned = true;
  // 如果沒有綁定過任何事件,直接返回
  if (!(cache = this.__events)) return this

  // 分割
  events = events.split(eventSplitter)

  // Fill up `rest` with the callback arguments.  Since we"re only copying
  // the tail of `arguments`, a loop is much faster than Array#slice.
  // 將除第一個參數(shù)`events`外的所有參數(shù)保存保存為數(shù)組存入`rest`
  for (i = 1, len = arguments.length; i < len; i++) {
    rest[i - 1] = arguments[i]
  }

  // For each event, walk through the list of callbacks twice, first to
  // trigger the event, then to trigger any `"all"` callbacks.
  // 對于每個事件,遍歷兩次回調(diào)隊列,第一次是觸發(fā)那個事件,第二次是觸發(fā)任何`all`事件的回調(diào)
  while (event = events.shift()) {
    // Copy callback lists to prevent modification.
    // 如果緩存中存在all事件,將其回調(diào)隊列分割存入all
    if (all = cache.all) all = all.slice()
    // 如果緩存中有當(dāng)前遍歷到的事件,將其回調(diào)隊列分割存入list
    if (list = cache[event]) list = list.slice()

    // Execute event callbacks except one named "all"
    // 當(dāng)遍歷到的事件名不是all時,觸發(fā)事件的所有回調(diào),以this作為調(diào)用上下文
    if (event !== "all") {
      returned = triggerEvents(list, rest, this) && returned
    }

    // Execute "all" callbacks.
    // 觸發(fā)對應(yīng)all事件的所有回調(diào)
    returned = triggerEvents(all, [event].concat(rest), this) && returned
  }

  // 返回值
  return returned
}

// trigger == emit
Events.prototype.emit = Events.prototype.trigger


// Helpers
// -------
// 保存對`Object.keys`方法的引用
var keys = Object.keys

// 不存在`Object.keys`方法時就自己實現(xiàn)
if (!keys) {
  // 接受一個對象,返回該對象所有自有屬性
  keys = function(o) {
    var result = []

    for (var name in o) {
      if (o.hasOwnProperty(name)) {
        result.push(name)
      }
    }
    return result
  }
}

// Mix `Events` to object instance or Class function.
// 將`Events`混入任何一個Class類的實例
Events.mixTo = function(receiver) {
  // 保存`Events`的原型
  var proto = Events.prototype

  // 判斷接收對象類型,是否為構(gòu)造函數(shù)
  if (isFunction(receiver)) {
    // 遍歷`Events`原型內(nèi)方法
    for (var key in proto) {
      // 將自有方法進(jìn)行復(fù)制
      if (proto.hasOwnProperty(key)) {
        receiver.prototype[key] = proto[key]
      }
    }
    // 經(jīng)過調(diào)試這步和上步的作用是一樣的,只是調(diào)用了ES5的API,不知是否和兼容性有關(guān)
    Object.keys(proto).forEach(function(key) {
      receiver.prototype[key] = proto[key]
    })
  }
  else { // 針對接收者不是構(gòu)造函數(shù)而是實例的情況
    // 生成Event類的實例
    var event = new Events
    // 遍歷,判斷,復(fù)制
    for (var key in proto) {
      if (proto.hasOwnProperty(key)) {
        copyProto(key)
      }
    }
  }

  // 復(fù)制屬性
  function copyProto(key) {
    receiver[key] = function() {
      // 由于receiver已經(jīng)是一個對象而不是構(gòu)造函數(shù),所以將所有方法的執(zhí)行上下文轉(zhuǎn)換為一個Event類的實例
      proto[key].apply(event, Array.prototype.slice.call(arguments))
      return this
    }
  }
}

// Execute callbacks
/**
 * 執(zhí)行回調(diào)的方法
 * @param  {Array} list    回調(diào)函數(shù)隊列
 * @param  {Array} args    參數(shù)數(shù)組
 * @param  {Object} context 調(diào)用上下文
 * @return {Boolean} pass
 */
function triggerEvents(list, args, context) {
  var pass = true

  if (list) {
    var i = 0, l = list.length, a1 = args[0], a2 = args[1], a3 = args[2]
    // call is faster than apply, optimize less than 3 argu
    // http://blog.csdn.net/zhengyinhui100/article/details/7837127
    // 由于`call`方法要比`apply`快,因此針對參數(shù)數(shù)量少于等于3個的情況進(jìn)行優(yōu)化,調(diào)用`call`,參數(shù)數(shù)量大于3個時調(diào)用`apply`
    switch (args.length) {
      case 0: for (; i < l; i += 2) {pass = list[i].call(list[i + 1] || context) !== false && pass} break;
      case 1: for (; i < l; i += 2) {pass = list[i].call(list[i + 1] || context, a1) !== false && pass} break;
      case 2: for (; i < l; i += 2) {pass = list[i].call(list[i + 1] || context, a1, a2) !== false && pass} break;
      case 3: for (; i < l; i += 2) {pass = list[i].call(list[i + 1] || context, a1, a2, a3) !== false && pass} break;
      default: for (; i < l; i += 2) {pass = list[i].apply(list[i + 1] || context, args) !== false && pass} break;
    }
  }
  // trigger will return false if one of the callbacks return false
  // 有一個回調(diào)函數(shù)的返回值為false則pass值為false
  return pass;
}

// 判斷是否為Function類型的工具函數(shù)
function isFunction(func) {
  return Object.prototype.toString.call(func) === "[object Function]"
}

module.exports = Events
分析

這個Events類的運行方式還是比較簡單的,這里就把實現(xiàn)機(jī)制歸納一下,這個應(yīng)該是學(xué)習(xí)的重點。具體代碼層面的實現(xiàn)看源碼和注釋就行了。

事件的所有相關(guān)信息全部保存在對象的__events屬性上,該屬性值是一個對象,以k-v的形式保存事件名和回調(diào)隊列的對應(yīng)關(guān)系,結(jié)構(gòu)就像這樣:

{
  "click": [callback1, context1, callback2, context2, ...],
  "remove": [callback1, context1, callback2, context2, ...],
  ...
}

一旦觸發(fā)了某個事件,比如click,那么它對應(yīng)的回調(diào)隊列中的所有回調(diào)函數(shù)就會依次被執(zhí)行。值得一提的時每個回調(diào)函數(shù)都有各自的執(zhí)行上下文對象,這個比較特別,回調(diào)和上下文在數(shù)組中是間隔排列的,因此觸發(fā)事件和解除綁定時都會特別處理這種特殊的數(shù)據(jù)結(jié)構(gòu)。我認(rèn)為之所以選用數(shù)組這種結(jié)構(gòu)主要還是為了保證所有回調(diào)的觸發(fā)順序可控,如果用對象的話,遍歷時的順序是不一定的。針對這個問題,在玉伯和一位開發(fā)者的討論中也能得到答案。

另外值得一提的是all這個事件,在一個對象上觸發(fā)任何事件,同時也一定會觸發(fā)all事件,實現(xiàn)原理很簡單,就是在trigger這個方法中,判斷一下事件緩存中有沒有all這個事件隊列,如果有,那么不管觸發(fā)哪個事件,最后都再觸發(fā)一下all事件隊列即可。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85385.html

相關(guān)文章

  • Arale源碼解析(3)——Base模塊和Aspect模塊

    摘要:本文同步自我的博客前言這個模塊實際上才是模塊系統(tǒng)中對外的模塊,它包含了之前介紹的類和類,以及自己內(nèi)部的模塊和模塊,因此模塊是真正的基礎(chǔ)類。這兩個方法的作用就是針對類上的某個方法,給這個方法綁定先于其執(zhí)行和后于其執(zhí)行的回調(diào)函數(shù)。 本文同步自我的GitHub博客 前言 Base這個模塊實際上才是Arale模塊系統(tǒng)中對外的模塊,它包含了之前介紹的Class類和Events類,以及自己內(nèi)部...

    stdying 評論0 收藏0
  • Arale源碼解析(1)——Class

    摘要:先來看源碼中,首先是做的是參數(shù)的處理工作,針對某些參數(shù)未傳的情況作了調(diào)整,最后達(dá)到的效果是的值為傳入的父類構(gòu)造函數(shù),如果沒有,設(shè)為。下一個語句其作用是處理父類構(gòu)造函數(shù)沒有修改的屬性值并且有方法的時候,在上調(diào)用方法。 本文同步自我的GitHub 概述 Arale是支付寶開發(fā)的一套基礎(chǔ)類庫,提供了一整套前端模塊架構(gòu),基于CMD規(guī)范,所有模塊均是以sea.js的標(biāo)準(zhǔn)進(jìn)行開發(fā)。其開發(fā)過程借...

    _ivan 評論0 收藏0
  • arale 源碼之 attribute 篇

    摘要:系列文章讀源碼之篇提供基本的屬性添加獲取移除等功能。判斷是否為等對象特性檢查閉包實現(xiàn)塊作用域,不污染全局變量。找這個屬性,若沒有則返回空對象執(zhí)行函數(shù),返回被修改的值。 系列文章:讀 arale 源碼之 class 篇 attributes 提供基本的屬性添加、獲取、移除等功能。它是與實例相關(guān)的狀態(tài)信息,可讀可寫,發(fā)生變化時,會自動觸發(fā)相關(guān)事件 先來了解一下 Attribute 模塊要實...

    Magicer 評論0 收藏0
  • arale 源碼之 class 篇

    摘要:擁有了和方法的三個變種屬性這三個屬性會做特殊處理繼承的方法,只支持單繼承建立原型鏈來實現(xiàn)繼承強(qiáng)制改變構(gòu)造函數(shù)提供語法糖,來調(diào)用父類屬性混入屬性,可以混入多個類的屬性將參數(shù)變成數(shù)組無論參數(shù)是類,還是對象,都混入。 更新:讀 arale 源碼之 attribute 篇 arale 是阿里、開源社區(qū)明星人物--玉伯,開發(fā)的一套組件,代碼相當(dāng)優(yōu)美,大贊玉伯的開源精神,我是您的粉絲。 這里分享下...

    firim 評論0 收藏0
  • jQuery源碼解析之clone()

    摘要:五作用的關(guān)鍵方法,用來從目標(biāo)節(jié)點克隆數(shù)據(jù)添加事件給克隆的元素注意采用數(shù)據(jù)分離的方法來保存上的事件和數(shù)據(jù),利用標(biāo)記每個元素,然后在內(nèi)存上,將每個元素相關(guān)的數(shù)據(jù)放到內(nèi)存中,然后在和內(nèi)存的數(shù)據(jù)之間建立映射。 showImg(https://segmentfault.com/img/remote/1460000018991125); 前言:這篇講完后,jQuery的文檔處理就告一段落了,有空我...

    coolpail 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<