ES5 有 6 种方式可以实现继承,分别为
1.原型链继承
父类的实例作为子类的原型
原型链继承的基本思想是:
利用原型让一个引用类型继承另一个引用类型的属性和方法。
//原型链继承
function Fu() {
this.name = "张三";
this.age = 2;
}
Fu.prototype.child = function() {
return this.name;
};
let a = new Fu();
console.log(a.child());//张三
优点:
- 简单易于实现,父类的新增的实例与属性子类都能访问
缺点:
- 可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
- 无法实现多继承
- 创建子类实例时,不能向父类构造函数中传参数
2、借用构造函数继承(伪造对象、经典继承)
复制父类的实例属性给子类
借用构造函数的技术,基本思想为:
在子类型的构造函数中调用超类型构造函数。
//构造函数继承
function Fu(name){
this.name = name;
}
function Student(name){
Fu.call(this,name);
}
let a = new Student("海绵宝宝");
console.log(a.name);//海绵宝宝
优点:
- 可以向超类型的构造函数传递参数
- 解决了原型中包含 引用类型值被所有实例共享的问题
缺点:
- 方法都在构造函数中定义,无法复用
- 不能继承原型属性/方法,只能继承父类的实例属性和方法
3.实例继承(原型式继承)
原型继承的基本思想是:
借用原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
// 原型式继承
var person = {
name:"大头儿子",
age:2
}
var person1 = Object.create(person);
person1.name = "小头爸爸";
person1.age = 26;
var person2 = Object.create(person);
person2.name = "围裙妈妈";
person2.age = 23;
console.log(person1);// {name: "小头爸爸", age: 26}
console.log(person2);// {name: "围裙妈妈", age: 23}
优点:
- 不限制调用方法,简单,易实现
缺点:
- 不能多次继承
4.组合继承(原型链 + 借用构造函数)
( 组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式。)
调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
基本思路:
使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对每个实例属性的继承,既通过了在原型上定义方法来实现了函数复用,有保证了每个实例都有自己的属性。
//组合继承(原型链和借用构造函数)
function Fu(name){
this.name = name;
}
Fu.prototype.sayHi = function (){
console.log(this.name)
}
function Student(name){
Fu.call(this,name)
}
let a = new Student("派大星");
console.log(a.name);//派大星
5、寄生式继承
( 寄生式继承是与原型式继承紧密相关的一种思路。 )
寄生式继承的思路与寄生构造函数和工厂模式类似:
即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了搜有的工作一样返回对象。
// 寄生式继承
function createAnother(original){
var clone = Object(original);
clone.sayHi = function (){
console.log("hi");
};
return clone;
}
var person = {
name:'张三',
age:2
}
var person2 = createAnother(person);
person2.sayHi();//hi
缺点:
- 使用寄生继承来为对象添加函数,会由于不能做到函数复用而效率低下。
- 同原型链实现继承一样,包含引用类型值的属性会被所有实例共享
6.寄生组合继承
通过寄生的方式来修复组合是继承的不足,完美的实现继承
所谓寄生组合式继承,即通过构造函数来继承属性,通过原型链的混成形式来继承方法。
基本思路:
不必为了指定子类型的原理而调用超类型的构造函数,我们需要的仅仅是超类型原型的一个副本,本质上就是使用寄生式继承来继承超类型的原型,然后,再将结果指定给子类型的原型。
// 寄生组合式继承(构造函数和原型的结合)最理想的继承方案
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype);//创建对象
prototype.constructor = subType;//增强对象
subType.prototype = prototype;//指定对象
}
function SuperType(name){
this.name = name;
}
SuperType.prototype.sayHi = function (){
console.log(this.name);
}
function SuberType(name){
SuperType.call(this,name);
}
inheritPrototype(SuberType,SuperType);
var a = new SuperType("张三");
a.sayHi();//张三
优点:
- 只调用了一次超类构造函数,效率更高
ES6还新增一种继承方式
//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
constructor(name='wang',age='27'){
this.name = name;
this.age = age;
}
eat(){
console.log(`${
this.name} ${
this.age} eat food`)
}
}
//继承父类
class Woman extends People{
constructor(name = 'ren',age = '27'){
//继承父类属性
super(name, age);
}
eat(){
//继承父类方法
super.eat()
}
}
let wonmanObj=new Woman('xiaoxiami');
wonmanObj.eat();
优点:
- 代码量少,易懂
ES5继承和ES6继承的区别:
es5继承首先是在子类中创建自己的this
指向,最后将方法添加到this
中
Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)
es6继承是使用关键字先创建父类的实例对象this,最后在子类class
中修改this