`

[Ext源码解读]事件的注册、添加与触发是如何实现的

    博客分类:
  • Ext
阅读更多
Ext提供了一套强大的事件处理机制,每个组件都有许多事件,用户可以很方便通过动态的方式为对象添加/删除事件监听函数(在实例化的时候不是必须的),从而动态的改变或添加对象的行为,而这一切又是如何实现的呢?
阅读前请您准备好Javascript基础知识(包括:prototype属性、Functin对象的apply和call方法、函数的作用域等)。
该脚本剥离了许多分支逻辑,修改了大多数函数的实现仅保留最基本逻辑,如需详细了解Ext内部请阅读Ext源代码。Enjoy it!
该脚本脱离了Ext库的依赖,可直接复制在firbug下运行,(推荐chrome的javascript控制台,功能更强大)
预期运行结果:

/**
 * 观察者模式,也称作订阅发布模式,体现一种一对多关系,一般用在一个对象的改变将影响其它多个对象的情况下。
 * 
 * 
 */
Observable = function(name) {
	this.name = name;
	this.events = {};
	this.addEvents('before', 'after');

	this.getName = function() {
		if (this.fireEvent('before', this.name) !== false) {
			console.log('My name is ' + this.name);
			this.fireEvent('after', this.name);
			return this.name;
		}
	}

}
Observable.prototype = {
	
	/**
	 * 添加事件名,就是个注册罢了,可以同时注册多个;试想一下如果添加就初始化了Event对象,如果一个组件有很多事件类型,
	 * 那么每次组件初始化它所具备的事件时要把每个事件组件都是实例化,那该是一件多么费内存的操作啊!
	 */
	addEvents : function() {
		var a = arguments, i = a.length;
		while (i--) {
			this.events[a[i]] = true;// 这里可不是数组,这是变量的形式访问属性
		}
	},

	addListener : function(eventName, fn, scope) {
		var ce;// 事件对象或者是true
		ce = this.events[eventName] || true;
		if (ce == true) {//事件默认是没有初始化的
			this.events[eventName] = ce = new Event(this, eventName);
		}
		ce.addListener(fn, scope);
	},
	/**
	 * @augments 第一个是事件名,之后是监听方法需要的参数
	 * @return ret 进一步执行的标实
	 */
	fireEvent : function() {
		var a = arguments, ret = true, ce;
		ename = a[0], ce = this.events[ename];
		if (Object.prototype.toString.call(ce) === '[object Object]') {
			ret = ce.fire.apply(ce, Array.prototype.slice.call(a, 1, a.length));
		}
		return ret;
	}
};

Event = function(obj, name) {
	this.name = name;
	this.obj = obj;
	this.listeners = [];
};

Event.prototype = {
	addListener : function(fn, scope, options) {
		scope = scope || this.obj;
		if (this.firing) { // 如果正在触发监听事件,则用slice方法创建一个与原对象一样的新对象,这样不会影响正在触发的监听方法链
			this.listeners = this.listeners.slice(0);
		}
		this.listeners.push({
					fireFn : fn,
					scope : scope,
					options : options
				});
	},
	fire : function() {
		var listeners = this.listeners, 
			args = Array.prototype.slice.call(arguments, 0, arguments.length), 
			len = listeners.length, i = 0, l;
		if (len > 0) {
			this.firing = true;
			for (; i < len; i++) {
				l = listeners[i];
				// 添加监听时设置的作用域具有最高优先级,其次是当前Event对象的目标对象,都没有就是window对象
				if (l&& l.fireFn.apply(l.scope || this.obj || window, args) === false) {
					return (this.firing = false);
				}
			}
		}
		this.firing = false;
        return true;
	}
};

var obj1 = new Observable('bob');

obj1.addListener('before', function() {
			console.log('First before listener my name is ' + this.name);
		});

obj1.addListener('before', function() {
			console.log('Second before listener my name is ' + this.obj1.name);
			return false;
		}, this);
obj1.addListener('after', function() {
			console.log('First after listener my name is ' + this.name);
		});
obj1.addListener('after', function() {
			console.log('Second after listener my name is ' + this.name);
		});
obj1.getName();
  • 大小: 4.4 KB
分享到:
评论
6 楼 chemzqm 2010-04-17  
hommy8 写道
var a = arguments;
Array.prototype.slice.call(a, 1, a.length);


请教大家,以上代码,a就是一个数组,为什么还要通过继承的方式调用Array的slice方法呢?这不是多此一举吗?看得我头都晕了。  请问是不是有特殊作用呢?

错了 a不是数组 arguments是函数执行时的参数对象,它只是具有array的某些方法属性罢了。还有一点,这种方式方式不能叫做继承,call方法的目的是装换作用域并传参,这里是把Array.prototype.slice这个函数的作用域转到a这个对象上;这行代码意义在于创建一个新的数组对象,它的起点和终点是原来的1到a.length,这么做是因为写fireEvent这个方法的时候第一个参数始终都是事件名,之后才是监听函数所需要的参数
5 楼 hommy8 2010-04-16  
var a = arguments;
Array.prototype.slice.call(a, 1, a.length);


请教大家,以上代码,a就是一个数组,为什么还要通过继承的方式调用Array的slice方法呢?这不是多此一举吗?看得我头都晕了。  请问是不是有特殊作用呢?
4 楼 chemzqm 2010-04-15  
beck5859509 写道
观察者模式,也称作订阅发布模式,体现一种一对多关系,一般用在一个对象的改变将影响其它多个对象的情况下。

我又重新读了以便代码,发现上面写的代码和这句话关系好象不大,还是太抽象了,
比如
var obj1 = new Observable('bob');  
obj1是观察者还是目标啊,它注册的before事件并没有影响到其它多个对象.
期待LZ详解.

obj1是观察者,被影响的目标是那些监听函数(在javascript中函数也是对象),你可以把这些监听函数想象成订阅者;在这里可以看到,Observable的fireEvent方法可以同时影响到多个注册到相同事件的方法上
3 楼 beck5859509 2010-04-15  
观察者模式,也称作订阅发布模式,体现一种一对多关系,一般用在一个对象的改变将影响其它多个对象的情况下。

我又重新读了以便代码,发现上面写的代码和这句话关系好象不大,还是太抽象了,
比如
var obj1 = new Observable('bob');  
obj1是观察者还是目标啊,它注册的before事件并没有影响到其它多个对象.
期待LZ详解.
2 楼 chemzqm 2010-04-15  
beck5859509 写道
写得不错,不过有处不是很清楚。
this.getName = function() { 
        if (this.fireEvent('before', this.name) !== false) { 
            console.log('My name is ' + this.name); 
            this.fireEvent('after', this.name); 
            return this.name; 
        } 
    } 
从楼主的执行结果来看,上面的 this.fireEvent("before',this.name)返回的是false,所以根本不会去执行after这个事件,楼主连续注册两次before事件,就是想让fire方法中的           return (this.firing = false)  语句返回 false,不知道我的理解对不对

更确切的说我是想说明通过在监听设置返回值为false有时是可以打断原本函数的流程的。当然这种做法实现需要 return (this.firing = false);这段代码来实现
另外一点可以从代码中看出:只要一个监听返回了false,其他后续注册在这个事件上的监听也不会被触发,譬如说我在最后面再添加一个before事件的监听,那它也会因为前面相同事件的监听返回false而不被执行
1 楼 beck5859509 2010-04-14  
写得不错,不过有处不是很清楚。
this.getName = function() { 
        if (this.fireEvent('before', this.name) !== false) { 
            console.log('My name is ' + this.name); 
            this.fireEvent('after', this.name); 
            return this.name; 
        } 
    } 
从楼主的执行结果来看,上面的 this.fireEvent("before',this.name)返回的是false,所以根本不会去执行after这个事件,楼主连续注册两次before事件,就是想让fire方法中的           return (this.firing = false)  语句返回 false,不知道我的理解对不对

相关推荐

    Ext JS源码分析与开发实例宝典光盘源码

    Ext JS源码分析与开发实例宝典光盘源码Ext JS源码分析与开发实例宝典光盘源码Ext JS源码分析与开发实例宝典光盘源码Ext JS源码分析与开发实例宝典光盘源码Ext JS源码分析与开发实例宝典光盘源码Ext JS源码分析与开发...

    ext源码分析

    通过观察源码,我们可以了解事件的注册、触发和监听机制。 10. 工具类和实用函数:EXT源码包含了大量的工具类和实用函数,如DOM操作、样式设置、字符串处理等,这些都是JavaScript开发中常用的功能。 通过以上分析...

    ext JS 源码和学习资料

    通过阅读源码,开发者可以了解到EXT JS如何实现组件化、事件处理、数据绑定等功能,进一步优化自己的代码结构和性能。例如,ext-3.x版本的源码中,`Ext.lib.Component`是所有组件的基类,而`Ext.container.Container...

    深入浅出ext js源码

    4. **事件系统**:EXT JS的事件系统是组件间通信的基础,通过源码可以学习事件的注册、触发和监听。 5. **Ajax通信**:EXT JS提供了封装的Ajax请求方法,通过源码可以了解它是如何处理异步请求的。 6. **主题与...

    ext 3.0源码+帮助文档chm

    在EXT 3.0源码中,我们可以看到EXT JS的事件处理机制,它是如何实现组件间的通信和用户交互的。事件监听器允许开发者在特定事件触发时执行相应的函数,提高了代码的可维护性和模块化。此外,EXT 3.0还引入了Ajax通信...

    ext4文件系统源码

    通过深入阅读和研究这些源码,你可以了解到EXT4如何处理文件的创建、删除、读写,如何维护inode和数据块的映射,以及如何实现日志机制保证数据安全。这对于Linux内核开发者、系统管理员以及对文件系统感兴趣的任何人...

    Ext江湖源码

    【标签】"Ext 源码 大漠孤烟"进一步确认了主题,强调了这是关于Ext框架的源代码,且与"大漠孤烟"有关。标签是用来分类和检索的关键字,这里提醒我们关注的重点在于理解和学习Ext的源码,以及可能的作者或贡献者。 ...

    ext 3.2源码

    源码中的`Ext.Component`是所有组件的基类,提供了事件处理、渲染、布局管理等基本功能。通过研究`Ext.Component`,我们可以理解EXT如何创建、更新和管理组件实例。 2. **布局管理器**:EXT 3.2提供了多种布局模式...

    Extjs源码之--Ext事件机制/继承关系

    EventManager.js就是实现这一模式的关键,它提供了注册、触发和解绑事件的接口。 1. **事件注册**: 在EXTJS中,每个组件(Component)都可以注册事件监听器。开发者可以使用`on`方法来监听特定事件,例如: ```...

    ext 源码api 开发文档

    通过阅读源码,开发者可以了解EXT内部的工作机制,例如事件处理、组件创建、布局管理等。这对于调试、优化和扩展EXT应用至关重要。 2. **API文档**:EXT的API文档详细列出了框架的所有类、方法、属性和事件。开发者...

    Ext项目小框架介绍源码

    - **事件处理**:Ext JS使用事件驱动的设计,组件之间通过触发和监听事件进行通信。你可以自定义事件处理函数来响应用户操作或系统状态变化。 在学习这个小框架的过程中,你不仅能够掌握Ext JS的基础用法,还可以...

    EXT dojochina Ext事件.rar

    EXT dojochina Ext事件是JavaScript库EXT JS中的一个重要概念,EXT JS是一个强大的前端开发框架,主要用于构建桌面级Web应用程序。这个压缩包可能包含了关于EXT JS事件处理的详细资料,特别是针对中国开发者...

    Ext.Net界面源码C# ASPX

    通过学习和研究这个源码,开发者不仅可以了解Ext.NET与C# ASPX的整合方式,还能掌握实际项目中的一些最佳实践和常见应用场景,提升Web开发技能。如果你正在寻找一个快速构建现代Web界面的方法,这个源码资源将是一个...

    openlayers的ol-ext插件包的源码

    openlayers的ol-ext插件包的源码,课程基于openlayers开发局域网地图,可以实现地图显示、地点标注、绘制图形、添加图片与文字、地图保存与恢复、地点搜索查询、路径规划、二三维地图切换等功能,为局域网地图的开发...

    EXT4 源码+window下模拟ext2+linux_ext文件系统模拟

    EXT4的源码主要由C语言编写,包含了大量的头文件、数据结构定义和函数实现。在源码中,你会看到诸如`fs/ext4`这样的目录,其中包含了EXT4文件系统的所有核心组件。这些组件包括超级块(superblock)、inode、目录项...

    ext项目实例源码

    ext项目实例源码

    linux ext2 文件系统模拟 c语言实现

    EXT2的C语言实现是一项技术性极强的任务,涉及到对操作系统底层机制的理解,包括文件系统的结构、inode、超级块、数据块等核心概念。在这里,我们将深入探讨EXT2文件系统的基本原理及其C语言模拟实现的关键点。 1. ...

    ext-1.0源码+部分中文解读

    本文将深入EXT-1.0的源码,探讨其中的核心概念和技术,以及部分中文解读。 1. **EXTJS概述** EXTJS是一个基于浏览器的JavaScript框架,它允许开发者创建复杂的、交互式的Web应用程序,无需深入了解底层HTML、CSS和...

    ext-3.0.0源码

    源码中的事件监听和触发机制是理解EXTJS事件处理的关键。 5. **Ajax与JSON**:EXTJS 3.0.0内置了Ajax功能,支持与服务器进行异步通信。同时,JSON作为数据交换格式被广泛应用。在源码中,我们可以看到...

    Ext表单组件实现用户注册

    本篇文章将聚焦于如何使用Ext表单组件来实现用户注册的功能。 首先,我们来看看`form.html`。这是一个HTML文件,通常会包含一个基本的页面结构,以及引入Ext JS库的链接。它可能还会有一个`&lt;script&gt;`标签,用来加载...

Global site tag (gtag.js) - Google Analytics