之前在 读Ext消息机制有感之简单仿写(一)中仿写了一部分的Ext事件机制内容。Ext中还有一个重要的类Ext.Observable,这里只要继承这个类,我们继承后的类也可以将加入自己的事件响应函数。然后根据(一)中介绍的内容,结合起来处理html中的一些原生的事件。在这篇学习笔记的封装代码中实现对一个事件响应多个函数的情况
下面的代码是看了Ext相关源码进行编写为的是提升自身js水平,忽略了不少东西,对一些响应函数的作用域以及返回的参数也处理的不是很好。仅供参考
,
Ext.Observable = function() {
var scope = this;
if(scope.addListeners) { //存在addListeners对象,就执行addListeners方法
scope.addListener(scope.addListeners);
}
scope.events = scope.events || {};
}
Ext.apply(Ext.Observable,{
addListener: function(ename, fn, scope) { //第一个参数可能是对象listeners: {'click': function(){}} 也可能是事件名字
if(Ext.isObject(ename)) {
for(var key in ename) {
var fn = ename[key];
this.addListener(key,fn,ename.scope || fn.scope); //提供一个作用用,在使用者编写消息响应函数的时候,通过调用this使用
}
}else {
if(typeof ename == 'string') {
var event = this.events[ename] || true; //this.addEvents执行在前,创建了events数组,或者在调用构造函数Observable时执行scope.events = scope.events || {};在后面通过创建的对象调用addEvents 和addListeners
if(typeof event == 'boolean') { //如果是boolean类型,说明这这个ename里面没有响应函数
this.events[ename] = new Ext.Event(this, ename); //这里的this其实就是继承Observable的自定义组件
}
this.events[ename].addListener(fn, scope);
}
}
},
fireEvent: function() {
//这里简单化,忽略冒泡等
var arg = Ext.toArray(arguments);
var ename = arg[0].toLowerCase();
var flag = false;
var event = this.events[ename]; //如果之前addEvent && addListener则存在 返回Event对象
if(Ext.isObject(event)) {
if(event.fire.call(event,arg.slice(1)) === false) { //第一个参数,让fire方法里面的this还是属于event,不然,如果是别的对象,fire里面this的属性会复制给这个对象
return false;
}
flag = true;
}
return flag;
},
addEvent: function() {
var arg = arguments[0];
this.events = this.events || {}; //this.events如果不存在就直接创建一个空对象,这里events可能在这里创建,也可能在Observable构造函数中创建
if(typeof arg == 'string') {
var len = arguments.length;
while(len--) {
var ename = arguments[len];
this.events[ename] = this.events[ename] || true; //如果ename不存在就创建,存在就覆盖,这里覆盖成原来的值
}
}
}
})
Ext.Event = function(scope, ename) {
this.ename = ename;
this.scope = scope;
this.listeners = [];
}
Ext.apply(Ext.Event, {
addListener: function(fn, scope) {
scope = scope || this.scope;
//先判断这个fn在这个scope作用域是否存在,如果在调用addListener或者addListener定义的对象
//中没有指明scope作用域,那么默认是以这个组件为响应函数的作用域
if(!this.isExistListener(fn, scope)) { //如果已经有这个响应函数就不需要重复放到listeners数组里面了
var listener = this.createListener(fn, scope);
if(this.fireing) { //这个事件已经在执行其中的响应函数
this.listeners = Array.prototype.slice.call(this.listeners, 0);
}
this.listeners.push(listener); //将这个事件的其中之一的响应函数放到listener数组中
}
},
isExistListener: function(fn, scope) {
var len = this.listeners.length;
while(len--) {
if(listeners[len].fn == fn && listeners[len].scope == scope) {
return true;
}
}
return false;
},
createListener: function(fn, scope) {
var obj = {
fn: fn,
scope: scope
}
obj.fireFn = fn; //这里先忽略target、delay、single、buffer的处理
return obj;
},
fire: function() {
var option = Ext.toArray(arguments);
var len = this.listeners.length;
while(len--) {
this.fireing = true; //表示已经是执行状态了
var fn = this.listeners[len];
if(fn.fireFn.apply(fn.scope || this.scope, option) === false) { //第一个参数是给响应函数指明作用域如果fn.scope不存在就使用这个Event(click)的scope
return (this.fireing = false); //执行结束
}
}
this.fireing = false; //如果listeners都执行完,就让this.fireing变成false
return true;
}
});
Ext.toArray = function(arg) {
var agent = navigator.userAgent.toLowerCase();
if(!(/opera/.test(agent)) && /msie/.test(agent)) { //IE浏览器,不能使用Array.prototype.slice.call(arg,0);
var array = [];
for(var index = 0; index < arg.length; index++) {
array[index] = arg[index];
}
return array;
}else { //其他浏览器
return Array.prototype.slice.call(arg,0);
}
}
Ext.extend = function(target, source, obj) {
if(Ext.isObject(source)) {
obj = source;
source = target;
target = obj.constructor? obj.constructor : function() {
source.apply(this,arguments);
}
}
var fn = function() {};
fn.prototype = source.prototype;;
target.prototype = new fn();
target.prototype.constructor = target;
target.prototype.superclass = source.prototype;
for(var key in obj) {
target.prototype[key] = obj[key];
}
target.extend = function(source, obj) { //给这个继承的组件提供一个extend方法
Ext.extend(target,source,obj);
}
target.superclass = function() { //定义一个返回superclass的方法
return source.prototype;
}
return target;
}
Ext.MyEvent = function() {
this.name = 'stray';
this.time = '2015-4-27';
this.addEvent('show','read');
Ext.MyEvent.prototype.superclass.constructor.call(this,arguments); //这一句代码挺关键的,主要是执行Ext.Observable构造函数
};
Ext.extend(Ext.MyEvent, Ext.Observable, {
addListeners: {
show: function() {
alert('由'+ this.name + '在' + this.time + '提供'); //这里为什么可以使用this? 上边fire函数中已经指明了作用域scope
},
read: function() {
alert('正在操作read响应函数');
}
}
});
下面附上完整的代码
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<head>
<script type="text/javascript"><!--
Ext = {version: '1.1'};
Ext.cacheEl = {};
/**
@param element: 原生element元素 或者是String类型的id
如果是new function创建的对象或者{}类型的,都是返回ture
*/
Ext.isObject = function(obj) {
return Object.prototype.toString.call(obj) == '[object Object]'? true : false;
}
/**
@param source 源
@param target 目标,这里的数据拷贝到source的prototype上
target可以是fucntion 创建的对象,也可以是{}类型
*/
Ext.apply = function(source,target) {
var _proto = source.prototype;
if(Ext.isObject(target)) {
for(var el in target) {
_proto[el] = target[el];
}
}
return source;
}
Ext.Element = function(element) {
element = typeof element == 'string'? document.getElementById(element) : element;
if(!element) {
return null;
}
var idEl = element.id;
if(idEl && !Ext.cacheEl[idEl]) {
Ext.Element.toAddCache(element);
}
this.dom = element;
this.id = idEl;
}
Ext.Manager = function() {
//定义其他方法供返回对象里面的方法调用
return {
addLister: function(event,actor,scope) {
var dom = Ext.cacheEl[scope.id].el; //这里其实可以直接用scope.dom或者由上边个参数进来,为了使用Ext.cacheEl,所以这么写
if(window.addEventListener) {
dom.addEventListener(event, actor);
}else{
if(window.attachEvent) {
dom.attachEvent('on' + event, actor);
}
}
}
};
}();
Ext.apply(Ext.Element,{
addListener: function(event,fn) { //addListener('click',function(){})
var scope = this;
var idEl = scope.id;
var eventEl = Ext.cacheEl[idEl].events;
eventEl[event] = [fn];
function actor(event) {
//这里Ext可以将event封装成自身的消息对象
fn.apply(fn.scope || {}); //执行相应函数,在这里可以定义一些参数,供使用者在编写相应方法时候调用
}
Ext.Manager.addLister(event,actor,scope);
}
});
/**
@param element: 原生element元素
*/
Ext.Element.toAddCache = function(element) {
var obj = {
el: element,
data: [],
events: []
}
Ext.cacheEl[element.id] = obj;
}
/**
定义几个将原生dom封装成Ext自定义的Element元素
*/
Ext.get = Ext.find = function(element) {
element = new Ext.Element(element);
return element;
}
Ext.Observable = function() {
var scope = this;
if(scope.addListeners) { //存在addListeners对象,就执行addListeners方法
scope.addListener(scope.addListeners);
}
scope.events = scope.events || {};
}
Ext.apply(Ext.Observable,{
addListener: function(ename, fn, scope) { //第一个参数可能是对象listeners: {'click': function(){}} 也可能是事件名字
if(Ext.isObject(ename)) {
for(var key in ename) {
var fn = ename[key];
this.addListener(key,fn,ename.scope || fn.scope); //提供一个作用用,在使用者编写消息响应函数的时候,通过调用this使用
}
}else {
if(typeof ename == 'string') {
var event = this.events[ename] || true; //this.addEvents执行在前,创建了events数组,或者在调用构造函数Observable时执行scope.events = scope.events || {};在后面通过创建的对象调用addEvents 和addListeners
if(typeof event == 'boolean') { //如果是boolean类型,说明这这个ename里面没有响应函数
this.events[ename] = new Ext.Event(this, ename); //这里的this其实就是继承Observable的自定义组件
}
this.events[ename].addListener(fn, scope);
}
}
},
fireEvent: function() {
//这里简单化,忽略冒泡等
var arg = Ext.toArray(arguments);
var ename = arg[0].toLowerCase();
var flag = false;
var event = this.events[ename]; //如果之前addEvent && addListener则存在 返回Event对象
if(Ext.isObject(event)) {
if(event.fire.call(event,arg.slice(1)) === false) { //第一个参数,让fire方法里面的this还是属于event,不然,如果是别的对象,fire里面this的属性会复制给这个对象
return false;
}
flag = true;
}
return flag;
},
addEvent: function() {
var arg = arguments[0];
this.events = this.events || {}; //this.events如果不存在就直接创建一个空对象,这里events可能在这里创建,也可能在Observable构造函数中创建
if(typeof arg == 'string') {
var len = arguments.length;
while(len--) {
var ename = arguments[len];
this.events[ename] = this.events[ename] || true; //如果ename不存在就创建,存在就覆盖,这里覆盖成原来的值
}
}
}
})
Ext.Event = function(scope, ename) {
this.ename = ename;
this.scope = scope;
this.listeners = [];
}
Ext.apply(Ext.Event, {
addListener: function(fn, scope) {
scope = scope || this.scope;
//先判断这个fn在这个scope作用域是否存在,如果在调用addListener或者addListener定义的对象
//中没有指明scope作用域,那么默认是以这个组件为响应函数的作用域
if(!this.isExistListener(fn, scope)) { //如果已经有这个响应函数就不需要重复放到listeners数组里面了
var listener = this.createListener(fn, scope);
if(this.fireing) { //这个事件已经在执行其中的响应函数
this.listeners = Array.prototype.slice.call(this.listeners, 0);
}
this.listeners.push(listener); //将这个事件的其中之一的响应函数放到listener数组中
}
},
isExistListener: function(fn, scope) {
var len = this.listeners.length;
while(len--) {
if(listeners[len].fn == fn && listeners[len].scope == scope) {
return true;
}
}
return false;
},
createListener: function(fn, scope) {
var obj = {
fn: fn,
scope: scope
}
obj.fireFn = fn; //这里先忽略target、delay、single、buffer的处理
return obj;
},
fire: function() {
var option = Ext.toArray(arguments);
var len = this.listeners.length;
while(len--) {
this.fireing = true; //表示已经是执行状态了
var fn = this.listeners[len];
if(fn.fireFn.apply(fn.scope || this.scope, option) === false) { //第一个参数是给响应函数指明作用域如果fn.scope不存在就使用这个Event(click)的scope
return (this.fireing = false); //执行结束
}
}
this.fireing = false; //如果listeners都执行完,就让this.fireing变成false
return true;
}
});
Ext.toArray = function(arg) {
var agent = navigator.userAgent.toLowerCase();
if(!(/opera/.test(agent)) && /msie/.test(agent)) { //IE浏览器,不能使用Array.prototype.slice.call(arg,0);
var array = [];
for(var index = 0; index < arg.length; index++) {
array[index] = arg[index];
}
return array;
}else { //其他浏览器
return Array.prototype.slice.call(arg,0);
}
}
Ext.extend = function(target, source, obj) {
if(Ext.isObject(source)) {
obj = source;
source = target;
target = obj.constructor? obj.constructor : function() {
source.apply(this,arguments);
}
}
var fn = function() {};
fn.prototype = source.prototype;;
target.prototype = new fn();
target.prototype.constructor = target;
target.prototype.superclass = source.prototype;
for(var key in obj) {
target.prototype[key] = obj[key];
}
target.extend = function(source, obj) { //给这个继承的组件提供一个extend方法
Ext.extend(target,source,obj);
}
target.superclass = function() { //定义一个返回superclass的方法
return source.prototype;
}
return target;
}
Ext.MyEvent = function() {
this.name = 'stray';
this.time = '2015-4-27';
this.addEvent('show','read');
Ext.MyEvent.prototype.superclass.constructor.call(this,arguments); //这一句代码挺关键的,主要是执行Ext.Observable构造函数
};
Ext.extend(Ext.MyEvent, Ext.Observable, {
addListeners: {
show: function() {
alert('由'+ this.name + '在' + this.time + '提供'); //这里为什么可以使用this? 上边fire函数中已经指明了作用域scope
},
read: function() {
alert('正在操作read响应函数');
}
}
});
window.onload = function() {
var myevent = new Ext.MyEvent();
Ext.get('logonbox').addListener('click', function() { //这里才和html原生的消息事件进行绑定
myevent.fireEvent('show');
});
Ext.get('logonbox').addListener('dblclick', function() { //这里才和html原生的消息事件进行绑定
myevent.fireEvent('read');
});
}
</script>
</head>
<body>
<div id='logonbox' >11111111</div>
</body>
</html>