日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

YUI事件体系之Y.CustomEvent

發(fā)布時間:2024/7/5 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 YUI事件体系之Y.CustomEvent 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇文章中,簡要介紹了YUI實現(xiàn)AOP的Y.Do對象。

接下來,我們繼續(xù)對YUI事件體系進(jìn)行探索。本次要介紹的是Y.CustomEvent對象,從命名上就可以看出,這個對象在整個YUI事件體系中十分重要。它建立起整個自定義事件的體系,而且,DOM事件也構(gòu)建在這個體系之上。

Y.Subscriber

Y.Subscriber的作用比較簡單:執(zhí)行回調(diào)函數(shù)。

Y.Subscriber = function (fn, context) {this.fn = fn; // 回調(diào)函數(shù)this.context = context; // 上下文this.id = Y.stamp(this); // 設(shè)置唯一id }; Y.Subscriber.prototype = {constructor: Y.Subscriber,// 執(zhí)行回調(diào)函數(shù)notify: function (args, ce) {if (this.deleted) return null;var ret;ret = this.fn.apply(this.context, args || []);// 只監(jiān)聽一次if (this.once) {ce._delete(this);}return ret;} };

Y.CustomEvent

Y.CustomEvent主要作用是:建立自定義事件機制,為方便的進(jìn)行事件創(chuàng)建、監(jiān)聽、觸發(fā)提供良好基礎(chǔ)。自定義事件機制,實際上是Observer Pattern(Publish–subscribe Pattern的演化)的一種實現(xiàn),這種機制能夠方便的實現(xiàn)模塊間解耦,增強模塊的擴(kuò)展性。

YUI的自定義事件較其它一些js庫來說要強大一些,有這樣一些好的features:

  • 支持事件接口(Event Facade),在回調(diào)函數(shù)中可以進(jìn)行調(diào)用
  • 支持設(shè)置默認(rèn)執(zhí)行方法
  • 支持停止/立即停止傳播,并可設(shè)定停止傳播時執(zhí)行的方法
  • 支持阻止默認(rèn)行為(默認(rèn)執(zhí)行方法),并可設(shè)定阻止默認(rèn)行為時執(zhí)行的方法
  • 支持冒泡。指定冒泡目標(biāo)序列,就可以順序的觸發(fā)事件(需要Y.EventTarget)
  • 支持廣播。每個自定義事件,都可以設(shè)置在當(dāng)前YUI實例范圍內(nèi)和全局YUI內(nèi)進(jìn)行廣播

可以看出,YUI的自定義事件和DOM事件極其類似,這種設(shè)計自然到我們在用自定義事件時,絲毫感覺不到和DOM事件的差異。

示例

讓我們先來看個簡單的例子:

// 例1 簡單自定義事件 YUI().use('event-custom', function (Y) {var eatEvent = new Y.CustomEvent('eat');var onHandle = eatEvent.on(function () {Y.log('before eating');});var onHandle2 = eatEvent.on(function () {Y.log('before eating, too');});var afterHandle = eatEvent.after(function () {Y.log('after eating');}); // output: "before eating", "before eating, too", "after eating"eatEvent.fire();onHandle2.detach();// output: "before eating", "after eating"eatEvent.fire(); });

有些事件只需觸發(fā)一次,比如你的各種第一次~~~。來看這個例子:

// 例2 僅觸發(fā)一次的自定義事件 YUI().use('event-custom', function (Y) {var birthEvent = new Y.CustomEvent('birth', {fireOnce: true // you can only birth once});var onBirthHandle = birthEvent.on(function () {Y.log('before birth');});// output: "before birth"birthEvent.fire();// nothing happenedbirthEvent.fire();// 只觸發(fā)一次的事件在觸發(fā)后,再次添加監(jiān)聽方法時,會被立即執(zhí)行// output: before birth, toovar onBirthHandle2 = birthEvent.on(function () {Y.log('before birth, too');}); });

也許你還在琢磨,事件廣播是什么?因為YUI使用了sandbox設(shè)計,可以生成不同實例綁定不同api,所以才有了事件廣播機制。來看這個例子:

// 例3 事件廣播 YUI().use('event-custom', function (Y) {var cryEvent = new Y.CustomEvent('cry', {broadcast: 2 // global broadcast});cryEvent.on(function () {Y.log('before cry');});Y.on('cry', function () {Y.log('YUI instance broadcast');});Y.Global.on('cry', function () {Y.log('YUI global broadcast');});// output: "before cry", "YUI instance broadcast", "YUI global broadcast"cryEvent.fire(); });

文章之前介紹過YUI自定義事件的種種NB之處,那么用起來如何呢,來看下面的例子:

// 例4 復(fù)雜自定義事件 YUI().use('event-custom', function (Y) {var driveEvent = new Y.CustomEvent('drive', {emitFacade: true,host: { // hacking. 復(fù)雜自定義事件需要指定host,該host必須augment Y.EventTarget_yuievt: {},_monitor: function () {}},defaultFn: function () {Y.log('execute defaultFn');},preventedFn: function () {Y.log('execute preventedFn');},stoppedFn: function () {Y.log('execute stoppedFn');}});driveEvent.on(function (e) {e.stopImmediatePropagation();});driveEvent.on(function (e) {e.preventDefault();});driveEvent.after(function (e) {Y.log('after driving');});// output: "execute stoppedFn", "execute defaultFn"driveEvent.fire(); });

不要失望,現(xiàn)在還沒有介紹到事件體系的精華部分Y.EventTarget,所以很多特性(例如冒泡)還不能體現(xiàn)出來,拭目以待吧。

源代碼分析

接下來,讓我們看看YUI的內(nèi)部實現(xiàn)吧。

注:為了更容易的看懂代碼的核心,我做了適當(dāng)?shù)暮喕?#xff0c;感興趣的朋友可以去看未刪節(jié)的源碼。

