最简单的事件广播类

昨天想了想,其实 Node 把 API 都设计成为事件,或许是不太恰当的。UI 用事件的概念去理解,自然十分好,但后台的逻辑,本身都是串行书写的,即一步一步的思维。如果使用事件,等于把同步的逻辑支离破碎。

回归主题,看看 N 年前的写的事件广播类。下面文字都是 N 年前写的。

Event 类,就是一个提供事件服务的类,写得简简单单,不求多元、繁复(明显没有比 Ext JS 都考虑得多,那是一种方向)。 好像但凡研究 JavaSscript 到一定阶段的人,都要搞清楚事件吧,嗯~此为必修课。事件的立论基础大家可以从观察者模式(Observable)得到许多灵感,当然就是必须有第三方的“中立”观察者, 一边提供订阅事件的接口,一边让组件触发事件的 fireEvent()。前言:不得不承认,有时候从新写一个库是一件很辛苦的事情。但是相比较之下,直接使用别人写好的软件来修改,难道这样痛苦的程度就会减少吗?

基于事件模式的设计 JS 的一大好处就是对 Function 天然的良性支持。这一点纵观眼下多少语言都未必能够赋予这种地位,有的即使赋予了,但率直来讲,也没有 JS 的那样的流通性的地位。试问一下,现今有多少主流语言是对 Function 这一类型主打的?若没有 JS 带来的影响,可能这一观念认识知道的人,不计发烧玩家、专家级人马,少之又少,或是又是其他具有“忽悠”价值的概念来代替这一朴质的概念……

事件本身与 Function 形影不离。好多 GUI 系统都以 Function 为编程的中心,因为制定 GUI 的过程往往就是制定 Function 的过程。事件类型一旦明确用哪一个好之后,开发人员所关心的便是这个事件到底应该执行些什么过程。若过程可以以一个变量或对象来表示,再给他起个名字,我们从某个角度上理解,也可以说该过程被抽象化了。抽象有什么好处?就是把一些必要的过程先写好,以后在复用。直截了当的说,我定义函数为一变量,可以全局变量,也可以私有变量,看您怎么用它——还可以当作参数传来传去。也是一下子这样说有点跨越性太大,不易接受。如果是真的,操作起来究竟有什么好处呢?首先我们得从了解函数其意义的本质上去入手理解。什么?函数是什么??这不是初中层次的内容?……哎呀,插一句,不是怕大家见笑哦,我就是看了什么好东西,一味东拿来一点西拿来一点拼装一起,还自诩高级,其实没有扎实的基础知识联系,准吃亏!哎~回头来还比不过老老实实的说明一下基础内容。——各位已深明大义的看官请掠过。

/**
 * @class $$.Event
 */
$$.event = function(){
	var events = {};
	
	this.addEvents = function(){
		for(var i = 0, j = arguments.length; i < j; i++){
			events[arguments[i].toLowerCase()] = [];
		}
	}
	
	/**
	  * 添加一个事件侦听器。
	  * @param	{String}   name
	  * @param	{Function} fn
	  * @return {this}
	  */
	this.addListener = this.on = function(name, eventHandler) {
		var eventQueen = events[name.toLowerCase()];
		
		if(!eventQueen){
			throw '没有该事件!请使用addEvent()增加事件';
		}

		eventQueen.push(eventHandler);
		
		return this;
	}
	
	/**
	  * 触发事件。
	  * @param {String} name
	  * @param {Array}  args
	  * @return {Boolean}
	  */
	this.fireEvent = function(name) {
		var eventQueen = events[name.toLowerCase()]; // listeners
		var args = eventQueen.length && Array.prototype.slice.call(arguments, 1); 
					
		var result;
		var output = [];
		
		for (var i = 0, j = eventQueen.length; i < j; i++) {
			result = eventQueen[i].apply(this, args);
			
			if(result === false){
				break;
			}else{
				output.push(result);
			}
		}
	
		return output;
	}
	
	/**
	  * 移除事件侦听器。须传入原来的函数句柄。
	  * @param {String}   name
	  * @param {Function} fn
	  * @return {this}
	  */
	this.removeListener = function(name, fn) {
		if (events[name]) {
			Array_Remove(events[name], fn);
		}
		return this;
	}
}

/**
 * 删掉某个元素。
 * @param	{Array}	arr
 * @param	{Mixed}	el
 * @return	{Mixed}
 */
function Array_Remove(arr, el){
	var index = -1;
	for(var i = 0, j = arr.length; i < j; i++){
		if(arr[i] == el){
			index = i;
			break;
		}
	}
	arr.splice(index, 1);
	return el;
}

记得还在一次技术沙龙作过分享,似乎受众们的反馈不错 呵呵,在那时的确有点那么一点点技术含量。现在,呵呵都跑去看 Promise/Aysnc.js 了。

