国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Arale源碼解析(2)——Events

adie / 3399人閱讀

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

帶注釋源碼
// Regular expression used to split event strings
// 用于分割事件名的正則,識(shí)別空格
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");
//
// 介紹使用方法,這個(gè)模塊可以混入任何對(duì)象之中,實(shí)現(xiàn)對(duì)自定義事件的資瓷~
function Events() {
}


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

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

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

  return this
}

// 綁定只執(zhí)行一次就銷(xiāo)毀的事件回調(diào)
Events.prototype.once = function(events, callback, context) {
  var that = this
  // 對(duì)傳入的`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.
// 移除一個(gè)或多個(gè)回調(diào),如果`context`為空,移除所有同名的回調(diào)。
// 如果`callback`為空,移除該事件上所有回調(diào)。
// 如果`events`為空,移除所有時(shí)間上綁定的所有回調(diào)函數(shù)。
Events.prototype.off = function(events, callback, context) {
  var cache, event, list, i

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

  // 對(duì)傳入的`events`進(jìn)行分割處理,如果沒(méi)有傳入`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)隊(duì)列
    list = cache[event]
    // 如隊(duì)列為空,跳過(guò)
    if (!list) continue

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

    // 遍歷回調(diào)隊(duì)列,注意每個(gè)回調(diào)和其調(diào)用上下文是間隔排列的,步長(zhǎng)為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;
  // 如果沒(méi)有綁定過(guò)任何事件,直接返回
  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.
  // 將除第一個(gè)參數(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.
  // 對(duì)于每個(gè)事件,遍歷兩次回調(diào)隊(duì)列,第一次是觸發(fā)那個(gè)事件,第二次是觸發(fā)任何`all`事件的回調(diào)
  while (event = events.shift()) {
    // Copy callback lists to prevent modification.
    // 如果緩存中存在all事件,將其回調(diào)隊(duì)列分割存入all
    if (all = cache.all) all = all.slice()
    // 如果緩存中有當(dāng)前遍歷到的事件,將其回調(diào)隊(duì)列分割存入list
    if (list = cache[event]) list = list.slice()

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

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

  // 返回值
  return returned
}

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


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

// 不存在`Object.keys`方法時(shí)就自己實(shí)現(xiàn)
if (!keys) {
  // 接受一個(gè)對(duì)象,返回該對(duì)象所有自有屬性
  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`混入任何一個(gè)Class類(lèi)的實(shí)例
Events.mixTo = function(receiver) {
  // 保存`Events`的原型
  var proto = Events.prototype

  // 判斷接收對(duì)象類(lèi)型,是否為構(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)過(guò)調(diào)試這步和上步的作用是一樣的,只是調(diào)用了ES5的API,不知是否和兼容性有關(guān)
    Object.keys(proto).forEach(function(key) {
      receiver.prototype[key] = proto[key]
    })
  }
  else { // 針對(duì)接收者不是構(gòu)造函數(shù)而是實(shí)例的情況
    // 生成Event類(lèi)的實(shí)例
    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è)對(duì)象而不是構(gòu)造函數(shù),所以將所有方法的執(zhí)行上下文轉(zhuǎn)換為一個(gè)Event類(lèi)的實(shí)例
      proto[key].apply(event, Array.prototype.slice.call(arguments))
      return this
    }
  }
}

// Execute callbacks
/**
 * 執(zhí)行回調(diào)的方法
 * @param  {Array} list    回調(diào)函數(shù)隊(duì)列
 * @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`快,因此針對(duì)參數(shù)數(shù)量少于等于3個(gè)的情況進(jìn)行優(yōu)化,調(diào)用`call`,參數(shù)數(shù)量大于3個(gè)時(shí)調(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
  // 有一個(gè)回調(diào)函數(shù)的返回值為false則pass值為false
  return pass;
}

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

module.exports = Events
分析

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

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

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/85385.html

相關(guān)文章

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

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

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

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

    _ivan 評(píng)論0 收藏0
  • arale 源碼之 attribute 篇

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

    Magicer 評(píng)論0 收藏0
  • arale 源碼之 class 篇

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

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

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

    coolpail 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<