概述
Ext.extend是Ext的继承机制,这个函数的代码相当难懂。要明白这个函数的代码,首先要知道这个函数如何使用。
使用方式
使用示例
假设有个function名为SuperClass,要实现一个子类,名为MyClass。下面的两种方式都可以实现这个功能。
MyClass = Ext.extend(SuperClass, { /* */ });
Ext.extend(MyClass, SuperClass, { /* */});
下面来个具体示例:
var a = function(id){
this.id = id;
}
a.prototype = {
tostring : function(){
return this.id;
}
};
b = function(id){
b.superclass.constructor.call(this, id);
}
Ext.extend(b, a, {
tostring : function(){
return String.format(\"b:{0}\", this.id);
}
});
//测试一下
var obj1 = new a(\"obj1\");
alert(obj1.tostring());
var obj2 = new b(\"obj2\"); alert(obj2.tostring());
或者下面的代码,可以得到同样的效果:
var a = function(id){
this.id = id;
}
a.prototype = {
tostring : function(){
return this.id;
}
};
b = Ext.extend(a, {
tostring : function(){
return String.format(\"b:{0}\", this.id);
}
});
//测试一下
var obj1 = new a(\"obj1\"); alert(obj1.tostring());
var obj2 = new b(\"obj2\");
alert(obj2.tostring());
一个错误例子
下面看个示例:
BaseClass = function() {
this.f1 = function() {
alert(\"f1 in base\");
}
this.f2 = function() {
alert(\"f2 in base\");
}
}
ChildClass = function() {
ChildClass.superclass.constructor.call(this);
}
Ext.extend(ChildClass, BaseClass, {
f1: function() {
alert(\"f1 in child\");
},
f3: function() {
alert(\"f3 in child\");
}
});
var b = new ChildClass(); b.f1();
b.f2();
b.f3();
可以去执行一下,可以发现f1的执行结果仍然是\"f1 in base\"。并没有真正的达到override的效果。
Ext.extend puts the properties specified in the 3rd argument into the subclass's prototype
也就是说:第三个参数里面的函数被放置在了子类的prototype中。
而在ChildClass.superclass.constructor.call(this);这句上,BaseClass的f1成了ChildClass的变量,而不是ChildClass.prototype。通过对JavaScript的原型继承的了解,可以知道,实例变量的优先级是高于prototype的,所以上面的这个代码是达不到override的功能的。
修改的方式如下:
BaseClass = function() {
};
BaseClass.prototype = {
f1: function() {
alert(\"f1 in base\");
}
};
代码解读
JavaScript中的继承实现
先了解一下最简单的继承是如何实现的:
function Extend(subFn, superFn){
subFn.prototype = new superFn()
subFn.prototype.constructor = subFn
}
function Animal(){
this.say1 = function(){
alert(\"Animal\");
}
}
function Tiger(){
this.say2 = function(){
alert(\"Tiger\");
}
}
Extend(Tiger,Animal);
var tiger = new Tiger();
tiger.say1();//\"Animal\"
tiger.say2();//\"Tiger\"
可以看到最简单的继承只做了两件事情,一是把subFn的prototype设置为superFn的一个实例,然后设置subFn.prototype.constructor为subFn。
Ext.extend的代码
Ext.extend函数中用到了Ext.override,这个函数把第二个参数中的所有对象复制到第一个对象的prototype中。首先贴上Ext.override函数的代码:
Ext.override = function(origclass, overrides){
if(overrides){
var p = origclass.prototype;
for(var method in overrides){
p[method] = overrides[method];
}
}
}
然后贴上Ext.extend的代码:
/**
* 继承,并由传递的值决定是否覆盖原对象的属性
* 返回的对象中也增加了override()函数,用于覆盖实例的成员
* @param {Object} subclass 子类,用于继承(该类继承了父类所有属性,并最终返回该对象)
* @param {Object} superclass 父类,被继承
* @param {Object} overrides (该参数可选) 一个对象,将它本身携带的属性对子类进行覆盖
* @method extend
*/
function extend (){
// inline overrides
var io = function(o){
for(var m in o){
this[m] = o[m];
}
};
return function(sb, sp, overrides){
if(typeof sp == 'object'){
overrides = sp;
sp = sb;
sb = function(){sp.apply(this, arguments);};
}
var F = function(){}, sbp, spp = sp.prototype;
F.prototype = spp;
sbp = sb.prototype = new F();
sbp.constructor=sb;
sb.superclass=spp;
if(spp.constructor == Object.prototype.constructor){
spp.constructor=sp;
}
sb.override = function(o){
Ext.override(sb, o);
};
sbp.override = io;
Ext.override(sb, overrides);
return sb;
};
}();
代码中进行了太多的简写,看起来不是特别方便,把代码中的简写补全,代码如下:
function extend(){ // inline overrides
var inlineOverride = function(o){
for (var m in o) {
this[m] = o[m];
}
};
return function(subFn, superFn, overrides){
if (typeof superFn == 'object') {
//如果subFn也是对象的话(一般来说subFn这里放的是父类的构造函数),
那么第三个参数overrides参数相当于被忽略掉 overrides = superFn;
superFn = subFn;
//subFn重新定义了函数
subFn = function(){
superFn.apply(this, arguments);
};
}
var F = function(){
}, subFnPrototype, superFnPrototype = superFn.prototype;
F.prototype = superFnPrototype;
subFnPrototype = subFn.prototype = new F();
subFnPrototype.constructor = subFn;
subFn.superclass = superFnPrototype;
if (superFnPrototype.constructor ==
Object.prototype.constructor) {
superFnPrototype.constructor = superFn;
}
subFn.override = function(obj){
Ext.override(subFn, obj);
};
subFnPrototype.override = inlineOverride;
Ext.override(subFn, overrides);
return subFn;
};
};
补全以后也不是特别容易明白,那么我们就把这个代码分开,分为2个参数和3个参数。
两个参数的Ext.extend代码
首先把代码改写成两个参数的。
//两个参数的时候的代码,注意第二个参数必须为object function extend(){ // inline overrides
var inlineOverride = function(o){
for (var m in o) {
this[m] = o[m];
}
};
return function(superFn, overrides){
var subFn = function(){
superFn.apply(this, arguments);
};
var F = function(){
}, subFnPrototype, superFnPrototype = superFn.prototype;
F.prototype = superFnPrototype;
//注意下面两句就是上面最简单的继承实现。
subFnPrototype = subFn.prototype = new F();
subFnPrototype.constructor = subFn;
//添加了superclass属性指向superFn的Prototype subFn.superclass = superFnPrototype;
//为subFn和subFnPrototype添加override函数
subFn.override = function(obj){
Ext.override(subFn, obj);
};
subFnPrototype.override = inlineOverride;
//覆盖掉子类prototype中的属性
Ext.override(subFn, overrides);
return subFn;
};
};
从注释中可以看到,做的工作很简单,只是定义一个subFn函数,这个函数中会调用superFn函数。定义了subFn以后,就使用上面的最简单的继承方式实现继承。然后为subFn和subFn的prototype添加了一个override函数。最后的Ext.override(subFn, overrides);把overrides中的函数写入subFn的prototype中。
三个参数的Ext.extend代码
下面我们把函数改写为只处理3个参数的,改写后的代码如下:
//三个参数时的代码 function extend(){ // inline overrides
var inlineOverride = function(o){
for (var m in o) {
this[m] = o[m];
}
};
return function(subFn, superFn, overrides){
var F = function(){
}, subFnPrototype, superFnPrototype = superFn.prototype;
F.prototype = superFnPrototype;
//注意下面两句就是上面最简单的继承实现。
subFnPrototype = subFn.prototype = new F();
subFnPrototype.constructor = subFn;
//添加了superclass属性指向superFn的Prototype subFn.superclass = superFnPrototype;
//为subFn和subFnPrototype添加override函数
subFn.override = function(obj){
Ext.override(subFn, obj);
};
subFnPrototype.override = inlineOverride;
//覆盖掉子类prototype中的属性
Ext.override(subFn, overrides);
return subFn;
};
};
过程与两个参数的时候相差无几,只是两个参数的时候,subFn时重新定义的一个function,而三个参数的时候,这个步骤就省略了。
总结及说明
这样大家就对这个函数很明白了吧,也可以知道Ext.extend的继承只会覆写构造函数prototype中的对象,使用的时候需要多加注意。
注意下面一段代码:
if (superFnPrototype.constructor == Object.prototype.constructor) {
superFnPrototype.constructor = superFn;
}
这段代码我在改写的Ext.extend中省略掉了。原因在于我尝试了多次,发现参数为两个参数的时候,只有第一个参数为Object对象或者为3个参数的时候,第二个参数为Object才会进入此段代码。
因篇幅问题不能全部显示,请点此查看更多更全内容