Ключом к созданию подклассов в языке JavaScript является корректная инициализация объекта-прототипа. Если класс В расширяет класс А, то объект В.prototype должен наследовать A.prototype
. В этом случае экземпляры класса В будут наследовать свойства от объекта В.prototype
, который в свою очередь наследует свойства от A.prototype
. В этом разделе демонстрируются все представленные выше термины, связанные с подклассами, а также рассматривается прием, альтернативный наследованию, который называется композицией.
Используя в качестве основы класс Set
из примера 9.6, этот раздел продемонстрирует, как определять подклассы, как вызывать конструкторы базовых классов и переопределенные методы, как вместо наследования использовать прием композиции и, наконец, как отделять интерфейсы от реализации с помощью абстрактных классов. Этот раздел завершает расширенный пример, в котором определяется иерархия классов Set
. Следует отметить, что первые примеры в этом разделе служат цели продемонстрировать основные приемы использования механизма наследования, и некоторые из них имеют существенные недостатки, которые будут обсуждаться далее в этом же разделе.
9.7.1. Определение подкласса
В языке JavaScript объекты наследуют свойства (обычно методы) от объекта-прототипа своего класса. Если объект O
является экземпляром класса В
, а класс В
является подклассом класса А
, то объект O
также наследует свойства класса А
. Добиться этого можно за счет наследования объектом-прототипом класса В
свойств объекта-прототипа класса А
, как показано ниже, с использованием функции inherit()
(пример 6.1):
В.prototype = inherit(A.prototype); // Подкласс наследует суперкласс
В.prototype.constructor = В; // Переопределить унаследованное св. constructor
Эти две строки являются ключом к созданию подклассов в JavaScript. Без них объект-прототип будет обычным объектом - объектом, наследующим свойства от Object.prototype
, - а это означает, что класс будет подклассом класса Object
, подобно всем остальным классам. Если добавить эти две строки в функцию defineClass()
(раздел 9.3), ее можно будет преобразовать в функцию defineSubclass()
и в метод Function.prototype.extend(),
как показано в примере 9.11.
Пример 9.11. Вспомогательные инструменты определения подклассов
// Простая функция для создания простых подклассов
function defineSubclass(superclass, // Конструктор суперкласса
constructor, // Конструктор нового подкласса
methods, // Методы экземпл.: копируются в прототип
statics) // Свойства класса: копируются в констр-р
{
// Установить объект-прототип подкласса
constructor.prototype = inherit(superclass.prototype);
constructor.prototype.constructor = constructor;
// Скопировать методы methods и statics, как в случае с обычными классами
if (methods) extend(constructor.prototype, methods);
if (statics) extend(constructor, statics);
// Вернуть класс
return constructor;
}
// To же самое можно реализовать в виде метода конструктора суперкласса
Function.prototype.extend = function(constructor, methods, statics) {
return defineSubclass(this, constructor, methods, statics);
};
Пример 9.12 демонстрирует, как определить подкласс «вручную», без использования функции def ineSubclass(). В этом примере определяется подкласс SingletonSet класса Set. Класс SingletonSet представляет специализированное множество, доступное только для чтения и состоящее из единственного постоянно элемента.
Пример 9.12. SingletonSet: простой подкласс множеств
// Функция-конструктор
function SingletonSet(member) {
this.member = member; // Сохранить единственный элемент множества
}
// Создает объект-прототип, наследующий объект-прототип класса Set.
SingletonSet.prototype = inherit(Set.prototype);
// Далее добавляются свойства в прототип.
// Эти свойства переопределяют одноименные свойства объекта
Set.prototype. extend(SingletonSet.prototype, {
// Установить свойство constructor
Читать дальше
Конец ознакомительного отрывка
Купить книгу