摘要:帶注釋源碼用于分割事件名的正則,識(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
摘要:本文同步自我的博客前言這個(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)部...
摘要:先來(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ò)程借...
摘要:系列文章讀源碼之篇提供基本的屬性添加獲取移除等功能。判斷是否為等對(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í)...
摘要:擁有了和方法的三個(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)源精神,我是您的粉絲。 這里分享下...
摘要:五作用的關(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的文檔處理就告一段落了,有空我...
閱讀 3525·2023-04-26 00:16
閱讀 1361·2021-11-25 09:43
閱讀 3824·2021-11-23 09:51
閱讀 2964·2021-09-24 09:55
閱讀 713·2021-09-22 15:45
閱讀 1387·2021-07-30 15:30
閱讀 3064·2019-08-30 14:04
閱讀 2237·2019-08-26 13:46