总结:这几种方法都有很明显的优缺点,但都是为了怎么更好的做到同时继承原型+原型链上的方法和属性。比如以下例子:可以继承Parent的方法和属性,但怎样一并继承Parent.prototype的属性和方法?
原型链继承
缺点:原型属性会被共享(如果一个实例改变了该属性,那么其他实例的属性会随之改变)
function Parent(){
this.arr = [1,2,3];
}
function Child(){}
Child.prototype = new Parent(); // 继承关键行
var child1 = new Child();
var child2 = new Child();
// 缺点
child1.arr.push(4)
console.log(child1.arr); // [1,2,3,4]
console.log(child2.arr); // [1,2,3,4]
构造函数继承
使用call或bind方法实现继承
优点:原型属性不会被共享
缺点:不会继承父类的prototype属性
function Parent(){
this.sayHello = function(){
console.log('Hello');
};
}
Parent.prototype.a = "我是父类的prototype属性"
function Child(){
Parent.call(this) //继承关键行
}
var child1 = new Child();
var child2 = new Child();
var parentObj = new Parent
// 优点
console.log(child1.sayHello === child2.sayHello); // false
// 缺点
console.log(parentObj.a); // "我是父类的prototype属性"
console.log(child1.a); // undefined
组合继承
就是(原型链继承+构造函数继承)
优点:1.原型属性不会被共享 2.可以继承父类的prototype属性
缺点:调用了2次父类的方法
function Parent(){
this.sayHello = function(){
console.log('Hello');
};
}
Parent.prototype.a = "我是父类的prototype属性"
function Child(){
Parent.call(this) //继承关键行1
}
// 缺点:Child.prototype本身可以继承Parent.sayHello 但这里赋值new Parent() 也就是调用了2次Parent()
Child.prototype = new Parent(); // 继承关键行2
var child1 = new Child();
// 优点
console.log(child1.a); // "我是父类的prototype属性"
寄生组合继承
Object.create() 创建一个空对象,并且指定这个空对象的prototype 是谁
优点:解决了上面三种方法的缺点
缺点:子类的prototype上的属性和方法会丢失
function Parent(){
this.sayHello = function(){
console.log('Hello');
};
}
Parent.prototype.a = "我是父类的prototype属性"
function Child(){
Parent.call(this) // 继承关键行1
}
Child.prototype.ChildFun = ()=>{
console.log("我是子类的方法")
}
// 优点 (创建一个没有实例的父类实例作为子类的原型)
Child.prototype = Object.create(Parent.prototype) // 继承关键行2
// 修复构造函数的指向
Child.prototype.constructor = Child // 继承关键行3
var child1 = new Child();
// 缺点
child1.ChildFun(); // child1.ChildFun不是一个函数
Class关键字继承:class继承
拓展
如果对原型和原型链不熟
// 创建原型
function Parent(name){
this.name= name;
}
// 原型上的方法
Parent.prototype.todo = ()=>{
console.log('做牛马')
} ;
// 创建新的原型对象 会共享原型上的属性和方法
var child1 = new Parent('小鹿');
console.log(child1.todo) // 做牛马 (child1 首先会在原型Parent里面找有没有todo 的方法,如果没有,就会去Parent.prototype上面找,直到找到原型链的顶端Object.prototype)