继续我们的 Javascript 面向对象之旅。 上次 已经提到过怎么去声明个 Javascript 类了,这篇主要是说明如何去继承 Javascript 类。
相对于其他语言而言,Javascript 类的机制显得尤其的宽松。这似乎是把双刃剑,使用不当就有可能割伤自己的手指。Javascript 没有严格意义上的抽象类的概念,这就意味着任何的类都可以被实例化。
在开始下面的文章前,我们首先重新认识下上篇我们已经定义好的类。
function Car(sColor) {
this.color = sColor;
this.showColor = function() {
alert(this.color);
}
}
是的,看起来非常好理解。那么接下来就开始在 Car 上加装点东西使它变成更具体化。
对象冒充
对象冒充(object masquerading)是日常 coding 中最常见的一种类继承的手法。其原理就是,构造函数使用 this 关键字给所有的属性和方法赋值。
因为构造函数只是一个函数,所以可以使基类的构造函数成为继承类的一个方法。这样,继承类就能收到基类构造函数中定义的属性和方法。例如上述的 Car 基类就可以这样继承。
function Trunk(sColor, sName) {
this.Car = Car;
this.Car(sColor);
delete this.Car;
this.name = sName;
this.sayName = function () {
alert(this.name);
}
}
在这里要需要注意一点,新属性和新的方法比必须在删除了新方法的代码后定义,否则可能会覆盖继承类的相关属性和方法。
深刻理解 Javascript 的 this 和函数机制是非常有用的。依次类推,我们就可以给让一个类继承多个基类(多重继承)。
call() 方法
了解对象冒充的机制以后使用 call() 方法就非常容易理解了。从代码量上理解,它仅仅是将上述方法的三条语句合并成一条(是的,没有任何的不同)。
function Trunk(sColor, sName) {
//this.Car = Car;
//this.Car(sColor);
//delete this.Car;
Car.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
}
}
有关如何使用 call() 的其他信息,可以访问 这里获得 。
apply() 方法
相对于 call() 方法,自然对于 apply() 方法也同样能够运行。只不过其参数是以数组的形式传递,请参考下面的代码。
function Trunk(sColor, sName) {
//this.Car = Car;
//this.Car(sColor);
//delete this.Car;
Car.apply(this, [sColor]);
this.name = sName;
this.sayName = function () {
alert(this.name);
}
}
当然,继承类的参数顺序与基类的参数顺序完全一致才可以传递参数对象。即便只有一个参数,也需要使用数组传递(比如上述的例子)。
原型链
上篇文章 已经介绍了使用原型方式定义类,而原型链扩展了这种方式,这种继承的方式非常的有趣。其实 prototype 对象是个模板,要实例化的对象都以这个模板为基础(我个人将这里的「模板」理解为指针)。
总而言之,prototype 对象的任何属性和方法都会被传递给那个类的所有实例,而原型链就是利用这种功能来实现继承机制,且看下面的代码实现。
function Car() {
}
Car.prototype.color = "red";
Car.prototype.showColor = function () {
alert(this.color);
}
Trunk.prototype = new Car;
从代码中可以得知,调用 Car 的构造函数时,没有给它传递任何的参数。这在原型链中视标准的做法,要确保构造函数没有任何的参数。
原型链的弊端就是不支持多重继承,同时与艳星连会用另一类型的对象重写基类的 prototype 属性(就犹如上篇类的声明说所言的道理一样,所以我个人将其理解为指针)。
混合模式
同类的声明机制一样,两种方法(对象冒充和原型链)也可以结合在一起实现继承机制。比如下面是一个非常「完美的」类声明。
function Car(sColor) {
this.color = sColor;
if (typeof Car._initialized == "undefined") {
Car.prototype.showColor = function() {
alert(this.color);
};
Car._initialized = true;
}
}
要继承这个类必须使用两种方式同时使用,应为类的声明中也使用了两种方式。
function Trunk(sColor, sName) {
Car.call(this, sColor);
this.name = sName;
}
Trunk.prototype = new Car();
Trunk.prototype.showName = function () {
alert(this.showName);
}
由于混合方式使用了原型链,所以 instanceof 运算符仍然能够正确的运行。
理论方面的东西就到这里为止,接下来计划说点实际的东西。不过还是调下大家的胃口,我打算安排在下一篇文章中说明,敬请期待。
未完待续
\"理论方面的东西就到这里为止,接下来计划说点实际的东西。不过还是调下大家的胃口,我打算安排在下一篇文章中说明,敬请期待。\"
下一篇文章?? where where ---燃烧脑细胞时间???
你好,你在文章中提到,混合模式的最完美的类声明。感觉比原型链复杂,但是没有发现其优点,能不能显式的说明一下。谢谢。
if (typeof Car._initialized == \"undefined\") {
这句话并不能使得Car.prototype.showColor只定义一次,因为可以使用delete Car._initialized删除这个属性,使得以上条件语句再次为true。不知道博主是不是这个意图?