一、原型链继承
- 原型链继承:利用原型让一个引用类型继承另一个引用类型的属性和方法
//父类
function SuperType(){
this.SuperProperty = "SuperType property";
}
//子类
function SubType(){
this.subProperty = "SubType property";
}
//子类SubType继承了父类SuperType
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.SuperProperty) // 继承父类的SuperProperty属性
通过创建 SuperType 的实例,并将该实例赋给SubType.prototype 来实现继承
- 所有引用类型默认都继承了 Object,而这个继承也是通过原型链实现的,SubType 继承了 SuperType,而 SuperType 继承了 Object。
console.log(instance instanceof Object); //true
console.log(instance instanceof SuperType); //true
console.log(instance instanceof SubType); //true
缺点:
- 在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。例如:
var instanceSup=new SuperType()
instanceSup.hasOwnProperty("SuperProperty") //true
var instance=new SubType()
instance.hasOwnProperty("SuperProperty") //false
SuperProperty是SuperType()的实例属性,是SubType()的原型属性
- 不能在不影响所有对象实例的情况下,给父类型的构造函数传递参数
二、构造函数继承
- 构造函数继承:通过使用 call()或 apply()方法在子类型构造函数的内部调用父类型构造函数
function SuperType(){
this.SuperProperty = "SuperType property";
}
function SubType(){
//继承了 SuperType
SuperType.call(this);
}
var instance = new SubType();
console.log(instance.SuperProperty) // "SuperType property"
- SuperProperty是SuperType()的实例属性,也是SubType()实例属性
var instanceSup=new SuperType()
instanceSup.hasOwnProperty("SuperProperty") //true
var instance=new SubType()
instance.hasOwnProperty("SuperProperty") //false
- 传递参数相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向父类型构造函 数传递参数。例如:
function SuperType(name){
this.name = name;
}
function SubType(){
//继承了 SuperType,同时还传递了参数
SuperType.call(this, "lilei");
//实例属性
this.age = 26;
}
var instance = new SubType();
console.log(instance.name); //"lilei";
console.log(instance.age); //26
缺点:无法继承原型链上的属性和方法
三、组合继承
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性和方法的继承
function SuperType(name){
this.name = name;
}
SuperType.prototype.country="China";
function SubType(name){
//继承属性
SuperType.call(this,name);
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
var instance1 = new SuperType("lilei", 26);
var instance2 = new SubType("xiaoming", 22);
console.log(instance1.name) // lilei
console.log(instance1.country) // China
console.log(instance2.name) // xiaoming
console.log(instance2.country) // China
优点:
- 可以继承实例属性/方法,也可以继承原型属性/方法
- 可传参
- 函数可复用
缺点:
子类原型上有父类的实例属性,子类实例屏蔽了子类原型上的属性;
四、原型式继承
- 借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类;例如:
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function SuperType(){
this.name = "lilei";
}
var superType = new SuperType();
var subType = object(superType);
console.log(subType.name) // "lilei"
把SuperType实例化的对象作为参数传给object函数,函数就会返回一个新对象。这个新对象将superType作为原型。
- ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:第一个作为新对象原型的对象,另一个作为新对象定义额外属性的对象(可选) 。例如:
function SuperType(){
this.name = "lilei";
}
var superType = new SuperType();
var subType = Object.create(superType,{
age: {
value: "12"
}
});
console.log(superType.name) // "lilei"
console.log(subType.name) // "lilei"
额外属性都会覆盖原型对象上的同名属性
function SuperType(){
this.name = "lilei";
}
var superType = new SuperType();
var subType = Object.create(superType,{
name: {
value: "haimeimei"
}
});
console.log(superType.name) // "lilei"
console.log(subType.name) // "haimeimei"
优点:
直接将父类对象生成新的对象传递给子类
缺点:
引用类型值的属性始终都会共享相应的值,和使用原型模式一样
五、寄生式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function createObject(o){
var clone=object(o);
clone.sayHello = function(){
console.log("hello");
};
return clone;
}
function SuperType(){
this.name = "lilei";
}
var superType = new SuperType();
var subType = createObject(superType);
subType.sayHello(); //"hello"
优点:不需要创建自定义类型
缺点:无法实现函数的复用
六、寄生组合式继承
组合继承的缺点是会调用两次父类型构造函数,子类原型上有父类的实例属性。因此我们只要给子类原型赋父类原型的属性。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
函数内部:
- 将父类原型地址赋值给变量prototype。
- 重写原型会失去的默认的 constructor 属性,所以添加一个constructor属性。
- 把prototype赋值给子类型的原型。
举个栗子:
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
}
SuperType.prototype.country="China";
function SubType(name){
SuperType.call(this,name);
}
inheritPrototype(SubType, SuperType);
SubType.prototype.country="America";
var superType = new SuperType("lilei");
var subType = new SubType("tom");
console.log(superType.name) //"lilei"
console.log(superType.country) //"China"
console.log(subType.name) //"tom"
console.log(subType.country) // "America"
优点:解决了子类原型上有父类的实例属性问题,是引用类型最理想的继承范式
缺点:过程比较复杂
面向对象编程到此就完结啦,希望对大家有帮助!
文章参考:
《JavaScript 高级程序设计》中文译本 第三版