Это мои заметки о Смешивании (объединении) объектов класса, четвертой главе Кайла Симпсона книги Вы не знаете Javascript: это и прототипы объектов.

Теория классов

Объектно-ориентированное или классово-ориентированное программирование подчеркивает, что данные по своей сути имеют связанное с ними поведение, поэтому правильный дизайн заключается в объединении данных и поведения вместе. В компьютерных науках это иногда называют «структурами данных».

Например, ряд символов обычно называют «строкой». Символы — это данные. Обычно вы хотите выполнять операции со строкой, такие как проверка ее длины, добавление данных и т. д. Класс String будет включать их как методы, а также строковые данные.

Классы подразумевают способ классификации определенной структуры данных. Мы делаем это, думая о любой данной структуре как о конкретной вариации более общего базового определения.

Например, мы могли бы создать класс Vehicle с двигателем и перемещением людей, а затем указать его в классе Car с четырьмя колесами. Car будет «наследовать» или «расширять» поведение класса Vehicle (копируя его), а затем дополнительно специализировать поведение (например, добавлять 4 колеса).

Вышеуказанные классы определяют поведение. Фактический объект Car будет создан путем инстанцирования. Экземпляр Car будет похож на автомобиль с уникальным идентификационным номером.

Классы наследуют поведение других классов и превращаются в объекты посредством инстанцирования.

Еще одно важное понятие — полиморфизм. Полиморфизм — это характеристика способности придавать разное значение или использование чему-то в разных контекстах.

Мы будем называть это идеей о том, что общее поведение родительского класса может быть переопределено в дочернем классе, чтобы придать ему больше специфики. Теория классов предполагает, что родительский и дочерний классы должны иметь одно и то же имя для определенного поведения, поэтому дочерний класс переопределяет родительский.

В JavaScript нет классов.

Классы — это необязательный шаблон проектирования, и вы можете использовать их в JavaScript.

Классовая механика

Класс — это план. Чтобы получить объект для взаимодействия, мы должны построить (или создать экземпляр) что-то из класса. Конечным результатом «конструкции» является объект, обычно называемый «экземпляром», к которому мы можем получить доступ через методы.

Объект является копией всех характеристик, описанных классом. Класс превращается в объект с помощью операции копирования.

На приведенной выше диаграмме вы можете видеть a1 и a2, созданные из класса Foo. Класс Bar унаследован от Foo, а b1 и b2 созданы из Bar.

Экземпляры классов создаются специальным методом класса, обычно с тем же именем, что и у класса, называемым конструктором. Явная работа этого метода заключается в инициализации любой информации (состояния), которая потребуется экземпляру. Например, проверьте этот псевдокод:

class CoolGuy {
  specialTrick = nothing
CoolGuy( trick ) {
    specialTrick = trick
  }
showOff() {
    output( "Here's my trick: ", specialTrick )
  }
}

Чтобы создать экземпляр CoolGuy, мы вызываем конструктор класса:

Joe = new CoolGuy( "jumping rope" )
Joe.showOff() // Here's my trick: jumping rope

Конструктор класса принадлежит классу.

Наследование классов

Вы можете определить другой класс, который наследуется от первого класса.

Второй класс часто называют «дочерним классом», а первый — «родительским классом».

Дочерний класс содержит первоначальную копию поведения родительского класса, но затем может переопределить любое унаследованное поведение и даже определить новое поведение. Например:

class Vehicle {
  engines = 1
ignition() {
    output( "Turning on my engine." )
  }
drive() {
    ignition()
    output( "Steering and moving forward!" )
  }
}
class Car inherits Vehicle {
  wheels = 4
  
  drive() {
    inherited:drive()
    output( "Rolling on all ", wheels, " wheels!" )
  }
}

Полиморфизм

Car определяет свой собственный метод drive(), который переопределяет одноименный метод, унаследованный от Vehicle. Но Car также ссылается на inherited:drive(), а это означает, что Car может ссылаться на исходный предварительно переопределенный drive(), который он унаследовал.

Это полиморфизм, или идея о том, что любой метод может ссылаться на другой метод на более высоком уровне иерархии наследования. Важно отметить, что дочерний класс фактически получает доступ к поведению из копии, полученной от родительского класса. Дочерний класс не имеет доступа к родительскому классу. Наследование классов подразумевает копии.

Мы называем это «относительным» полиморфизмом, потому что мы пытаемся получить доступ к ссылке только с одного уровня вверх по иерархии наследования.

Во многих языках ключевое слово super используется вместо inherited:, что опирается на идею о том, что «суперкласс» является родителем/предком текущего класса. super — это способ для конструктора дочернего класса относительно ссылаться на конструктор своего родительского класса через унаследованное поведение.

Благодаря полиморфизму определения методов могут меняться в зависимости от того, на экземпляр какого класса вы ссылаетесь.

Множественное наследование

Некоторые языки, ориентированные на классы, позволяют вам наследовать более чем от одного родителя. Множественное наследование означает, что каждое определение родительского класса копируется в дочерний класс.

Множественное наследование вводит несколько проблем. «Проблема алмаза» относится к сценарию, в котором класс «D» наследуется от «B» и C, а каждый из них наследуется от «A». Если «B» и «C» имеют свои собственные разные методы drive(), какой из них должен наследовать «D»?

JavaScript не имеет такой функциональности, хотя некоторые пытались ввести этот шаблон кода.

Миксины

В JavaScript нет классов, только объекты. Они не копируются, а соединяются вместе.

Разработчики JS имитируют поведение отсутствующего копирования классов с помощью миксинов.

Название «mixin» происходит от идеи, что вы «смешиваете» содержимое родительского «класса» (объекта в JS) с дочерним «классом».

Явные миксины

Утилита для копирования поведения из родительского класса в дочерний часто называется extend(..). Здесь мы будем называть их mixin(..) для иллюстративных целей.

Явная функция миксина будет принимать исходный и целевой объекты в качестве параметров. Он проверит, имеет ли целевой объект свойства исходного объекта, и если нет, он «скопирует» его с помощью присваивания =.

Полиморфизм достигается вызовом нужного родительского метода в контексте дочернего класса через .call(this);. Например:

var Car = mixin( Vehicle, {
  wheels: 4,
  
  drive: function() {
    Vehicle.drive.call( this );
    console.log( "Rolling on all " + this.wheels + " wheels!" );
  }
} );

Кайл Симпсон называет Vehicle.drive.call( this ) примером «явного псевдополиморфизма».

Этот стиль кода обычно приводит к более сложному, трудночитаемому и сложному в сопровождении коду, потому что вы должны создавать эту ручную и явную связь в каждой функции, которая нуждается в полиморфной ссылке.

Миксины — это не то же самое, что реальное дублирование в классах, потому что миксины копируют только ссылки на объекты/функции.

Неявные миксины

Неявные миксины — это когда вы просто «заимствуете» функцию другого объекта. Они не используют функцию для копирования свойств, таких как явные примеси. Например:

var Another = {
  cool: function() {
    // borrow Something's cool function
    Something.cool.call( this );
  }
};

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

Подделка классов в JS может создать много проблем.