
JS 中如何实现类的继承
准确地说,JS 中没有类的概念,有的只是构造函数和原型链。不过,我们可以通过构造函数和原型链来实现类似于类的继承,并且可以玩出很多花样、实现不同的特性。
准确地说,JS 中没有 类 的概念,有的只是 构造函数 和 原型链。不过,我们可以通过构造函数和原型链来实现类似于类的继承,并且可以玩出很多花样、实现不同的特性。
原型链继承
直接把要继承的构造函数的 prototype
指向一个实例化的父类对象即可。
function Animal(name) {
this.name = name
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`)
}
function Dog(name, breed) {
this.breed = breed
}
Dog.prototype = new Animal() // 实现继承
const dog = new Dog('Rex', 'Bulldog')
dog.speak() // Output: Rex makes a noise.
优点:简单易懂,符合原型链的基本思想
缺点:
- 如果弗雷包含引用类型的属性(如数组),子类实例对这个属性的修改影响其他子类实例, 因为是以引用的方式继承的 。
- 在创建子类实例时,不能向父类构造函数传递参数。
构造函数继承
直接调用父类构造函数的 Function.prototype.call
方法,指定 this
和参数即可。
function Animal(name) {
this.name = name
}
function Dog(name, breed) {
Animal.call(this, name)
this.breed = breed
}
const dog = new Dog('Rex', 'Bulldog')
优点:
- 避免了引用类型的属性共享问题,传入的值直接在这个实例中单独初始化。
- 可以在创建子类实例时向父类传递参数。
缺点:
- 方法都在构造函数中定义,无法直接的实现函数复用,每个实例都有自己独立的方法副本。
组合继承(原型链继承 + 构造函数继承)
在子类构造函数内部先用 call
绑定 this
,然后再指定子类构造函数的 prototype
为父类的实例,最后把 prototype.constructor
指向自己。
function Animal(name) {
this.name = name
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`)
}
function Dog(name, breed) {
Animal.call(this, name)
this.breed = breed
}
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
const dog = new Dog('Rex', 'Bulldog')
优点:结合了原型链继承和构造函数继承的优点,避免了它们各自的缺点。
缺点:调用了 两次 父类构造函数,导致子类原型上 多了不需要的父类属性 。
原型式继承
使用 Object.create
方法来创建一个子类对象。
const animal = {
type: 'animal',
speak() {
console.log(`${this.name} makes a noise.`)
},
}
const dog = Object.create(animal)
dog.name = 'Rex'
优点:不需要定义构造函数,只需要指定要继承的对象,就可以创建对象。
缺点:和原型链继承一样,引用类型的属性会被共享。
寄生式继承
提供了一个返回子类实例的函数,在其内部调用 Object.create
方法创建子类实例,并为其指定相关属性,之后将其返回。
function createDog(name) {
const dog = Object.create(animal)
dog.name = name
return dog
}
优点:可以在不必为新对象构建自定义类型的情况下,实现对象的属性继承。
缺点:和原型链继承一样,引用类型的属性会被共享。
寄生组合式继承
引入一个函数 inheritPrototype
,传入子类构造函数和父类构造函数,将两者的 prototype
绑定到一起(类似于 new
操作符)。
其余的部分和构造函数继承类似。
function inheritPrototype(subType, superType) {
const prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
function Animal(name) {
this.name = name
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`)
}
function Dog(name, breed) {
Animal.call(this, name)
this.breed = breed
}
inheritPrototype(Dog, Animal)
const dog = new Dog('Rex', 'Bulldog')
dog.speak()
优点:解决了组合继承中调用两次父类构造函数的问题,避免了不必要的属性初始化。
缺点:相对于其他方式,寄生组合式继承是比较完善的一种继承方式,但是在方法实现上相对比较复杂。