var AFTER = 'after',// config白名單CONFIGS = ['broadcast', 'monitored', 'bubbles', 'context', 'contextFn', 'currentTarget', 'defaultFn', 'defaultTargetOnly', 'details', 'emitFacade', 'fireOnce', 'async', 'host', 'preventable', 'preventedFn', 'queuable', 'silent', 'stoppedFn', 'target', 'type'];Y.CustomEvent = function (type, o) {this.id = Y.stamp(this);this.type = type;this.context = Y;this.preventable = true;this.bubbles = true;this.subscribers = {}; // (前置)監(jiān)聽對象容器 注:YUI3.7.0將此處進(jìn)行了優(yōu)化this.afters = {}; // 后置監(jiān)聽對象容器 注:YUI3.7.0將此處進(jìn)行了優(yōu)化this.subCount = 0;this.afterCount = 0;o = o || {};this.applyConfig(o, true); }; Y.CustomEvent.prototype = {constructor: Y.CustomEvent,// 設(shè)置參數(shù)applyConfig: function (o, force) {if (o) {Y.mix(this, o, force, CONFIGS);}},// 添加前置監(jiān)聽對象on: function (fn, context) {var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;return this._on(fn, context, a, true);},// 添加后置監(jiān)聽對象after: function (fn, context) {var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;return this._on(fn, context, a, AFTER);},// 內(nèi)部添加監(jiān)聽對象_on: function (fn, context, args, when) {var s = new Y.Subscriber(fn, context);if (this.fireOnce && this.fired) {// 僅觸發(fā)一次的事件在觸發(fā)后,再次添加監(jiān)聽方法時,會被立即執(zhí)行this._notify(s, this.firedWith);}if (when == AFTER) {this.afters[s.id] = s;this.afterCount++;} else {this.subscribers[s.id] = s;this.subCount++;}return new Y.EventHandle(this, s);},// 觸發(fā)事件fire: function () {if (this.fireOnce && this.fired) {// 僅觸發(fā)一次的事件,如果已經(jīng)觸發(fā)過,直接返回truereturn true;} else {// 可以設(shè)置參數(shù),傳給回調(diào)函數(shù)var args = Y.Array(arguments, 0, true);this.fired = true;this.firedWith = args;if (this.emitFacade) {// 復(fù)雜事件return this.fireComplex(args);} else {return this.fireSimple(args);}}},// 觸發(fā)簡單事件fireSimple: function (args) {this.stopped = 0;this.prevented = 0;if (this.hasSubs()) {var subs = this.getSubs();// 處理前置監(jiān)聽對象this._procSubs(subs[0], args);// 處理前置監(jiān)聽對象this._procSubs(subs[1], args);}this._broadcast(args);return this.stopped ? false : true;},// 判斷是否有監(jiān)聽對象hasSubs: function (when) {var s = this.subCount,a = this.afterCount;if (when) {return (when == 'after') ? a : s;}return (s + a);},// 獲取所有前置/后置監(jiān)聽對象getSubs: function () {var s = Y.merge(this.subscribers),a = Y.merge(this.afters);return [s, a];},// 獲取監(jiān)聽對象_procSubs: function (subs, args, ef) {var s, i;for (i in subs) {if (subs.hasOwnProperty(i)) {s = subs[i];if (s && s.fn) {if (false === this._notify(s, args, ef)) {// 回調(diào)返回false時,立即停止處理后續(xù)回調(diào)this.stopped = 2;}if (this.stopped == 2) {// 立即停止處理后續(xù)回調(diào),方便實現(xiàn)stopImmediatePropagationreturn false;}}}}return true;},// 通知監(jiān)聽對象,執(zhí)行回調(diào)方法_notify: function (s, args, ef) {var ret = s.notify(args, this);if (false === ret || this.stopped > 1) {return false;}return true;},// 廣播事件_broadcast: function (args) {if (!this.stopped && this.broadcast) {var a = Y.Array(args);a.unshift(this.type);// 在當(dāng)前YUI實例Y上廣播if (this.host !== Y) {Y.fire.apply(Y, a);}// 在全局對象YUI上廣播,跨實例if (this.broadcast == 2) {Y.Global.fire.apply(Y.Global, a);}}},// TODO: 在下一篇介紹Y.EventTarget的文章中再做介紹fireComplex: function (args) {},// 移除監(jiān)聽器detach: function (fn, context) {// unsubscribe handleif (fn && fn.detach) {return fn.detach();}var i, s,found = 0,subs = Y.merge(this.subscribers, this.afters);for (i in subs) {if (subs.hasOwnProperty(i)) {s = subs[i];if (s && (!fn || fn === s.fn)) {this._delete(s);found++;}}}return found;},_delete: function (s) {if (s) {if (this.subscribers[s.id]) {delete this.subscribers[s.id];this.subCount--;}if (this.afters[s.id]) {delete this.afters[s.id];this.afterCount--;}}if (s) {s.deleted = true;}} };

