一、原型链
function Parent1(name){
this.name=name;
this.role='father';
this.play=[1,2,3];
}
function Child1(name){
this.name=name;
this.role='child';
}
Child1.prototype=new Parent1();//继承Parent1
var s1=new Child1("Nike")
var s2=new Child1("Mary")
s1.play.push(4);
s1.play //[1,2,3,4]
s2.play //[1,2,3,4],s2的play属性也发生了改变
s1 instanceof Child1 //true
s1 instanceof Parent1 //true,s1是Child1和Parent1的实例
s1.constructor===Child1 //false,可见无法找到自己的直接原型
s1.constructor===Parent1 //true
缺点:
1、包含引用类型的原型属性会被所有实例共享(这也是为什么要在构造函数中,而不是原型对象中定义属性的原因。
2、实例无法正确判断谁是自己的直接原型。
二、借用构造函数
function Parent2(name){
this.name=name;
this.friends=['red','blue']
}
Parent2.prototype.sayName=function(){
console.log(this.name)
}
function Child2(name){
Parent2.call(this)//"借调"了超类的构造函数
// Parent2.call(this,'Nike')//还可以继承的同时,给子类传参,这是借用构造函数的一个优势
}
var s3=new Child2("Nike")
var s4=new Child2("Mary")
s3.friends.push('green')
s3.friends //['red','blue','green']
s4.friends //['red','blue'],每个实例都可以拥有自己独有的引用类型。解决了方法一存在的问题
s3 instanceof Child2 //true
s3 instanceof Parent2 //false,s3是Child2和Parent2的实例,但是这里没有检测出是Parent2的实例
s3.constructor===Child2 //true,可见可以找到自己的直接原型
s3.constructor===Parent2 //false
s3.sayName();//Uncaught TypeError: s3.sayName is not a function,可见借用构造函数的方法不能继承父类原型链上的方法。
缺点:缺点:解决了引用类型被共享的问题,也解决了原型链方法找不到直接父类的问题,但是该方法的子类不能继承父类的原型链(即prototype上的方法),并且方法都在构造函数中定义,那么函数定义就无从谈起了。
三、组合继承
function Parent3(name){
this.name=name;
this.role='father';
this.play=[1,2,3];
}
function Child3(name){
Parent3.call(this)//第二次调用Parent3
this.name=name;
this.role='child';
}
Child1.prototype=new Parent1();//继承Parent3,第一次调用Parent3
var s5=new Child3("Nike")
var s6=new Child3("Mary")
s5.play.push(4);
s5.play //[1,2,3,4]
s6.play //[1,2,3],每个实例都可以拥有自己独有的引用类型。
s5 instanceof Child3 //true
s5 instanceof Parent3 //false,s5是Child3和Parent3的实例,但是这里没有检测出是Parent3的实例
s5.constructor===Child3 //true,可见找到自己的直接原型
s5.constructor===Parent3 //false
缺点:无论在什么情况下都会调用两次超类型构造函数,一次是在创建子类原型的时候,另一次是在子类构造函数的内部。
四、组合继承优化
function Parent4(name){
this.name=name;
this.play=[1,2,3]
}
function Child4(name){
Parent4.call(this)//"借调"了超类的构造函数
}
Child4.prototype=Parent4.prototype;//引用通一个对象
var s7=new Child4("Nike")
var s8=new Child4("Mary")
s7.play.push(4);
s7.play //[1,2,3,4]
s8.play //[1,2,3],每个实例都可以拥有自己独有的引用类型。
s7 instanceof Child4 //true
s7 instanceof Parent4 //true,检测出是Parent4和Child4的实例
s7.constructor===Child4 //false,可见不能找到自己的直接原型
s7.constructor===Parent4 //true
缺点:解决了方法三调用两次父类构造函数的问题,但是该方法又没有办法找到自己的直接父类。
五、寄生组合式继承
function Parent5(name){
this.name=name;
this.role='father';
this.play=[1,2,3];
}
function Child5(name){
Parent5.call(this);
this.name=name;
this.role='child';
}
Child5.prototype=Object.create(Parent5.prototype);//隔离①
// Child5.prototype=Parent5.prototype;
Child5.prototype.constructor=Child5;//覆盖②
var s9=new Child5("Nike")
var s10=new Child5("Mary")
s9.play.push(4);
s9.play //[1,2,3,4]
s10.play //[1,2,3],每个实例都可以拥有自己独有的引用类型。
s9 instanceof Child5 //true
s9 instanceof Parent5 //true,检测出是Parent5和Child5的实例
s9.constructor===Child5 //true,可见可以找到自己的直接原型
s9.constructor===Parent5 //false
注:
1、Object.create()是将对象继承到__proto__上;
2、Object.create()方法接收两个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()和object()方法的行为相同。
①,②解决了找不到直接原型的问题,该方法的每一步都可以得到正确答案。