7种方法实现ES5中的继承
1、借用构造函数继承父类属性
步骤
1)在子类的构造函数中执行父类的构造函数,并为其绑定子类的this。
2)通过call()
函数,改变this指针的指向,让父类的构造函数把成员属性和方法都挂到子类的this上去。
实现示例
function Father(name,age){// Father构造函数是父类
this.name = name;
this.age = age;
this.say = function(){
console.log('好好学习');
}
}
//构造函数继承的方式,子类继承不到父类原型上的属性和方法
Father.prototype.getName=function(){console.log('原型上的方法')}
function Son(name,age,sourse){// Son构造函数是子类
// 执行父类构造方法并绑定子类的this, 使得父类中的属性能够赋到子类的this上
Father.call(this,name,age);// 子类继承父类的属性
this.sourse = sourse;// 子类可以拥有自己的特有属性
}
var f1 = new Father('张三',45);
var s1 = new Son('李四',18,60);
console.log(f1);
console.log(s1);
f1.getName();
s1.say();
// s1.getName();//报错,原因是继承不到父类的原型上的属性。
优缺点
优点:避免实例之间共享一个原型实例,能向父类构造方法传参。
缺点:继承不到父类原型上的属性和方法。
2、利用原型对象继承父类方法
步骤
1)直接让子类的原型对象指向父类实例。当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承。
实现示例
function Father(){
this.name = '张三'
}
// 父类的原型方法
Father.prototype.getName = function(){
console.log(this.name)
};
function Son(){};
//让子类的原型对象指向父类实例
Son.prototype = new Father();
//根据原型链的规则,绑定一下constructor, 这一步不影响继承, 只是在用到constructor时会需要
Son.prototype.constructor = Son;
//子类的方法
Son.prototype.sourse= function(){console.log(60)}
var s1 = new Son();
console.log(s1.__proto__);
console.log(s1)
s1.getName();
s1.sourse();
优缺点
优点:通过原型链继承的方式,原先存在父类型的实例中的所有属性和方法,现在也能存在于子类型的原型中了。
缺点:
1)由于所有子类实例原型都指向同一个父类实例, 因此对某个子类实例的父类引用类型变量修改会影响所有的子类实例。
2)在创建子类实例时无法向父类构造传参, 即没有实现super()
的功能。
3、组合继承
步骤
1)组合继承是综合上面两种方法实现继承的一种方式。
实现示例
function Father(name,age){
this.name = name;
this.age = age;
}
Father.prototype.say = function(){
console.log('hello');
}
function Son(name,age,sourse){
Father.call(this,name,age);
this.sourse = sourse;
}
Son.prototype = new Father();
Son.prototype.said = function(){
console.log('hi');
}
var s1 = new Son('李四',18,60);
console.log(Son.prototype);
console.log(s1.__proto__);
console.log(s1);
s1.say();
s1.said();
优缺点
缺点:每次创建子类实例都执行了两次构造函数(Father.call()和new Father())。
优点:组合继承拥有上面两种方法的优点。同时还能避免上面两种方法的缺点。
4、寄生式继承
步骤
1)基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。
2)实例化一个临时副本实现相同的原型链继承。
实现示例
function object(o){
function Father(){};
Father.prototype = o;
return new Father;
}
function Son(o){
var cons = object(o);
cons.say = function(){
console.log('hello');
}
return cons;
}
var Person = {
name:'张三',
age:18
}
var s1 = Son(Person);
console.log(s1);
console.log(s1.name);
console.log(s1.age);
s1.say();
优缺点
优点:解决了组合继承中每次创建子类实例都执行了两次构造函数
缺点:
1)原型继承存在的缺点他都存在。
2)使用寄生式继承为对象添加方法,会由于不能做到方法的复用而降低效率,这一点和构造函数模式类似。
5、寄生式组合继承
定义:所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
基本思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的是超类型原型的一个副本。
步骤
1)使用寄生式继承来继承超类型的原型。
2)将结果指定给子类型的原型。
实现示例
function Father(name){
this.name = name;
}
Father.prototype.getName=function(){
console.log(this.name);
}
function inherit(son,father){
function F(){};
F.prototype = father.prototype;
var prototype = new F;
prototype.constructor = son;
//将子类的原型指向父类原型的一个副本
son.prototype = prototype;
}
function Son(name,age){
Father.call(this,name);
this.age = age;
}
//将子类Son的原型指向父类原型的一个副本
//注意:要执行该动作后才能在Son的prototype上定义方法,否则没用
inherit(Son,Father);
Son.prototype.getAge=function(){
console.log(this.age);
}
var s1 =new Son('张三',18);
console.log(s1);
s1.getName();
s1.getAge();
优缺点
优点:
1)集寄生式继承和组合式继承的优点与一身,实现基本类型继承的最有效方法。
2)只调用了一次父类(father),并且避免了(子类原型)son.prototype上面创建多余大的不必要属性,同时保持原型链不变。
6、直接继承prototype
步骤
1)我们在组合继承的基础上将指向父类实例改为指向父类原型。
实现示例
function Father(name,age){// Father构造函数是父类
this.name = name;
this.age = age;
}
Father.prototype.getName=function(){console.log(this.name)}
function Son(name,age,sourse){
Father.call(this,name,age);
this.sourse = sourse;
}
Son.prototype = Father.prototype;
Son.prototype.constrcutor = Son;
Son.prototype.getAge=function(){
console.log(this.age);
}
var f1 = new Father('张三',45);
var s1 = new Son('李四',18,60);
console.log(s1);
console.log(f1);
s1.getName();
s1.getAge();
f1.getAge();
优缺点
优点:效率比较高。
缺点 : 给子类添加方法时,父类也会增加一个和子类相同的方法,就是由于子类原型和父类原型指向
同一个对象,我们对子类原型的操作会影响到父类原型。
7、改进直接继承prototype
我们不希望子类的方法能对父类产生影响,应该怎么做呢?
步骤
我们可以给父类的原型(Parent.prototype
)做一个浅拷贝。
实现示例
function Father(name,age){// Father构造函数是父类
this.name = name;
this.age = age;
}
Father.prototype.getName=function(){console.log(this.name)}
function Son(name,age,sourse){
Father.call(this,name,age);
this.sourse = sourse;
}
Son.prototype = Object.create(Father.prototype);
Son.prototype.constrcutor = Son;
Son.prototype.getAge=function(){
console.log(this.age);
}
var f1 = new Father('张三',45);
var s1 = new Son('李四',18,60);
console.log(s1);
console.log(f1);
s1.getName();
s1.getAge();
f1.getAge();
优缺点
优点:解决了子类影响父类的问题。