首先,先写一个父类 Animal
:
function Animal(name) {
this.name = name;
}
Animal.prototype.getName = function () {
console.log(this.name);
};
// 测试用函数
function test(Sup, Sub) {
const o = new Sub(2, "name");
console.log(o); // 输出实例
console.log(o instanceof Sub); // 是否是子类的实例?
console.log(o instanceof Sup); // 是否是父类的实例?
}
原型链继承
核心:父类实例作为子类原型
function Cat(age) {
this.age = age;
}
Cat.prototype = new Animal()
test(Animal, Cat);
缺点:
- 创建子类实例时,
无法向父类构造函数传参
构造函数继承
核心:父类构造函数在子类构造函数中执行一下
function Dog(age, name) {
Animal.call(this, name);
this.age = age;
}
test(Animal, Dog);
缺点:
- 只继承父类的
实例属性和方法
,没有继承原型属性和方法
- 实例并不是父类的实例,只是子类的实例
组合继承
核心:原型链继承与构造函数继承组合在一块
function Fish(age, name) {
Animal.call(this, name);
this.age = age;
}
Fish.prototype = new Animal();
Fish.prototype.constructor = Fish;
test(Animal, Fish);
缺点:
- 调用了两次父类函数,子类原型上多了一些重复属性和方法
寄生组合继承
核心:用父类prototype作为原型创建一个空对象,并设为子类原型
function Bird(age, name) {
Animal.call(this, name);
this.age = age;
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
test(Animal, Bird);
几乎没有缺点
拷贝继承
核心:通过for…in,把父类prototype上可枚举的属性和方法循环赋值到子类原型上
function Mouse(age, name) {
const o = new Animal(name);
for (const key in o) {
Mouse.prototype[key] = o[key];
}
this.age = age;
}
test(Animal, Mouse);
缺点:
- 利用循环,效率低
- 只能复制可枚举属性和方法
- 实例并不是父类的实例,只是子类的实例
extends继承
class Sup {
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name);
}
}
class Sub extends Sup {
constructor(age, name) {
super(name);
this.age = age;
}
getAge() {
console.log(this.age);
}
}
test(Sup, Sub)
目前最优雅的继承方式:继承了父类实例方法属性的同时也继承了原型方法属性