💎 Одна статья для понимания наследования JS

JavaScript

20190317203439.png

Когда я недавно рассматривал наследование js, я обнаружил, что некоторые концепции все еще не очень ясны. Вот несколько основных способов наследования в JS, наследование конструктора, наследование цепочки прототипов, составное наследование, прототипное наследование, паразитное наследование, паразитное комбинированное наследование и класс ES6:

Наследование конструктора

Наследование конструктора не использует прототип, этот метод более распространен, а его определение и использование относительно просты. Вот пример 🌰:

  • 👍 Вы можете определить методы частной собственности
  • 👍 Подклассы могут передавать параметры суперклассам
  • ❌ не может определить общие методы собственности / или писать снаружи и терять инкапсуляцию
function Parent(name, friends) {
    this.name = name
    this.friends = friends // 👍 可以定义私有 引用类型不会被共享
    this.share = share // ❌ 可以定义公有 但需要放在外部
    this.log = log // ❌ 避免重复声明,为了复用需要放在外面
}
// ❌ 公有属性和方法定义在外面失去了封装性
let share = [1, 2, 3]
function log() {
    return this.name
}

function Child(name, friends, gender) {
    Parent.call(this, name, friends) // 👍 可以在子类传递参数给父类
    this.gender = gender
}

20190317153141.png

Наследование цепочки прототипов

Режим цепочки прототипов требует ручного повторного связывания конструктора и не может определять частные переменные.

  • 👍 Может определять методы публичного свойства
  • ❌ Будь то определение или наследование, вам нужно вручную изменить конструктор
  • ❌ Общая инкапсуляция
  • ❌ Невозможно определить методы частной собственности
  • ❌ Нет возможности передать параметры родительскому классу
function Parent() {}
Parent.prototype = {
    constructor: Parent, // ❌ 需要手动绑定 constructor
    name: 'oli', // ❌ 不能定义私有属性,全部都是公有
    friends: ['alice', 'troy'], // 👍 可以定义公有属性 所有实例都引用这个
    log: function() { // 👍 方法被共享了
        return this.name
    }
}
// 也可以写成多个 Parent.prototype.func1 = function(){} 封装性更差 但不用修改 constructor
// ❌ 封装性一般

function Child() {} // ❌ 没办法向父类传递参数
Child.prototype = new Parent() // 使用 new 操作符创建并重写 prototype
Child.prototype.constructor = Child // ❌ 每次继承都需要手动修改 constructor 谁叫你是覆盖 prototype 属性呢

20190317164208.png

наследование композиции

Комбинация двух вышеперечисленных становится комбинированным шаблоном наследования.Это распространенный метод, который сочетает в себе преимущества двух до появления класса ES6.🦐🍜 Взгляните на пример:

  • 👍 Паблик написан в прототипе
  • 👍 приватная запись в конструкторе
  • 👍 Вы можете передавать параметры родительскому классу
  • ❌ Необходимо вручную связать конструктор
  • ❌ Общая инкапсуляция
  • ⚡ Неоднократно вызывая потерю производительности родительского класса
function Parent(name, friends) {
    // 😀 私有的写这里
    this.name = name // 👍 可以定义私有属性
    this.friends = friends // 👍 可以定义公有引用属性不会被共享
}
Parent.prototype = {
    // 😀 公有的写这里
    constructor: Parent, // ❌ 需要手动绑定 constructor
    share: [1, 2, 3], // 👍 这里定义的公有属性会被共享
    log: function() { // 👍 方法被共享了
        return this.name
    }
}
// ❌ 封装性一般

function Child(name, friends, gender) {
    Parent.call(this, name, friends) // 👍 可以向父类传递参数 ⚡ 这里又调用了一次 Parent
    this.gender = gender
}
Child.prototype = new Parent() // 使用 new 操作符创建并重写 prototype ⚡ 这里调用了一次 Parent
// 有方法避免多次调用直接去掉 new 操作符 转而写成 Child.prototype = Parent.prototype 这样并不好,虽然避免出现重复调用但导致修改子类 constructor 的时候父类也被修改了
Child.prototype.constructor = Child // ❌ 每次继承都需要手动修改 constructor 谁叫你是覆盖 prototype 属性呢
// 如果使用 Child.prototype = Parent.prototype 那么 constructor 子类父类是同一个

20190317171213.png

прототипное наследование

