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 hasOwnProperty 方法 &amp; in 关键字
Nov 26 Javascript
Jquery 基础学习笔记之文档处理
May 29 Javascript
Ruffy javascript 学习笔记
Nov 30 Javascript
通过jQuery打造支持汉字,拼音,英文快速定位查询的超级select插件
Jun 18 Javascript
在次封装easyui-Dialog插件实现代码
Nov 14 Javascript
Express URL跳转(重定向)的实现方法
Apr 07 Javascript
AngularJs 终极购物车(实例讲解)
Nov 08 Javascript
vue动态设置img的src路径实例
Sep 18 Javascript
微信小程序授权登录及解密unionId出错的方法
Sep 26 Javascript
微信小程序webview实现长按点击识别二维码功能示例
Jan 24 Javascript
Vuejs学习笔记之使用指令v-model完成表单的数据双向绑定
Apr 29 Javascript
vue 验证码界面实现点击后标灰并设置div按钮不可点击状态
Oct 28 Javascript
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
JAVA/JSP学习系列之七
2006/10/09 PHP
PHP中实现汉字转区位码应用源码实例解析
2010/06/14 PHP
php中http与https跨域共享session的解决方法
2014/12/20 PHP
Laravel中注册Facades的步骤详解
2016/03/16 PHP
thinkPHP框架自动填充原理与用法分析
2018/04/03 PHP
PHP Trait代码复用类与多继承实现方法详解
2019/06/17 PHP
AJAX使用了UpdatePanel后无法使用alert弹出脚本
2010/04/02 Javascript
jQuery学习笔记(1)--用jQuery实现异步通信(用json传值)具体思路
2013/04/08 Javascript
JavaScript 语言基础知识点总结(思维导图)
2013/11/10 Javascript
jQuery向上遍历DOM树之parents(),parent(),closest()之间的区别
2013/12/02 Javascript
jquery表单验证框架提供的身份证验证方法(示例代码)
2013/12/27 Javascript
JavaScript中this详解
2015/09/01 Javascript
jq实现左滑显示删除按钮,点击删除实现删除数据功能(推荐)
2016/08/23 Javascript
jquery日历插件e-calendar升级版
2016/11/10 Javascript
Bootstrap table简单使用总结
2017/02/15 Javascript
原生JS实现图片翻书效果
2017/02/16 Javascript
web.js.字符串与正则表达式操作
2017/05/13 Javascript
AngularJS  ng-repeat遍历输出的用法
2017/06/19 Javascript
extjs简介_动力节点Java学院整理
2017/07/17 Javascript
如何开发出更好的JavaScript模块
2017/12/22 Javascript
解决JS表单验证只有第一个IF起作用的问题
2018/12/04 Javascript
图文详解vue框架安装步骤
2019/02/12 Javascript
ES6函数和数组用法实例分析
2020/05/23 Javascript
python解析html开发库pyquery使用方法
2014/02/07 Python
python导出hive数据表的schema实例代码
2018/01/22 Python
python多进程实现文件下载传输功能
2018/07/28 Python
pytorch 可视化feature map的示例代码
2019/08/20 Python
python编写softmax函数、交叉熵函数实例
2020/06/11 Python
关于多种方式完美解决Python pip命令下载第三方库的问题
2020/12/21 Python
Html5之自定义属性(data-,dataset)
2019/11/19 HTML / CSS
香港太阳眼镜网上商店:SmartBuyGlasses香港
2016/07/22 全球购物
Lulu & Georgia官方网站:购买地毯、家具、抱枕、壁纸、床上用品等
2018/03/19 全球购物
运动会广播稿200字(10篇)
2014/10/12 职场文书
2014年办公室文员工作总结
2014/11/12 职场文书
孔子观后感
2015/06/08 职场文书
就业指导讲座心得体会
2016/01/15 职场文书