深入理解JavaScript中的继承

378 阅读2分钟

原型链继承

function Parent () {
    this.name = 'hello world'
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child () {}
Child.prototype = new Parent()
const child = new Child()
console.log(child.getName()) // hello world 

原型链继承会有一下问题:

  • 引用类型的属性被所有实例共享
  • 在创建Child的实例的时候,不能向Parent传参
function Parent () {
    this.name = ['Jack', 'Tom']
}

function Child () {}
Child.prototype = new Parent()
const child = new Child()
child.name.push('haha')
const child2 = new Child()
console.log(child2.name) // ['Jack', 'Tom', 'haha']

借用构造函数

function Parent (name) {
    this.name = name
}

function Child (name, age) {
    Parent.call(this, name)
    this.age = age
}

const child1 = new Child('xiao ming', 25)
const child2 = new Child('xiao hong', 28)
console.log(child1.age) // 25
console.log(child1.name) // xiao ming
console.log(child2.age) // 28
console.log(child2.name) // xiao hong

借用构造函数优点:

  • 避免了引用属性被所有实例所共享
  • 可以在子类可以拥有自己的属性 缺点:
  • 不能继承父类原型上的属性和方法

组合继承

既然原型链继承和借用构造函数继承都有各自的缺点,那能不能把两者结合起来,答案是肯定的

function Parent (name) {
    this.name = name
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child (name, age) {
    Parent.call(this, name)
    this.age = age
}

Child.prototype.getInfo = function () {
    console.log('my name is' + this.name + 'i am' + this.age + 'old')
}

Child.prototype = new Parent()

const child1 = new Child('Tom', 21)
const child2 = new Child('Mary', 19)
console.log(child1.getInfo())
console.log(child1.getInfo())

原型式继承

function create (o) {
    function F () {}
    F.prototype = o
    return new F()
}

以上就是对ES5中Object.create()的模拟实现,将传入的对象作为创建对象的原型,这个缺点跟原型链是一样的,引用类型的属性始终都会被所有子类所共享

寄生式继承

寄生式继承就是创建一个近用于封装继承过程的函数,该函数在内部以某种方式增强对象,最后再将这个对象返回

function createPlus (o) {
    const clone = Object.create(o)
    clone.sayName = function () {
        console.log('hi')
    }
    return clone
}

const person = {
    name: 'Tom'
}

const p1 = createPlus(person)
console.log(p1.sayName())

缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

寄生组合式继承

function Parent (name) {
    this.name = name
    this.colors = ['red', 'green', 'yellow']
}
function Child (name, age) {
    Parent.call(this, name)
    this.age = age
}

// Object.create()的模拟实现
function F () {}
F.prototype = Parent.prototype
Child.prototype = new F()

const child = new Child('Jack', 18)

最后我们封装一下这个继承方法

function Parent (name) {
    this.name = name
    this.colors = ['red', 'green', 'yellow']
}

function Child (name, age) {
    Parent.call(this, name)
    this.age = age
}

function inherit (Parent, Child) {
    const prototype = Object.create(Parent.prototype)
    prototype.constructor = Child
    Child.prototype = prototype
}

inherit(Parent, Child)

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式