继上篇《JavaScript 中的面向对象编程》,JS中的继承,单独使用原型链存在的问题是:对象实例会共享所有继承的属性和方法(包括实例属性)。因此,为解决这些问题,继承存在多种模式。
1. 借用构造函数
在子类构造函数的内部调用父类构造函数,使每个实例具有自己的属性。具体:使用call或apply方法,将父对象的构造函数绑定到子对象上,见示例。
2. 组合继承
使用最多的继承模式就是组合继承,使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性。
function Supertype(name){
this.name = name; //实例属性
this.colors = ["red","green","blue"]; //实例属性
}
Supertype.prototype.sayName = function(){ //原型方法
return this.name;
}
function Subtype(name, age){
Supertype.call(this, name); //继承实例属性
this.age = age; //新属性
}
Subtype.prototype.sayAge = function(){
return this.age;
}
Subtype.prototype = new Supertype();//Subtype继承了Supertype,此时Subtype.prototype对象的constructor指向Supertype
Subtype.prototype.constructor = Subtype;//将Subtype.prototype对象的constructor指向Subtype
var instance = new Subtype("hxj",20);
alert(instance.colors); //"red","green","blue"
alert(instance.sayName()); //"hxj"
alert(instance.sayAge()); //20
3. 原型式继承(
非构造函数的继承)
不定义构造函数,利用object()函数,对传入的对象执行浅拷贝。
function object(o){
function F(){} //利用空对象作为中介
F.prototype = o; //将传入的对象作为空对象的原型
return new F(); //返回空对象的新实例
}
使用示例:
var person = {
name: "hxj",
friends:["wyr","lzh"]
}
var newperson = object(person); //调用
newperson.name = "lyy"; //重新赋值
newperson.friends.push("hhh"); //重新赋值
4. 寄生式继承
类似于原型式继承,创建一个仅用于封装继承过程的函数。在函数内部,基于传入的对象创建新的对象,再增强对象(添加新属性和方法),最后返回对象。
5. 寄生组合式继承(最有效方式)
前面组合继承的缺点:会调用两次父类构造函数。
第一次: Subtype.prototype = new Supertype();
第二次: var instance = new subtype("hxj", 20); => Supertype.call(this, name);
为解决此问题,使用寄生组合式继承:使用原型链的混成形式继承共享的属性和方法,通过借用构造函数继承实例属性。
本质是:不用为指定子类的原型而调用父类的构造函数,只需要父类原型的一个副本。
function inherit(subtype, Supertype){
var o = object(Supertype.prototype); //创建父类的一个副本
o.constructor = subtype; //重新设置construct属性
subtype.prototype = o; //将副本赋值给子类的原型
}
换一种写法(不使用object()函数,合并为一个extend()函数):
function extend(subtype, Supertype) {
var F = function () { };
F.prototype = Supertype.prototype;
subtype.prototype = new F();
subtype.prototype.constructor = subtype;
}
使用示例:
function Supertype(name){
this.name = name; //实例属性
this.colors = ["red","green","blue"]; //实例属性
}
Supertype.prototype.sayName = function(){ //原型方法
return this.name;
}
function Subtype(name, age){
Supertype.call(this, name); //继承实例属性
this.age = age; //新属性
}
Subtype.prototype.sayAge = function(){
return this.age;
}
inherit(subtype, Supertype); //调用
var instance = new Subtype("hxj",20);
alert(instance.colors); //"red","green","blue"
alert(instance.sayName()); //"hxj"
alert(instance.sayAge()); //20