摘要:帶注釋源碼用于分割事件名的正則,識別空格介紹使用方法,這個模塊可以混入任何對象之中,實現(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
摘要:本文同步自我的博客前言這個模塊實際上才是模塊系統(tǒng)中對外的模塊,它包含了之前介紹的類和類,以及自己內(nèi)部的模塊和模塊,因此模塊是真正的基礎(chǔ)類。這兩個方法的作用就是針對類上的某個方法,給這個方法綁定先于其執(zhí)行和后于其執(zhí)行的回調(diào)函數(shù)。 本文同步自我的GitHub博客 前言 Base這個模塊實際上才是Arale模塊系統(tǒng)中對外的模塊,它包含了之前介紹的Class類和Events類,以及自己內(nèi)部...
摘要:先來看源碼中,首先是做的是參數(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ā)過程借...
摘要:系列文章讀源碼之篇提供基本的屬性添加獲取移除等功能。判斷是否為等對象特性檢查閉包實現(xiàn)塊作用域,不污染全局變量。找這個屬性,若沒有則返回空對象執(zhí)行函數(shù),返回被修改的值。 系列文章:讀 arale 源碼之 class 篇 attributes 提供基本的屬性添加、獲取、移除等功能。它是與實例相關(guān)的狀態(tài)信息,可讀可寫,發(fā)生變化時,會自動觸發(fā)相關(guān)事件 先來了解一下 Attribute 模塊要實...
摘要:擁有了和方法的三個變種屬性這三個屬性會做特殊處理繼承的方法,只支持單繼承建立原型鏈來實現(xiàn)繼承強(qiáng)制改變構(gòu)造函數(shù)提供語法糖,來調(diào)用父類屬性混入屬性,可以混入多個類的屬性將參數(shù)變成數(shù)組無論參數(shù)是類,還是對象,都混入。 更新:讀 arale 源碼之 attribute 篇 arale 是阿里、開源社區(qū)明星人物--玉伯,開發(fā)的一套組件,代碼相當(dāng)優(yōu)美,大贊玉伯的開源精神,我是您的粉絲。 這里分享下...
摘要:五作用的關(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的文檔處理就告一段落了,有空我...
閱讀 3550·2023-04-26 00:16
閱讀 1367·2021-11-25 09:43
閱讀 3836·2021-11-23 09:51
閱讀 2975·2021-09-24 09:55
閱讀 726·2021-09-22 15:45
閱讀 1402·2021-07-30 15:30
閱讀 3072·2019-08-30 14:04
閱讀 2254·2019-08-26 13:46