Прототипное наследование напрямую использует ES5.Object.createметод, принцип этого метода заключается в создании конструктора, прототип конструктора указывает на объект, затем вызывается оператор new для создания экземпляра и возвращается экземпляр, который по сути является поверхностной копией

  • 👍 Метод родительского класса можно использовать повторно
  • ❌ Все ссылочные свойства родительского класса являются общими
  • ❌ Подклассы не могут передавать параметры суперклассам
let parent = {
    name: 'parent',
    share: [1, 2, 3], // ❌ 父类的引用属性全部被子类所共享
    log: function() { // 👍 父类方法可以复用
        return this.name
    }
}

let child = Object.create(parent) // ❌ 子类不能向父类传递参数

20190317183345.png

паразитарное наследование

Добавляйте свойства и методы в подклассы на основе прототипного наследования.

  • 👍 Метод родительского класса можно использовать повторно
  • 👍 Добавлены другие свойства и методы
  • ❌ Все ссылочные свойства родительского класса являются общими
  • ❌ Подклассы не могут передавать параметры суперклассам
let parent = {
    name: 'parent',
    share: [1, 2, 3],
    log: function() {
        return this.name
    }
}

function create(obj) {
    let clone = Object.create(obj) // 本质上还是 Object.create
    clone.print = function() { // 增加一些属性或方法
        console.log(this.name)
    }
    return clone
}

let child = create(parent)

Паразитическая композиционная наследственность

Метод, сформированный путем смешивания цепочки прототипов, конструктора, композиции, прототипа и паразита:

Объединение унаследованных методов вызовет Parent дважды, один раз вChild.prototype = new Parent(), однажды вParent.call(). Это единственный недостаток наследования композиции, и паразитная композиция решает эту проблему:

  • 👍 Паблик написан в прототипе
  • 👍 приватная запись в конструкторе
  • 👍 Вы можете передавать параметры родительскому классу
  • 👍 не будет повторно вызывать родительский класс
  • ❌ Необходимо вручную связать конструктор (при переопределении прототипа)
  • ❌ Необходимо вызвать дополнительный общий метод инкапсуляции
function Parent(name, friends) {
    this.name = name
    this.friends = friends
}
Parent.prototype = {
    constructor: Parent, // ❌ 需要手动绑定 constructor
    share: [1, 2, 3],
    log: function() {
        return this.name
    }
}

function Child(name, friends, gender) {
    Parent.call(this, name, friends) // ⚡ 这里只需要调用一次 Parent
    this.gender = gender
}
// 上半部分和组合继承一样

let F = function() {} // 创建一个中介函数
F.prototype = Parent.prototype // 这个中介的原型指向 Parent 的原型
Child.prototype = new F() // 注意这里没有使用 new 操作符调用 Parent
Child.prototype.constructor = Child

Инкапсулируйте вышеуказанный метод:

function Parent(name, friends) {
    this.name = name // 👍 可以定义私有属性
    this.friends = friends // 👍 可以定义公有引用属性不会被共享
}
Parent.prototype = {
    constructor: Parent, // ❌ 需要手动绑定 constructor
    share: [1, 2, 3], // 👍 这里定义的公有属性会被共享
    log: function() { // 👍 方法被共享了
        return this.name
    }
}

function Child(name, friends, gender) {
    Parent.call(this, name, friends) // 👍 可以向父类传递参数 ⚡ 这里又调用了一次 Parent
    this.gender = gender
}

function proto(child, parent) {
    let clonePrototype = Object.create(parent.prototype)
    child.prototype = clonePrototype
    child.prototype.constructor = child
}

proto(Child, Parent)

ES6 class

Синтаксис класса относительно ясен.Если вы можете использовать класс, просто используйте класс:

class Parent {
    constructor(name, friends) { // 该属性在构造函数上,不共享
        this.name = name
        this.friends = friends
    }
    log() { // 该方法在原型上,共享
        return this
    }
}
Parent.prototype.share = [1, 2, 3] // 原型上的属性,共享

class Child extends Parent {
    constructor(name, friends, gender) {
        super(name, friends)
        this.gender = gender
    }
}

Кроме того, вы можете использовать метод get set, чтобы записать свойство общего доступа в прототип.

Кроме того, class — это своего рода синтаксический сахар, используйте babel, чтобы преобразовать его, чтобы увидеть:

20190317201244.png

резюме

И, наконец, последняя картинка является кратким описанием:

20190317202656.png

Ссылаться на: