Extjs学习笔记之八 继承和事件基础


Posted in Javascript onJanuary 08, 2010

这里接口的意思是Observable实际上起了一个抽象类的作用,Extjs中有大量的组件都是继承自这个类的。这个类提供了一些基本的方法比如addEvents,addlistener,fireEvent等等。

本文暂时不介绍如何使用extjs的组件响应事件,而是介绍Extjs的事件的一些实现原理。整个Extjs框架都是以一种面向对象的方式开发的,所以理解Javascript中的继承也很重要。我前面的一篇文章 补点基础:Javascript中的类和闭包 也是为这篇做准备。另外,博客园内还有一个写的很好的系列 JavaScript继承详解. 他主要是根据Douglas Crockford的两篇文章写的。 其实实现继承的原理都差不多,大家可以参考阅读。

Extjs实现继承的函数是一个很核心的函数Ext.extend,extend方法有两个重构版本,第一个接受两个参数,第一个是extend( Function superclass, Object overrides ) ,第二个是extend( Function subclass, Function superclass,Object overrides ) : Function,第二个版本是在subclass的基础上。superclass就是超类的构造函数,overrides是一个对象,里边的属性就是要覆盖父类的属性。继承了父类的子类具有父类的prototype中的所有方法。并且子类可以覆盖父类的方法(override),更进一步,子类的每个对象也可以覆盖父类的方法。其实我觉得这个函数没什么作用,修改prototype的效果是等效的,当然,extjs的目的肯定是要把prototype这个神奇的东西完全屏蔽起来,使程序员能够像处理其他语言一样来处理Javascript。当然,即使如此,它的继承和一般的继承还是有些不同的,下面先看个例子,准备好一个Person类

Person = function(name) { 
this.name = name; 
this.fn = function() { alert('I am a person') }; 
} 
Person.prototype.print=function(){ alert('I am a person');} 
Person.prototype.showAge = function() { alert('I am older than 0'); } 
Person.prototype.showName = function() { alert('Show Name:'+this.name) }; 
var per = new Person('Tom'); 
per.showName();子类:Student = function(id) { 
this.id = id; 
} 
Student.prototype.showID = function() { alert(this.id); } //子类的方法

继承:
Ext.extend(Student, Person);
stu.showName(); !!没有结果!stu没有name的定义stu.fn(); !!没有结果 stu.showID(); !!!还是没有结果到此我们已经发现了一些不同:在父类的构造函数中的内容是不会继承的,父类的构造函数不会被调用,子类(prototype中)已有的方法也会丢失!继续看下去,将Ext.extend下面的代码替换成:
var stu = new Student('01'); 
Student.override({ print: function() { alert('I am a student'); } }); 
stu.override({ print: function() { alert('I am a bad student,but I won\'t affect others'); } }); 
stu.print(); 
stu.showAge(); 
var stu2 = new Student(); 
stu2.print();

