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

这里接口的意思是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本身的行为,这个就和我上面标注的没有看懂的那几句代码有关系。下回再讨论。

(0)

相关推荐

  • 深入了解javascript中的prototype与继承

    通常来说,javascript中的对象就是一个指向prototype的指针和一个自身的属性列表.javascript创建对象时采用了写时复制的理念.只有构造器才具有prototype属性,原型链继承就是创建一个新的指针,指向构造器的prototype属性.prototype属性之所以特别,是因为javascript时读取属性时的遍历机制决定的.本质上它就是一个普通的指针. 构造器包括: 1.Object2.Function3.Array4.Date5.String 下面我们来举一些例子吧 复制代

  • javascript prototype的深度探索不是原型继承那么简单第1/3页

    1 什么是prototype JavaScript中对象的prototype属性,可以返回对象类型原型的引用.这是一个相当拗口的解释,要理解它,先要正确理解对象类型(Type)以及原型(prototype)的概念.         前面我们说,对象的类(Class)和对象实例(Instance)之间是一种"创建"关系,因此我们把"类"看作是对象特征的模型化,而对象看作是类特征的具体化,或者说,类(Class)是对象的一个类型(Type).例如,在前面的例子中,p1和

  • JS继承--原型链继承和类式继承

    什么是继承啊?答:别人白给你的过程就叫继承. 为什么要用继承呢?答:捡现成的呗. 好吧,既然大家都想捡现成的,那就要学会怎么继承! 在了解之前,你需要先了解构造函数.对象.原型链等概念...... JS里常用的两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 原型链继承: 复制代码 代码如下: //要继承的对象var parent={name : "baba" say : function(){ alert("I am baba");}} //

  • 不错的一篇关于javascript-prototype继承

    1.最基本的用法 把ClassA的一个实例赋值给ClassB, ClassB就继承了ClassA的所有属性. 代码入下: function ClassA() { this.a='a'; } function ClassB() { this.b='b'; } ClassB.prototype=new ClassA(); var objB=new ClassB(); for(var p in objB)document.write(p+" "); [Ctrl+A 全选 注:如需引入外部Js

  • js中继承的几种用法总结(apply,call,prototype)

    一,js中对象继承 js中有三种继承方式 1.js原型(prototype)实现继承 复制代码 代码如下: <SPAN style="BACKGROUND-COLOR: #ffffff"><SPAN style="FONT-SIZE: 18px"><html>  <body>  <script type="text/javascript">      function Person(na

  • JavaScript是如何实现继承的(六种方式)

    前言:大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承,而且其实现继承主要是依靠 原型链 来实现. 1.原型链 基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法. 构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. 原型链实现继承例子: function SuperType() { this.property

  • js的2种继承方式详解

    js中继承可以分为两种:对象冒充和原型链方式 一.对象冒充包括三种:临时属性方式.call()及apply()方式1.临时属性方式 复制代码 代码如下: function Person(name){     this.name = name;     this.say = function(){          alert('My name is '+this.name);     }}function F2E(name,id){     this.temp = Person;     thi

  • 前端开发必须知道的JS之原型和继承

    一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型.这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型.譬如普通函数: 复制代码 代码如下: function F(){ alert(F.prototype instanceof Object) //true; } 构造函数,也即构造对象.首先了解下通过构造函数实例化对象的过程. 复制代码 代码如下: function A(x){ this.x

  • 实现JavaScript中继承的三种方式

    一.原型链继承 在原型链继承方面,JavaScript与java.c#等语言类似,仅允许单父类继承.prototype继承的基本方式如下: 复制代码 代码如下: function Parent(){} function Child(){} Child.prototype = new Parent(); 通过对象Child的prototype属性指向父对象Parent的实例,使Child对象实例能通过原型链访问到父对象构造所定义的属性.方法等. 构造通过原型链链接了父级对象,是否就意味着完成了对象

  • JavaScript的继承实现小结

    最近在忙前端的工作,因为之前做.net和php的开发比较多,前端开发喜欢把库拿来就用,几次事实证明,不懂原理,连改代码也改不好,所以还是下定决心研究下JavaScript的几个技术难点. 0x1.JavaScript的对象和构造函数 定义一个JavaScript对象可以这么定义 var a = { x : 1, y : 2, add : function () { return this.x + this.y; }, mul : function () { return this.x * thi

随机推荐