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 相关文章推荐
让网页根据不同IE版本显示不同的内容
Feb 08 Javascript
js window.onload 加载多个函数和追加函数详解
Jan 08 Javascript
jQuery寻找n以内完全数的方法
Jun 24 Javascript
关于事件mouseover ,mouseout ,mouseenter,mouseleave的区别
Oct 12 Javascript
jQuery div拖拽用法实例
Jan 14 Javascript
JS组件Bootstrap实现图片轮播效果
May 16 Javascript
JavaScript中boolean类型之三种情景实例代码
Nov 21 Javascript
boostrapTable的refresh和refreshOptions区别浅析
Jan 22 Javascript
vue脚手架搭建过程图解
Jun 06 Javascript
深入理解js A*寻路算法原理与具体实现过程
Dec 13 Javascript
Vue开发之watch监听数组、对象、变量操作分析
Apr 25 Javascript
vue实现轮播图帧率播放
Jan 26 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
全国FM电台频率大全 - 30 宁夏回族自治区
2020/03/11 无线电
第五节 克隆 [5]
2006/10/09 PHP
php 获取本机外网/公网IP的代码
2010/05/09 PHP
PHP批量上传图片的具体实现方法介绍.
2014/02/26 PHP
CentOS 6.3下安装PHP xcache扩展模块笔记
2014/09/10 PHP
php中使用base HTTP验证的方法
2015/04/20 PHP
JSON用法之将PHP数组转JS数组,JS如何接收PHP数组
2015/10/08 PHP
PHP面向对象程序设计实例分析
2016/01/26 PHP
Javascript注入技巧
2007/06/22 Javascript
jQuery AJAX 调用WebService实现代码
2010/03/24 Javascript
深入理解JavaScript系列(16) 闭包(Closures)
2012/04/12 Javascript
JS使用正则截取两个字符串之间的字符串实现方法详解
2017/01/06 Javascript
Vue.js 2.0学习教程之从基础到组件详解
2017/04/24 Javascript
详解vee-validate的使用个人小结
2017/06/07 Javascript
使用Node.js实现ORM的一种思路详解(图文)
2017/10/24 Javascript
浅析为什么a=&quot;abc&quot; 不等于 a=new String(&quot;abc&quot;)
2017/10/25 Javascript
用vue2.0实现点击选中active其他选项互斥的效果
2018/04/12 Javascript
JavaScript中AOP的实现与应用
2019/05/06 Javascript
element-ui 远程搜索组件el-select在项目中组件化的实现代码
2019/12/04 Javascript
简单了解前端渐进式框架VUE
2020/07/20 Javascript
[01:32]TI奖金增速竟因它再创新高!DOTA2勇士令状不朽珍藏Ⅰ饰品欣赏
2018/05/18 DOTA
浅谈python socket函数中,send与sendall的区别与使用方法
2017/05/09 Python
Python数据分析之双色球统计两个红和蓝球哪组合比例高的方法
2018/02/03 Python
python实现AES加密与解密
2019/03/28 Python
浅谈python 中类属性共享的问题
2019/07/02 Python
python 实现快速生成连续、随机字母列表
2019/11/28 Python
Pandas时间序列:重采样及频率转换方式
2019/12/26 Python
基于pygame实现童年掌机打砖块游戏
2020/02/25 Python
python GUI库图形界面开发之PyQt5控件数据拖曳Drag与Drop详细使用方法与实例
2020/02/27 Python
python 6种方法实现单例模式
2020/12/15 Python
迪卡侬(Decathlon)加拿大官网:源自法国的运动专业超市
2020/11/22 全球购物
保送生自荐信范文
2013/10/06 职场文书
2014年党员自我评价材料
2014/09/22 职场文书
高中教师个人总结
2015/02/10 职场文书
加强党性修养心得体会
2016/01/21 职场文书
Python Pandas 删除列操作
2022/03/16 Python