继承,面向对象三大特性之一,在讨论面向对象编程就离不开继承。继承在js编程中对函数复用性起到了举足轻重的地位。尤其在class类中等面向对象编程的领域中,继承这一特性大大节省了开发者在开发中的时间。 首先来讲js继承的实现方式:
原型链继承
说到原型链,原型链可以理解为以下:
原型链可以理解为由__proto__组成的原型关系,以上图片红色线条就代表原型链,以上也解释了为什么
- Object instanceof Function === true
- Function instanceof Object === true
- Function.[[prototype]] === Function.prototype
原型链继承代码
function SuperType() {
this.parent = true;
this.parentFunction = function() {
return this.parent;
}
}
//原型链继承
function childType() {}
childType.prototype = new SuperType();
let result = new childType();
console.log(result.parentFunction()); //true
childType通过prototype继承了SuperType的属性和方法。 确定是否为原型链方法一般由两种形式。
- 第一种是通过instanceof判断原型链上是否有对应对象
- 第二种是通过调用对象的prototype.isPrototypeof(实例对象)
两种方法都会返回布尔值
缺点
- 父类的引用实例会在子类实例中共享
- 子类无法向父类方法传参
盗用构造函数继承(经典继承)
思路:在子类构造函数调用父类构造函数
实现:
function SuperType(name) {
this.name = name;
}
function childType() {
//实现继承并传递参数
SuperType.call(this, "Bob");
}
let result = new childType();
console.log(result.name); //Bob
优点:
- 可以向父类属性传参
缺点:
- 无法继承父类方法
- 构造函数难以重复使用
组合继承(伪经典继承)
思路:通过原型链继承方法,通过构造函数继承实例属性
实现:
function SuperType(name) {
this.name = name;
}
SuperType.prototype.printName = function() {
console.log(this.name);
}
function childType() {
// 继承属性
SuperType.call(this, "Bob");
}
//继承方法
childType.prototype = new SuperType();
let result = new childType();
console.log(result.name); //Bob
result.printName(); //Bob
优点
- 避免共享父类实例的同时继承了父类方法
缺点:
- 调用了两次父类函数
原型式继承
思路:本质上是对原始对象的克隆,通过创建一个新对象实现继承。主要通过object.create或者通过改变原型而创建新对象的函数来实现。
实现:
let person = {
name: "Luce",
}
function object(newObject) {
function F() {};
F.prototype = newObject;
return F;
}
// 实现继承
let result = object(person);
// 以下效果和上面相同
//let result = Object.create(person);
result.name = "Bob";
console.log(result.name); //Bob
实际相当于对象的浅拷贝,通过函数的原型绑定对象
缺点
- 父类的引用实例会在子类实例中共享
- 子类无法向父类方法传参
寄生式继承
思路:通过内部创建一个新对象,并增强(添加新方法或者属性)新对象,然后返回这个新对象
实现:
let person = {
name: "Bob",
}
function createObject(object) {
let newObject = Object.create(object);
// 寄生
newObject.printName = function() {
console.log(this.name);
};
return newObject;
}
let result = createObject(person);
result.printName(); //Bob
缺点:
- 构造函数难以重复使用
寄生式组合继承
思路:使用寄生式继承的形式将父类原型以新对象再次创建,然后将新对象赋值给子对象
实现:
function changeChildType(childType, superType) {
// 获取父类方法,并将父类方法通过原型链赋值给子类
let newObject = Object.create(superType.prototype);
newObject.constructor = childType; //解决由于重写原型导致childType的constructor丢失
childType.prototype = newObject;
}
function SuperType(name) {
this.name = name;
}
SuperType.prototype.printName = function() {
console.log(this.name);
}
function childType() {
// 继承属性
SuperType.call(this, "Bob");
}
changeChildType(childType, SuperType);
let result = new childType();
console.log(result.name); //Bob
result.printName(); //Bob
优点:
- 只调用一次父类方法
- 不改变原型链
Class继承(类继承)
思路:es6类支持单继承,使用extends,就可以继承任何拥有[[Construct]]和原型的对象。既可以继承类,也可以继承普通的构造函数。
实现:类继承实现原理也是使用的原型链,通过构造函数的形式,创建类似class类的语法
class ParentClass {}
// 继承类
class Bus extends ParentClass {}
function ParentFunction() {}
// 继承普通构造函数
class Child extends ParentFunction {}
class的super
- 在执行后会调用父类函数,并将实例返回给this
- 只能在派生类构造函数和静态方法使用
- 不能单独引用,要么用它调用构造函数,要么用它引用静态方法
- super()如同调用构造函数,如果需要给父类构造函数传参,需要手动传入