目录
4、组合继承(原型链继承+借用构造函数继承):可传参、继承原型
首先定义一个父类Animal:
//构造函数
function Animal(name) {
this.name = name || 'Animal';
this.sleep = function() {
console.log(this.name + '正在睡觉!');
};
}
//原型上面的方法:
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
}
1、原型链继承:将父类的实例作为子类的原型
关键语句:Dog.prototype = new Animal();
显著缺点:无法继承多个;
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问的到
- 简单
缺点:
- 无法实现继承多个;
- 来自原型对象的所有属性被所有实例共享;
- 创建子类实例时,无法向父类构造函数传参(在子类中可通过this.name赋值,子类实例调用父类方法时,也可访问到);
- 为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中。例:Dog.prototype.name = 'dog';
2、借用构造函数继承:复制父类的实例属性给子类
核心:使用父类的构造函数增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
关键语句:Animal.call(this)
显著缺点:只能继承父类的实例属性和方法,不能继承父类原型属性/方法。故只是子类的实例,不是父类的实例;
特点:
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、实例继承:为父类实例添加新特性,作为子类实例返回
关键语句:var instance = new Animal(); return instance;
显著缺点:是父类的实例(因为通过new调用了父类),不是子类的实例
特点:
- 不限制调用方式,不管是new子类(),还是子类(),返回的对象都具有相同的效果
缺点:
- 实例是父类的实例,不是子类的实例
- 不支持多继承
4、组合继承(原型链继承+借用构造函数继承):可传参、继承原型
核心:通过调用父类构造函数,继承父类的属性并可传参,然后将父类实例作为子类原型,继承原型属性/方法。
关键语句:Animal.call(this); //构造函数
Cat.prototype = new Animal(); //原型链继承
显著缺点:会调用两次父类的构造函数
特点:
- 即可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:
- 调用了俩次父类的构造函数,生成了俩份实例(子类实例将子类原型上的那份屏蔽了)
5、es6的extends
注意:
- 子类必须在constructor方法中调用super方法。如果子类没有定义constructor方法,会被默认添加constructor方法以及其内部的super方法。
- 在子类的constructor方法中,只有调用super之后,才可以使用this关键字,否则会报错。
super
内部的this
指的是子类的实例- super作为对象时,在子类中指向父类的原型对象。即super=Parent.prototype。
6、寄生组合式继承
子类的原型 child.prototype 指向父类的原型 father.prototype;
子类原型的构造函数 child.prototype.constructor 指向子类本身 child;
关键语句:
inheritPrototype方法设置子类的原型,使其指向父类的原型,并且构造函数指向子类本身;
father.apply(this,[]) //父类构造函数
特点:
- 寄生组合式继承的高效体现在它只调用了一次Person构造函数
- 并且因此避免了在Pan.prototype上面创建不必要的、多余属性
- 原型链还能保持不变
- 还能正常使用instanceof和isPropertyOf()
- 开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
//1. 实现子类继承父类(通用方法)。child类继承father类 。子类的原型指向父类的原型,子类的构造函数指向其本身
function inheritPrototype(child,father){
var prototype = Object.create(father.prototype); // 1)子类原型 指向父类的原型
prototype.constructor = child; // 2) 子类原型的构造函数指向child类。
child.prototype = prototype; // 3)设置子类的原型为prototy。
}
//2. 创建父类
function father(name){
this.faName = 'father';
}
father.prototype.getfaName = function(){
console.log(this.faName);
}
//3. 创建子类
function child(args){
this.chName = 'child';
father.apply(this,[]); //借用父类的构造函数,可访问父类本身的方法和属性
}
// 继承父类
inheritPrototype(child,father);
// 子类原型上添加方法
child.prototype.getchName = function(){
console.log(this.chName);
}
//4. 打印测试
let c = new child();
console.log(c.faName); //father
c.getfaName(); //father
console.log(child.prototype.isPrototypeOf(c)); //true。child的实例对象的原型是否是child.prototype
console.log(c instanceof child) //true
console.log(c instanceof father) //true