这里的函数都能够按预期输出,showAge是执行的父类的方法,stu.print是执行的stu.override中指定的方法,而stu2执行的是Student.override中指定的方法。到这里,我们已经大概能猜出extend是如何实现的了。下面看它真正的源代码,这个方法位于Ext.js中,代码和注释如下:extend : function(){
// inline overrides 
var io = function(o){ //注意这个方法的this,仅看这里并不知道这个this是什么,下面这个io会被赋值给sbp.override,也就是子类的prototype 
for(var m in o){ //从而每个子类的对象的override都会指向这个方法,如果子类对象调用了override,那么这个this就是子类的对象了。也就是 
this[m] = o[m]; //上面的例子中stu.override表现出来的效果,仅对当前对象有效。从这里可以看出,override不仅仅是传统意义上的覆盖,完全也可以 
} //用来添加新方法。 
}; 
var oc = Object.prototype.constructor; return function(sb, sp, overrides){ 
if(Ext.isObject(sp)){ //是在检测当前使用的是哪个版本的重构函数。如果sp实际上是overrides,就做些替换工作,让变量的实际意义和名称相符合。 
overrides = sp; 
sp = sb; 
sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; //这个没看懂…… 
} 
var F = function(){}, 
sbp, 
spp = sp.prototype; 
F.prototype = spp; //F是父类的一个“干净”拷贝,所谓干净,是指它不会把父类中在构造函数内部定义的属性带过来。 //例如 Person=function() // {this.privateFn=new function{ some code goes here}} //那么这个privateFn对子类是不可见的,所以在构造函数中利用this定义的属性都相当于是类的私有变量。 
sbp = sb.prototype = new F(); //将子类的prototype设置为父类的prototype,继承的核心步骤。 sbp.constructor=sb; //设置正确的构造函数指向,见 JavaScript继承详解 
sb.superclass=spp; //设置父类 
if(spp.constructor == oc){ //没看懂……,这个是干嘛用的?望高人指点 
spp.constructor=sp; 
} 
sb.override = function(o){ //子类的重写方法,这个重写方法是函数的重写方法。它修改的是prototype。 
Ext.override(sb, o); //见最后。 
}; 
sbp.superclass = sbp.supr = (function(){ //设置原型的父类 
return spp; 
}); 
sbp.override = io; //给子类的prototype提供override方法,这样单个实体也可以覆盖,它修改的是实体对象。注意和上面的sb的override区分。 
Ext.override(sb, overrides); //重写 
sb.extend = function(o){return Ext.extend(sb, o);}; //给子类提供extend方法,以实现多重继承 
return sb; //返回子类。 
}; 
}();

下面是Ext.override的代码,比较明了的,和那个inline override相比,它就是修改的prototype:override :
function(origclass, overrides){ 
if(overrides){ 
var p = origclass.prototype; 
Ext.apply(p, overrides); 
if(Ext.isIE && overrides.hasOwnProperty('toString')){ // 这个是什么?IE的特殊点? 
p.toString = overrides.toString; 
} 
} 
}

