注:大概等同于《Professional JavaScript for Web Developer 2nd Edition》一书中关于继承实现的记录和总结,看过这本书的同学可以略过= =
一、最简单的实现方法Prototype Chaining
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
限制:
1.若指定用来作为子类的prototype的父类中有引用类型的属性,则子类的所有实例都可以修改这个属性
2.无法传递参数给父类的构造函数
由于以上的两个限制,这种实现方式很少使用。
二、Prototype Chaining的改进Constructor Stealing
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
//inherit from SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green”
这种方式解决了Prototype Chaining方式的限制,对于父类中的引用类型,使各子类可以拥有自己的一份实例,而且也可以向父类的构造函数中传入参数,但是这种方式也有限制:
1.每次构造一个子类的实例,父类中想要被继承的函数都只能在父类构造函数中定义,这样不能实现函数重用,因为构造函数中声明的方法每次调用构造函数时都会生成一个新的函数实例
2.子类无法访问父类的prototype中定义的属性和方法,因为子类的prototype不再指向父类的实例。
因此,这种方式也很少使用。
三、第一种和第二种方式的组合Combination Inheritance
使用prototype chaining来继承prototype中的属性和方法,使用constructor stealing来进行实例属性的继承
function SuperType(name){
this.name = name;
this.colors = [“red”, “blue”, “green”];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//inherit properties
SuperType.call(this, name);
this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType(“Nicholas”, 29);
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
instance1.sayName(); //”Nicholas”;
instance1.sayAge(); //29
var instance2 = new SubType(“Greg”, 27);
alert(instance2.colors); //”red,blue,green”
instance2.sayName(); //”Greg”;
instance2.sayAge(); //27
这种方式是比较常用的方式,但是这种方式,父类的构造函数会调用两次,一次是在指定子类的prototype的时候,一次是在子类的构造函数中。
四、其它方式
1.Prototypal Inheritance
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name: “Nicholas”,
friends: [“Shelby”, “Court”, “Van”]
};
var anotherPerson = object(person);
anotherPerson.name = “Greg”;
anotherPerson.friends.push(“Rob”);
var yetAnotherPerson = object(person);
yetAnotherPerson.name = “Linda”;
yetAnotherPerson.friends.push(“Barbie”);
alert(person.friends); //”Shelby,Court,Van,Rob,Barbie”
object方法创建了一个临时的构造函数F,然后将F的prototype指向传入的对象o,再返回一个F的实例。
这种方式适用于想避免独立声明一个构造函数所需要的资源开销,又想创建一个和别的对象具有相同属性或者行为的情况。
2.Parasitic Inheritance(寄生继承?)
function createAnother(original){
var clone = object(original); //create a new object by calling a function
clone.sayHi = function(){ //augment the object in some way
alert(“hi”);
};
return clone; //return the object
}
var person = {
name: “Nicholas”,
friends: [“Shelby”, “Court”, “Van”]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //”hi”
这种方式的理念和工厂模式有点相似,通过一个同统一的函数实现继承,然后根据需要给这个对象增加属性或者方法,然后返回。
此方式同样适用于你不想额外定义自己的类型和构造函数的情况,其中object函数不一定要和前一种方式一样,任何返回类型是object的函数都可以用于这种方式。
五、最好的方式Parasitic Combination Inheritance(寄生组合继承)
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //create object
prototype.constructor = subType; //augment object
subType.prototype = prototype; //assign object
}
function SuperType(name){
this.name = name;
this.colors = [“red”, “blue”, “green”];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
这种方法和combination inheritance方式类似,但是在使用prototype chaining的时候采用之前提到的parasitic inheritance来实现,避免重新调用一次父类的构造函数,如函数inheritePrototype中的代码所示。同样地,inheritePrototype中的object函数只要是返回类型是object的函数即可。
这种方式最早在Yahoo的YUI库中被使用,可以说是最优的继承实现方式了。