第二次重写,sea.js 封装。

/**
* 观察者类,用于推送消息或者广播事件。addEventListener 方法为对象新增消息类型,fire 方法进行消息推送。
* 使用该类后,增加一个 msgs 属性,结构如下:
* msg = Object : [ 
* 	{
* 	   name : String,
*      bodies : Object [
* 			{
* 				scope	: Mixed,
* 				fn		: Function,
* 				args	: Mixed []
* 			}
* 	   ]
*  }
* ]
*/
define(function(require, exports, module) {
    var $$ = require('common/lang');
    module.exports = $$.event = $$.define(Object, function (){
    	var msg = {
            name : '',
    		bodies : [{
    		    scope : null,
    			fn : null,
    			agrs : []
    		}], /* ... */
    		isCustomDomEvent : false
        }
        function observe(){
        	// I'm Event
        	this.event = true;
        }
        this.init = observe;

        /**
        * @param {String}	    msgName		消息名称
        * @param {Function}	    msgHandler	        消息行为
        * @param {*}		    scope		函数作用域
        * @param {*|Array}	    args		消息行为的前期参数
        * @return {Boolean}
        */
        this.addEventListener = this.on = function (msgName, msgHandler, scope, args) {
            if (!this.msgs) {
                this.msgs = [];// 全部消息之容器
            }

            check_msgBody_Queen(this.msgs);
            var msgObj;

            var hasToken = false;
            for (var i = 0, j = this.msgs.length; i < j; i++) {
                msgObj = this.msgs[i];
                if (msgObj.name == msgName) {
                    hasToken = true;
                    break; // 找到已存在的消息队列
                }
            }
            if (hasToken == false) {
                // 消息结构体 @todo remove create()
                msgObj = Object.create(msg); // 新建消息对象
                msgObj.name = msgName;
                msgObj.bodies = [];         // you must new it!
                if (msgName)
                    msgObj.isCustomDomEvent = this.customDomEventNames
                                             ? (indexOf(msgName, this.customDomEventNames) != -1)
                                             : false;
            }

            check_msgBody_Queen(msgObj.bodies);

            if (!(typeof args instanceof Array)) { // 前期输入的参数
                if (args == undefined) {
                    args = [];
                } else {
                    args = [args];
                }
            }

            msgObj.bodies.push({        // 压入消息队列
                fn: msgHandler,
    			scope: scope || this,  // 保存函数作用域
    			args: args
            });

            if (hasToken == false) {
                this.msgs.push(msgObj);
            }

            return true;
        }

        /**
        * 检查消息体队列其类型
        * @param {Array} arr
        */
        function check_msgBody_Queen(arr) {
            if (!arr || !arr instanceof Array) {
                throw new TypeError();
            }
        }

        function indexOf(item, arr) {
            for (var i = 0, j = arr.length; i < j; i++) {
                if (item == arr[i]) {
                    return i;
                }
            }

            return -1;
        }

        /**
        * 根据 msgName,查找指定的消息并讲其分发。
        * @param {String} msgName
        * @param {Array/Mixed} args 可选
        * @param {Mixed} scope 可选
        */
        this.fire = function (msgName, args, scope) {
        	var foundMsgObj = false;
            for (var msgObj, i = 0, j = this.msgs.length; i < j; i++) {
                msgObj = this.msgs[i];
                if (msgObj.name == msgName) {
                	foundMsgObj = true;
                    break;
                }
            }

            if (foundMsgObj === false) {
            	debugger;
                throw '没有此消息类型' + msgName;
            }


            var _fn, _body, _args;
            for (var body, i = 0, j = msgObj.bodies.length; i < j; i++) {
                body = msgObj.bodies[i];

                _fn = body.fn;
                _scope = scope || body.scope;
                if(args == undefined){
                	_args = body.args;
                }else{
                	// args 应该在数组前面,否则数组顺序会颠倒
                	if (args instanceof Array){
                		_args = args.concat(body.args);
                	}else{
                		_args = [args].concat(body.args);
                	}
                }
                
                _fn.apply(_scope, _args);
            }
        }
        // event-->msg
        this.customDomEvent = function (el, eventType, msgName) {
            if (!this.customDomEventNames) {
                this.customDomEventNames = [];
            }
            this.customDomEventNames.push(msgName);
            el.addEventListener(eventType, (function (e) {
                e = e || window.event;
                this.instance.fire(this.msgName, e, this.instance);
            }).bind({
                instance: this,
                msgName: msgName
            }));
        }
    });

    /**
     * 占位用的空函数。
     * @return {[type]} [description]
     */
    module.exports.emptyHandler = function(){}
});
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 岁月 设计师:pinMode 返回首页