在此之前,我写了一个'
在JavaScript面向对象编程中使用继承'的一系列文章。有很多的热心网友参与了讨论,指出了其中很多的问题并给予了我很多的好建议,非常感谢他们。同时在OOP中和继承关系非常紧密的就是
重载这个东西,那么我介绍的继承方法支持重载吗?
这里我要说的重载(override),确实就是传统OO中的重载,而不是我们在JavaScript对象中对其同名属性或方法的重写(rewrite)。在此我就不详细讲什么是OOP中的override了,因为只要是使用.NET来编程的程序员都应该很了解的。
在我们的 附加继承法的核心代码中:
delete base[key]的作用是删除掉基类对象实例中的属性,因为它们都已经被shallow copy到子类实例中了。而
this.base = base;的作用是保留了基类对象实例,实际上就是保留了基类中的所有方法,这些保留下来的方法同时也是shallow copy到子类中了的。只是我们在子类中可以方便的将其rewrite,注意这里不是override,而一定是rewrite。rewrite后其实子类实例中就没有了基类中copy过来的同名方法了。
不过由于我们保留了对基类实例的引用 this.base = base;,所以即使在子类中被rewrite的方法,基类中仍然可以使用 this.base.MethodName()来访问。这个调用基类方法的语句已经和C#中的override后调用基类方法非常的相似了 ,我们就这么来调用基类方法可以吗?当然是不可以的,因为这时的方法都属于基类对象base,它是无法访问子类中的属性的。不过幸好JScript提供两个函数,call和apply,于是我们可以使用这样两个方法来重新应用 this(子类实例)对象。调用语句如下:
第一个语句用来处理没有参数的override的情况,第二个用来处理调用子类方法有参数,而其参数还需要原样传入基类方法的情况。这样的override语句虽然算不时上sexy,不过他的好处是对基类方法的编写没有任何的要求,而且即使错误的使用了
this.base.MethodName()还是比较容易debug出错误来的。
这个override最大问题是什么呢?就是它只能支持对子类中,没有使用同样override方法的方法进行这种override。好绕口,看看下面这个实例就知道问题了:
// 运行示例代码需要'
附加继承法
'中那个Extends方法的支持
三个类grandpa、father和son,它们分别具有两个属性和两个方法。其中的getName方法,是从father中调用了grandpa中被override的同名方法,然后又从son中的getName调用了father中被override的同名方法。这样就在执行son中的getName时,在father中的getName方法中产生死循环,最后stack overflow,IE crash 。而其中的getFamilyName方法,是从son中调用grandpa中被override的同名方法,而grandpa中的getFamilyName没有再调用任何override方法,所以可以正确的运行。
由于JavaScript的OOP始终是在做一些模拟的工作,除了能实现某些模拟出来的功能外,同时实现模拟的代码的复杂度也是应该考虑的,像这样的override从代码实现上看非常的轻量级,而且也是很容易理解的。同时可以满足绝大多数的JS控件的开发,随后我会再给出一个ListView控件的事例,它的实现完全基于'附加继承法'和我这篇文章中所讲到的'重载'技术。
非常欢迎您的宝贵意见和建议,让我们开发的更好(copy了dudu的语录:)。
Attention:运行代码alert(s.getName())一定要关闭其它的IE,或避免正在任何IE中填写表单。
这里我要说的重载(override),确实就是传统OO中的重载,而不是我们在JavaScript对象中对其同名属性或方法的重写(rewrite)。在此我就不详细讲什么是OOP中的override了,因为只要是使用.NET来编程的程序员都应该很了解的。
在我们的 附加继承法的核心代码中:
for
(
var
key
in
base )
{
if ( ! this [key] )
{
this [key] = base[key];
if ( typeof (base[key]) != ' function ' )
{
delete base[key];
}
}
}
this .base = base;
{
if ( ! this [key] )
{
this [key] = base[key];
if ( typeof (base[key]) != ' function ' )
{
delete base[key];
}
}
}
this .base = base;
不过由于我们保留了对基类实例的引用 this.base = base;,所以即使在子类中被rewrite的方法,基类中仍然可以使用 this.base.MethodName()来访问。这个调用基类方法的语句已经和C#中的override后调用基类方法非常的相似了 ,我们就这么来调用基类方法可以吗?当然是不可以的,因为这时的方法都属于基类对象base,它是无法访问子类中的属性的。不过幸好JScript提供两个函数,call和apply,于是我们可以使用这样两个方法来重新应用 this(子类实例)对象。调用语句如下:
this
.base.MethodName.call(
this
);
this .base.MethodName.apply( this , arguments);
this .base.MethodName.apply( this , arguments);
这个override最大问题是什么呢?就是它只能支持对子类中,没有使用同样override方法的方法进行这种override。好绕口,看看下面这个实例就知道问题了:
<
html
>
< head >
< title > Researh Override in JavaScript OOP </ title >
< meta name ="author" content ="birdshome@博客园" />
</ head >
< body >
< script language ="javascript" >
function grandpa(name, familyName)
{
this.name = name;
this.familyName = familyName;
}
grandpa.prototype.getName = function()
{
return "grandpa's name: " + this.name;
};
grandpa.prototype.getFamilyName = function()
{
return "grandpa's family name: " + this.familyName;
};
function father(name, familyName)
{
this.Extends(grandpa);
this.name = name;
this.familyName = familyName;
}
father.prototype.getName = function()
{
return this.base.getName.call(this) + "/r/nfather's name: " + this.name;
};
function son(name, familyName)
{
this.Extends(father);
this.familyName = familyName;
this.name;
}
son.prototype.getName = function()
{
return this.base.getName.call(this) + "/r/nson's name: " + this.name;
};
son.prototype.getFamilyName = function()
{
return this.base.getFamilyName.call(this) + "/r/nson's family name: " + this.familyName;
};
</ script >
< script language ="javascript" >
var g = new grandpa('Michael', 'William');
alert(g.getName());
var f = new father('John', 'William');
alert(f.getName());
var s = new son('Tom', 'William');
// alert(s.getName());
alert(s.getFamilyName());
</ script >
</ body >
</ html >
< head >
< title > Researh Override in JavaScript OOP </ title >
< meta name ="author" content ="birdshome@博客园" />
</ head >
< body >
< script language ="javascript" >
function grandpa(name, familyName)
{
this.name = name;
this.familyName = familyName;
}
grandpa.prototype.getName = function()
{
return "grandpa's name: " + this.name;
};
grandpa.prototype.getFamilyName = function()
{
return "grandpa's family name: " + this.familyName;
};
function father(name, familyName)
{
this.Extends(grandpa);
this.name = name;
this.familyName = familyName;
}
father.prototype.getName = function()
{
return this.base.getName.call(this) + "/r/nfather's name: " + this.name;
};
function son(name, familyName)
{
this.Extends(father);
this.familyName = familyName;
this.name;
}
son.prototype.getName = function()
{
return this.base.getName.call(this) + "/r/nson's name: " + this.name;
};
son.prototype.getFamilyName = function()
{
return this.base.getFamilyName.call(this) + "/r/nson's family name: " + this.familyName;
};
</ script >
< script language ="javascript" >
var g = new grandpa('Michael', 'William');
alert(g.getName());
var f = new father('John', 'William');
alert(f.getName());
var s = new son('Tom', 'William');
// alert(s.getName());
alert(s.getFamilyName());
</ script >
</ body >
</ html >
三个类grandpa、father和son,它们分别具有两个属性和两个方法。其中的getName方法,是从father中调用了grandpa中被override的同名方法,然后又从son中的getName调用了father中被override的同名方法。这样就在执行son中的getName时,在father中的getName方法中产生死循环,最后stack overflow,IE crash 。而其中的getFamilyName方法,是从son中调用grandpa中被override的同名方法,而grandpa中的getFamilyName没有再调用任何override方法,所以可以正确的运行。
由于JavaScript的OOP始终是在做一些模拟的工作,除了能实现某些模拟出来的功能外,同时实现模拟的代码的复杂度也是应该考虑的,像这样的override从代码实现上看非常的轻量级,而且也是很容易理解的。同时可以满足绝大多数的JS控件的开发,随后我会再给出一个ListView控件的事例,它的实现完全基于'附加继承法'和我这篇文章中所讲到的'重载'技术。
非常欢迎您的宝贵意见和建议,让我们开发的更好(copy了dudu的语录:)。
Attention:运行代码alert(s.getName())一定要关闭其它的IE,或避免正在任何IE中填写表单。
posted on 2005-02-24 00:22 birdshome 阅读(6237) 评论(1) 编辑 收藏 收藏至365Key 所属分类: JScript&DHTML开发
# re: 在JavaScript面向对象编程中使用重载 回复
评论
由于发现bug,请再参阅:"
在JScript面向对象编程中使用重载(续)、修复附加继承法中模拟重载的一缺陷"两篇文章。