现在就可以开始正式介绍Extjs的事件模型了。和其他语言事件类似,首先要为一个类定义事件,其他语言(例如C#)的事件一般有一个专门的event类型,event类型实际上可以看作是委托的数组,当然委托实际上是函数,添加时间监听器(listener),就是想委托数组中添加委托(函数),所谓触发事件就是把数组中的函数统统执行一遍。Javascript也是类似的,只是Javascript的函数比那些语言强大灵活的多,因此也不需要什么event类型了。Javascript的事件看起来就像一个字符串(它内部应该也是保留了一个数组的),可以通过Observale.addEvents方法添加事件,通过Observale.fireEvent触发事件,通过Observale.addListner增加事件监听器。下面举一个没什么意义却能说明问题的例子。
Odder = function(min, max) { 
this.min = min; 
this.max = max; 
this.addEvents('onFindOdd'); 
} 
Ext.extend(Odder, Ext.util.Observable, { run: 
function() { 
for (var i = this.min; i < this.max; i++) { 
if (i % 2 != 0) { 
this.fireEvent('onFindOdd',i); 
} 
} 
} 
}); 
var p = new Odder(4, 8); 
p.addListener('onFindOdd',function(n){alert(n);}); 
p.run();

Odder是这么一个类,它通过一个构造函数传入一个范围,然后寻找这个范围内的所有奇数,每找到一个就触发一个事件。我给它加一个事件处理程序,把它找到的奇数alert出来。 要注意,这里的事件处理程序的参数只能靠程序员自己保持一致,它不像委托那样强类型。

注意,我没有采用官网上的例子:

Employee = Ext.extend(Ext.util.Observable, { 
constructor: function(config){ 
this.name = config.name; 
this.addEvents({ 
"fired" : true, 
"quit" : true 
}); // Copy configured listeners into *this* object so that the base class's 
// constructor will add them. 
this.listeners = config.listeners; 
// Call our superclass constructor to complete construction process. 
Employee.superclass.constructor.call(config) 
} 
});This could then be used like this: 
var newEmployee = new Employee({ 
name: employeeName, 
listeners: { 
quit: function() { 
// By default, "this" will be the object that fired the event. 
alert(this.name + " has quit!"); 
} 
} 
});

我觉得官网上的例子内部还有文章,它的重载项中包含了constructor属性,给人的感觉是是重载了父类的构造函数,然后子类就会调用这个构造函数来创建,其实不是的,它改变了Javascript本身的行为,这个就和我上面标注的没有看懂的那几句代码有关系。下回再讨论。
Javascript 相关文章推荐
javascript 对象定义方法 简单易学
Mar 22 Javascript
javascript动态添加、修改、删除对象的属性与方法详解
Jan 27 Javascript
js 判断所选时间(或者当前时间)是否在某一时间段的实现代码
Sep 05 Javascript
详解javascript传统方法实现异步校验
Jan 22 Javascript
Bootstrap3多级下拉菜单
Feb 24 Javascript
详解用webpack2.0构建vue2.0超详细精简版
Apr 05 Javascript
使用JQ完成表格隔行换色的简单实例
Aug 25 Javascript
Vue框架下引入ActiveX控件的问题解决
Mar 25 Javascript
详解Node.js一行命令上传本地文件到服务器
Apr 22 Javascript
微信小程序wepy框架学习和使用心得详解
May 24 Javascript
vue实现信息管理系统
May 30 Javascript
Vue中插槽slot的使用方法与应用场景详析
Jun 08 Vue.js
Extjs学习笔记之七 布局
Jan 08 #Javascript
IE6下JS动态设置图片src地址问题
Jan 08 #Javascript
Javascript 中的类和闭包
Jan 08 #Javascript
Extjs学习笔记之六 面版
Jan 08 #Javascript
jQuery开发者都需要知道的5个小技巧
Jan 08 #Javascript
javascript new一个对象的实质
Jan 07 #Javascript
IE iframe的onload方法分析小结
Jan 07 #Javascript
You might like
php error_log 函数的使用
2009/04/13 PHP
微信公众平台之快递查询功能用法实例
2015/04/14 PHP
WordPress中转义HTML与过滤链接的相关PHP函数使用解析
2015/12/22 PHP
PHP中phar包的使用教程
2017/06/14 PHP
文字不间断滚动(上下左右)实例代码
2013/04/21 Javascript
JS截取字符串常用方法整理及使用示例
2013/10/18 Javascript
深入理解JavaScript是如何实现继承的
2013/12/12 Javascript
jQuery对val和atrr(&quot;value&quot;)赋值的区别介绍
2014/09/26 Javascript
javascript实现将文件保存到本地方法汇总
2015/07/26 Javascript
非常漂亮的相册集 使用jquery制作相册集
2016/04/28 Javascript
jQuery中的一些常见方法小结(推荐)
2016/06/13 Javascript
Node.js中路径处理模块path详解
2016/11/14 Javascript
JavaScript 值类型和引用类型的初次研究(推荐)
2017/07/19 Javascript
新手vue构建单页面应用实例代码
2017/09/18 Javascript
bing Map 在vue项目中的使用详解
2018/04/09 Javascript
手把手教你用Node.js爬虫爬取网站数据的方法
2018/07/05 Javascript
JQuery Ajax执行跨域请求数据的解决方案
2018/12/10 jQuery
Javascript读取上传文件内容/类型/字节数
2019/04/30 Javascript
详解vue beforeRouteEnter 异步获取数据给实例问题
2019/08/09 Javascript
原生JS实现拖拽效果
2020/12/04 Javascript
利用Python开发实现简单的记事本
2016/11/15 Python
详解python使用pip安装第三方库(工具包)速度慢、超时、失败的解决方案
2018/12/02 Python
Python如何调用JS文件中的函数
2019/08/16 Python
Django 设置多环境配置文件载入问题
2020/02/25 Python
关于keras.layers.Conv1D的kernel_size参数使用介绍
2020/05/22 Python
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
2013/07/19 HTML / CSS
来自世界上最好大学的在线课程:edX
2018/10/16 全球购物
俄罗斯游戏商店:Buka
2020/03/01 全球购物
int和Integer有什么区别
2013/05/25 面试题
钳工实习自我鉴定
2013/09/19 职场文书
护理自荐信范文
2013/10/05 职场文书
客户经理岗位职责
2013/12/08 职场文书
ktv好的活动方案
2014/08/17 职场文书
档案工作汇报材料
2014/08/21 职场文书
小学生安全教育广播稿
2014/10/20 职场文书
zabbix自定义监控nginx状态实现过程
2021/11/01 Servers