JS 继承
JS实现继承主要是依靠原型链来实现的。
原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法。具体解释为:让子引用类型的原型对象等于父引用类型的实例,子类型的原型对象中就会有一个内部属性[[prototype]](__ proto __)指向父类型的原型对象,倘若父类型的原型对象又是等于另外一个引用类型的实例,重复上述过程,就形成了一条原型链。
原型搜索机制:当读取一个实例的属性或者方法时,会首先在实例中搜索,搜索不到就会顺着原型链向上搜索,直到搜索到原型链的末端(Object.prototype)为止。
所有引用类型默认都继承了Object,所有函数的默认原型都是Object的实例。
// 原型链模式(重写原型对象,用一个新类型的实例替代)
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.constructor = SubType;
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
仅仅利用原型链来实现继承,父类型的实例属性会变成子类型的原型属性而被子类型的实例所共享,若属性时一个引用类型,那改变一个子类型实例对象的属性,另外一个实例也自然会同步修改了。
借用构造函数
在子类型构造函数中执行父类型构造函数,从而使子类型实例也拥有父类型的实例属性。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType(name){
//inherit from SuperType
SuperType.call(this, name);
this.age = 29;
}
组合继承
原型链模式与构造函数模式相结合:使用原型链实现对原型属性和方法的继承,使用借用构造函数来实现对实例属性的继承。是JS中最常见的继承模式。
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;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
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
组合模式有一个不足就是都会调用两次父类型的构造函数:一次是创建子类型原型的时候,一次是在子类型构造函数的内部。子类型原型中的父类型实例属性时完全没有必要的。
原型式模式
借助原型基于已有的对象创建新对象。
function object(o) {
function F(){ }
F.prototype = o;
return new F();
}
新对象将传入的对象o作为原型。若o中有引用类型的属性,修改新对象的该属性时,由于该属性在原型中,o中的该属性也会同步修改。
ES5新增的Object.create()方法同上述函数(支持IE9+)。
寄生式继承
创建一个封装继承过程的函数。
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert("Hi");
}
return clone;
}
寄生式继承无法做到对象方法的复用,从而占用内存,降低效率。
寄生组合式继承
将寄生式继承与组合继承相结合。仅仅将子类型的原型复制为超类型原型的一个副本。从而避免调用两次父类型构造函数以及子类型原型中包含父类型的实例属性。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
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);
};
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
寄生组合式继承是引用类型最理想的继承范式。