继承

76 阅读3分钟

1. 原型链继承

  • 核心
    • 子类的原型直接指向父类实例
  • 优点
    • 代码简单
  • 缺点
    • 多子类实例,父类型属性会共享
    • 子类实例无法传递参数
function Parent() { this.color = ['red'] }
function Child() {}

// 核心方法
Child.prototype = new Parent()

// 实例使用
const child1 = new Child()
const child2 = new Child()
child1.color.push('blue')

// 子类属性不独立,属于引用类型,相互影响
console.log(child1.color) // ['red', 'blue']
console.log(child2.color) // ['red', 'blue']

2. 构造函数

  • 核心
    • 子类构造函数调用父类构造函数
  • 优点
    • 可以传递参数
    • 子类实例属性独立,不相互影响
  • 缺点
    • 只能继承构造函数中的属性和方法,无法继承原型上的属性和方法
function Parent(name) { 
    this.name = name
    this.color = ['red']
}

// 父类原型上的属性和方法
Parent.prototype.age = 12
Parent.prototype.sayName = function () {
    console.log(this.name)
}

function Child(name) {
    // 核心方法 调用父类构造函数
    Parent.call(this, name)
    // Parent.apply(this, [...name]) 
    // Parent.bind(this, name)()
}

const child1 = new Child('hh')
const child2 = new Child('xx')
child1.color.push('blue')

// 只能继承父类构造函数中的属性和方法
console.log(child1.name, child1.color) // hh ['red', 'blue']
// 子类属性独立
console.log(child2.name, child2.color) // xx ['red']

// 无法继承父类原型上的属性和方法
console.log(child1.sayName) // undefined
console.log(child1.age) // undefined

3. 组合继承

  • 核心
    • 原型链继承+构造函数继承
  • 优点
    • 父类构造函数原型上的方法和属性都可以继承
    • 子类实例属性和方法独立,不相互影响
  • 缺点
    • 父类构造函数会调用两次,一次在子类构造函数中,一次在子类原型赋值时
function Parent(name) {
    this.name = name
    this.color = ['red']
}

Parent.prototype.age = 12
Parent.prototype.sayName = function () {
    console.log(this.name)
}

function Child(name) {
    // 第一次调用父类构造函数
    Parent.call(this, name)
}

// 核心方法 子类原型指向父类实例
Child.prototype = new Parent() // // 第二次调用父类构造函数
Child.prototype.constructor = Child

const child1 = new Child('hh')
const child2 = new Child('xx')
child1.color.push('blue')

// 可以继承父类构造函数中的属性和方法
console.log(child1.name, child1.color) // hh ['red', 'blue']
// 子类属性独立
console.log(child2.name, child2.color) // xx ['red']

// 可以继承父类原型上的属性和方法
console.log(child1.sayName()) // hh
console.log(child1.age) // 12

4. 寄生组合继承(最优解

  • 优点
    • 避免父类构造函数调用两次
    • 父类构造函数原型上的方法和属性都可以继承
    • 子类实例属性和方法独立,不相互影响
function Parent(name) {
    this.name = name
    this.color = ['red']
}

Parent.prototype.age = 12
Parent.prototype.sayName = function () {
    console.log(this.name)
}

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

// 核心方法
function inheritPrototype(child, parent) {
    // 利用Object.create基于现有对象创建一个新对象
    const newConstructor = Object.create(parent.prototype)
    // 子类原型指向新创建出来的对象
    child.prototype = newConstructor
    child.prototype.constructor = child
}

inheritPrototype(Child, Parent)

const child1 = new Child('hh')
const child2 = new Child('xx')
child1.color.push('blue')

// 可以继承父类构造函数中的属性和方法
console.log(child1.name, child1.color) // hh ['red', 'blue']
// 子类属性独立
console.log(child2.name, child2.color) // xx ['red']

// 可以继承父类原型上的属性和方法
console.log(child1.sayName()) // hh
console.log(child1.age) // 12

5. ES6 Class继承 (优先使用)

  • 核心
    • es6中的 classextends 关键词
  • 优点
    • 语法简洁
  • 缺点
    • 有一定兼容性问题,在不支持ES6语法的环境中需要转换
class Parent {
    age = 12
    constructor (name) {
        this.name = name
        this.color = ['red']
    }
    sayName() {
        console.log(this.name)
    }
}

class Child extends Parent {
    constructor(name) {
        super(name)
    }
}

const child1 = new Child('hh')
const child2 = new Child('xx')
child1.color.push('blue')

// 可以继承父类 构造函数 中的属性和方法
console.log(child1.name, child1.color) // hh ['red', 'blue']
// 子类属性独立
console.log(child2.name, child2.color) // xx ['red']

// 可以继承父类 原型上 的属性和方法
console.log(child1.sayName()) // hh
console.log(child1.age) // 12