YUI事件体系之Y.EventTarget
上兩篇文章YUI事件體系之Y.Do、YUI事件體系之Y.CustomEvent中,分別介紹了YUI實(shí)現(xiàn)AOP的Y.Do對(duì)象,以及建立自定義事件機(jī)制的Y.CustomEvent對(duì)象。
本篇文章,將要介紹YUI事件體系集大成者、最為精華的部分——Y.EventTarget。
Y.EventTarget
DOM事件中的目標(biāo)元素為event.target,這類(lèi)元素可以觸發(fā)、監(jiān)聽(tīng)一些事件,例如input元素的click、focus等事件。這也正是Y.EventTarget的命名淵源,它提供了一種讓任意對(duì)象定義、監(jiān)聽(tīng)、觸發(fā)自定義事件的實(shí)現(xiàn)方式。
從設(shè)計(jì)上看,Y.EventTarget通過(guò)內(nèi)部維護(hù)一系列Y.EventCustom對(duì)象,提供了可以通過(guò)事件名稱(chēng)進(jìn)行事件定義、監(jiān)聽(tīng)和觸發(fā)的便捷接口。另外,推薦使用Y.augment將它以組合的方式加載在其它類(lèi)上,而不要使用繼承。關(guān)于Y.augment和Y.extend之間的異同,可以參考我之前的一篇文章:Y.extend與Y.augment。
YUI很多基礎(chǔ)類(lèi)都擴(kuò)展了Y.EventTarget,重要的有Y(YUI instance,sandbox)、Y.Global、Y.Node、Y.NodeList、Y.Base等。
YUILibrary有專(zhuān)門(mén)一個(gè)章節(jié)介紹EventTarget,非常詳盡,如果你對(duì)EventTarget的設(shè)計(jì)思路和使用方法感興趣,請(qǐng)移步Y(jié)UILibrary-EventTarget。
示例
首先,讓我們看看Y.EventTarget獨(dú)立調(diào)用的例子:
// 例1 YUI().use('event-custom', function (Y) {var et = new Y.EventTarget();et.on('say', function (msg) {console.log('say:', msg);});et.on('listen', function (msg) {console.log('listen:', msg);});// output: say: Hello, worldinstance.fire('say', 'Hello, world'); });這種方式實(shí)際意義不大,YUI中只有Y.Global使用了這種方式。
下面,讓我們看下最廣泛的使用方式,即通過(guò)Y.augment擴(kuò)展其它類(lèi):
// 例2 YUI().use('event-custom', function (Y) {function MyClass() {}MyClass.prototype.add = function (item) {// do sththis.fire('addItem', { item: item });};MyClass.prototype.remove = function (item) {// do sththis.fire('removeItem', { item: item });};// 用EventTarget擴(kuò)展MyClassY.augment(MyClass, Y.EventTarget);var instance = new MyClass();// 監(jiān)聽(tīng)addItem事件instance.on('addItem', function (data) {console.log('add an item:', data.item);});// 監(jiān)聽(tīng)removeItem事件instance.on('removeItem', function (data) {console.log('remove an item:', data.item);});// output: add an item: orangeinstance.add('orange');// output: remove an item: redinstance.remove('red'); });源代碼分析
接下來(lái),讓我們看看YUI的內(nèi)部實(shí)現(xiàn)吧。
注:為了更容易的看懂代碼的核心,我做了適當(dāng)?shù)暮?jiǎn)化,感興趣的朋友可以去看未刪節(jié)的源碼。
var AFTER_PREFIX = '~AFTER~';// EventTarget構(gòu)造器 var ET = function (opts) {var o = opts || {};// 私有事件聚合器this._yuievt = this._yuievt || {id: Y.guid(),events: {},config: o,// 默認(rèn)配置defaults: {context: o.context || this,host: this,emitFacade: o.emitFacade,fireOnce: o.fireOnce,queuable: o.queuable,broadcast: o.broadcast}}; };ET.prototype = {constructor: ET,// 創(chuàng)建事件publish: function (type, opts) {var ce,events = this._yuievt.events,defaults = this._yuievt.defaults;ce = events[type];if (ce) { // 已創(chuàng)建過(guò)該事件if (opts) {ce.applyConfig(opts, true);}} else { // 基于CustomEvent,創(chuàng)建新事件ce = new Y.CustomEvent(type,(opts) ? Y.merge(defaults, opts) : defaults);events[type] = ce;}return ce;},// 監(jiān)聽(tīng)事件on: function (type, fn, context) {var ce,after,handle,args = null;// 判斷是否為后置監(jiān)聽(tīng)if (type.indexOf(AFTER_PREFIX) > -1) {after = true;type = type.substr(AFTER_PREFIX.length);}// 獲取自定義事件對(duì)象,如果未創(chuàng)建則先創(chuàng)建ce = this._yuievt.events[type] || this.publish(type);if (arguments.length > 3) {args = Y.Array(arguments, 3, true);}// 調(diào)用自定義事件對(duì)象的_on方法監(jiān)聽(tīng)事件handle = ce._on(fn, context, args, after ? 'after' : true);return handle;},// 監(jiān)聽(tīng)一次事件once: function () {var handle = this.on.apply(this, arguments);if (handle.sub) {// 監(jiān)聽(tīng)器執(zhí)行一次則失效handle.sub.once = true;}return handle;},// 后置監(jiān)聽(tīng)事件after: function (type, fn) {var a = Y.Array(arguments, 0, true);a[0] = AFTER_PREFIX + type;return this.on.apply(this, a);},// 后置監(jiān)聽(tīng)一次事件onceAfter: function () {var handle = this.after.apply(this, arguments);if (handle.sub) {handle.sub.once = true;}return handle;},// 觸發(fā)事件fire: function (type) {var ce,args;args = Y.Array(arguments, 1, true);ce = this._yuievt.events[type];// 尚未創(chuàng)建事件if (!ce) return true;return ce.fire.apply(ce, args);},// 注銷(xiāo)事件監(jiān)聽(tīng)detach: function (type, fn, context) {var events = this._yuievt.events,ce,i;// 未設(shè)置事件類(lèi)型,則注銷(xiāo)所有類(lèi)型的事件if (!type) {for (i in events) {if (events.hasOwnProperty(i)) {events[i].detach(fn, context);}}return this;}ce = events[type];if (ce) {ce.detach(fn, context);}return this;} };進(jìn)階用法
Y.EventTarget作為一個(gè)十分重要的類(lèi),提供了非常豐富、方便的使用方式,除了依賴(lài)內(nèi)部Y.CustomEvent實(shí)現(xiàn)的事件接口、默認(rèn)執(zhí)行方法、事件廣播等,其余主要有:
a) 事件冒泡
多個(gè)EventTarget對(duì)象之間可以建立一定事件傳播關(guān)系,類(lèi)似DOM事件中的冒泡。
// 例3 YUI().use('event-custom', function (Y) {// 父類(lèi)function Parent() { ... }Y.augment(Parent, Y.EventTarget, true, null, { emitFacade: true });// 子類(lèi)function Child() { ... }Y.augment(Child, Y.EventTarget, true, null, { emitFacade: true });var parent = new Parent(),child = new Child();// 子類(lèi)對(duì)象添加冒泡目標(biāo)對(duì)象,child -> parentchild.addTarget(parent);parent.on('hear', function (e) {console.log('parent hear', e.msg);});child.on('hear', function (e) {console.log('child hear', e.msg);});// output: child hear Hi, parent hear Hichild.fire('hear', { msg: 'Hi' }); });b) 事件前綴
在事件冒泡的基礎(chǔ)上,考慮到區(qū)分不同EventTarget對(duì)象觸發(fā)相同事件,YUI引入了事件前綴(Event Prefix)。
// 例4 YUI().use('event-custom', function (Y) {// 父類(lèi)function Parent() { ... }Y.augment(Parent, Y.EventTarget, true, null, {emitFacade: true,prefix: 'parent' // 配置事件前綴});// 子類(lèi)function Child() { ... }Y.augment(Child, Y.EventTarget, true, null, {emitFacade: true,prefix: 'child' // 配置事件前綴});var parent = new Parent(),child = new Child();child.addTarget(parent);parent.on('hear', function (e) { // 不能捕捉到child的hear事件console.log('parent hear', e.msg);});child.on('hear', function (e) {console.log('child hear', e.msg);});// output: child hear Hichild.fire('hear', { msg: 'Hi' });parent.on('*:see', function (e) { // 要想監(jiān)聽(tīng)到其它EventTarget對(duì)象的see事件,需要設(shè)置prefixconsole.log('parent see', e.thing);});child.on('child:see', function (e) { // 等同監(jiān)聽(tīng)see事件console.log('child see', e.thing);});// output: child hear MM, parent see MMchild.fire('see', { thing: 'MM' }); });參考
- YUILibrary-CustomEvent
- YUILibrary-EventTarget
總結(jié)
以上是生活随笔為你收集整理的YUI事件体系之Y.EventTarget的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 最全Java面试208题,涵盖大厂必考范
- 下一篇: 直通BAT必考题系列:JVM的4种垃圾回