適用場景

自定義事件的適用場景與Publish–subscribe Pattern基本一致。具體來講,我覺得以下一些場景是非常適合用自定義事件的:

a) 需要暴露接口/行為以滿足擴(kuò)展需要

底層模塊一般會設(shè)計的盡量簡單,解決核心問題,并適當(dāng)?shù)拈_放一些接口,方便應(yīng)用層進(jìn)行擴(kuò)展以滿足實際需求。例如表單驗證控件,有可能需要在某個表單項驗證成功/失敗后執(zhí)行一些額外操作,舉一個實際的例子:當(dāng)用戶輸入的郵箱地址驗證成功時,我們會檢查是不是某些比較爛的郵件服務(wù)商,如果是則給出一些建議。

YUI作為一個底層基礎(chǔ)庫,在組件/控件層面加入了大量的自定義事件,以滿足實際應(yīng)用中的需要。例如Y.Anim的start、end事件,Y.io的success、failure、end事件,Y.Attribute中的屬性變化事件等。

b) 行為可能會被其它模塊/方法中止

這一點非常像DOM事件,我們經(jīng)常會中止一些事件的默認(rèn)行為,例如anchor的點擊事件。

自定義事件 VS 回調(diào)函數(shù)

這是一個比較難的問題,我自己的看法是:相對回調(diào)函數(shù),自定義事件是一種更重但更靈活的方案。在實際應(yīng)用中,如果對于關(guān)心某消息的受眾不夠清楚,那么就使用事件。否則,比較適合使用回調(diào)函數(shù)。

MSDN上的解釋更好一些:“An event is like an anonymous broadcast, while a call-back is like a handshake. The corollary of this is that a component that raises events knows nothing about its clients, while a component that makes call-backs knows a great deal”。

另外,如果對于性能特別關(guān)心,在可能的情況下,盡量使用回調(diào)。

參考

  • YUILibrary-CustomEvent
  • YUILibrary-EventTarget
  • Wikipedia-Publish–subscribe Pattern
  • Zakas-Custom events in JavaScript
  • When to Use Events or Call-Backs for Notifications

總結(jié)

以上是生活随笔為你收集整理的YUI事件体系之Y.CustomEvent的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。