JavaScript实现继承的六种方式
1.借用构造函数
借用构造函数实现继承,就是在子类型构造函数内部调用父类型构造函数,使用apply()和call()方法将父类型的构造函数绑定在子类型上。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
SuperType.call(this); //继承了 SuperType
}
var instance1 = new SubType();
instance1.colors.push("black");
//instance1实例得到了一个color属性的副本,改变的是instance1这个实例的color属性,而不是其原型对象中的color属性。
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
通过使用apply()或call()方法,实际上是在新创建的SubType实例的环境下调用SuperType构造函数。SubType的每个实例都会具有自己的color属性的副本。
2.原型链继承
依靠原型链来实现继承,就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
//创建 SuperType 的实例,并将该实例赋给SubType.prototype,这样SubType就继承了SuperType。
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
var instance = new SubType();
//存在于SuperType 的实例中的所有属性和方法,也存在于 SubType.prototype 中了。
alert(instance.getSuperValue()); //true
3.组合继承
组合继承就是将原型链和借用构造函数的技术组合到一起。
使用原型链实现对原型属性和方法的继承;通过借用构造函数来实现对实例属性的继承。这样在原型上定义的方法实现了函数的复用,又能保证每个实例都有自己的属性。
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;
//添加constructor属性是为了弥补重写原型而失去的默认constructor属性。
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
//instance1和instance2这两个实例分别拥有自己的属性,又拥有相同的方法。
4.原型式继承
借助原型可以基于已有的对象创建新对象,而不必因此创建自定义的类型。
function object(o){
function F(){} ;
F.prototype = o;
return new F();
}
//在 object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
//person对象作为后续对象的继承,后续的对象在person的基础上根据具体的需求进行修改。
var anotherPerson = object(person);
//创建的新对象将person作为原型。原型中包含一个基本类型值的属性和一个引用类型值的属性,那么这个引用类型值的属性friends,将会共享给新创建的对象。
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"
ES5提供了Object.create()方法来实现原型式继承。
两个参数:用作新对象原型的对象、为新对象定义其他属性的对象(可选)。
只传入一个参数时,Object.create()方法和object()方法行为相同:
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
console.log(person);
Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都通过自己的描述符定义。重新定义的属性会覆盖原型对象上的同名属性。
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
5.寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
function createAnother(original){ //参数original是作为新对象基础的对象
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
//基于person返回了一个新对象。新对象不仅拥有person的所有属性和方法,同时也拥有自己的sayHi()方法。
6.寄生组合式继承
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上就是使用寄生式继承来继承超类型的原型,再将结果指定给子类型的原型。
function inheritPrototype(subType, superType){
var prototype = Object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
函数接受两个参数:子类型和超类型。
第一步:创建超类型原型的一个副本。
第二步:为创建的副本添加属性,这里添加的constructor 属性是为了弥补因重写原型而失去的默认的 constructor 属性。
第三步:将新创建的对象赋值给子类型的原型。
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);
};