<b> return defineSubclass(this, constructor, methods, statics);</b>
<b>};</b>
Пример 9.12 демонстрирует, как определить подкласс «вручную», без использования функции def ineSubclass(). В этом примере определяется подкласс SingletonSet класса Set. Класс SingletonSet представляет специализированное множество, доступное только для чтения и состоящее из единственного постоянно элемента.
Пример 9.12. SingletonSet: простой подкласс множеств
<b>// Функция-конструктор</b>
<b>function SingletonSet(member) {</b>
<b> this.member = member; // Сохранить единственный элемент множества</b>
<b>}</b>
<b>// Создает объект-прототип, наследующий объект-прототип класса Set.</b>
<b>SingletonSet.prototype = inherit(Set.prototype);</b>
<b>// Далее добавляются свойства в прототип.</b>
<b>// Эти свойства переопределяют одноименные свойства объекта</b>
<b>Set.prototype. extend(SingletonSet.prototype, {</b>
<b> // Установить свойство constructor</b>
<b> constructor: SingletonSet,</b>
<b> // Данное множество доступно только для чтения: методы add() и remove()</b>
<b> // возбуждают исключение</b>
<b> add: function() { throw "множество доступно только для чтения"; },</b>
<b> remove: function() { throw "множество доступно только для чтения"; },</b>
<b> // Экземпляры SingletonSet всегда имеют размер, равный 1</b>
<b> size: function() { return 1; },</b>
<b> // Достаточно вызвать функцию один раз и передать ей единственный элемент,</b>
<b> foreach: function(f, context) { f.call(context, this.member); },</b>
<b> // Метод contains() стал проще: такая реализация пригодна только</b>
<b> // для множества с единственным элементом</b>
<b> contains: function(x) { return х === this.member; }</b>
<b>});</b>
Класс
<b>SingletonSet</b>
имеет очень простую реализацию, состоящую из пяти простых методов. Этот класс не только реализует пять основных методов класса
<b>Set</b>
, но и наследует от своего суперкласса такие методы, как
<b>toString(), toArray() и equals().</b>
Возможность наследования методов является одной из основных причин определения подклассов. Метод
<b>equals()</b>
класса
<b>Set</b>
(определен в разделе 9.6.4), например, может сравнивать любые экземпляры класса
<b>Set</b>
, имеющие методы
<b>size()</b>
и
<b>foreach(),</b>
с любыми экземплярами класса
<b>Set,</b>
имеющими методы
<b>size()</b>
и
<b>contains().</b>
Поскольку класс
<b>SingletonSet</b>
является подклассом класса
<b>Set</b>
, он автоматически наследует его метод
<b>equals()</b>
и не обязан иметь собственную реализацию этого метода. Безусловно, учитывая чрезвычайно упрощенную структуру множества, содержащего единственный элемент, можно было бы реализовать для класса
<b>SingletonSet </b>
более эффективную версию метода
<b>equals():</b>
<b>SingletonSet.prototype.equals = function(that) {</b>
<b> return that instanceof Set && that.size()==1 && that.contains(this.member);</b>
<b>};</b>
Обратите внимание, что класс
<b>SingletonSet</b>
не просто заимствует список методов из класса
<b>Set</b>
: он динамически наследует методы класса
<b>Set</b>
. Если в
<b>Set.prototype</b>
добавить новый метод, он тут же станет доступен всем экземплярам классов
<b>Set</b>
и
<b>SingletonSet</b>
(в предположении, что класс
<b>SingletonSet </b>
не определяет собственный метод с таким же именем).
9.7.2. Вызов конструктора и методов базового класса
Класс
<b>SingletonSet</b>
из предыдущего раздела определяет совершенно новый тип множеств и полностью переопределяет основные методы, наследуемые от суперкласса. Однако часто при определении подкласса необходимо лишь расширить или немного изменить поведение методов суперкласса, а не заменить их полностью. В этом случае конструктор и методы подкласса могут вызывать конструктор и методы базового класса.
Пример 9.13 демонстрирует применение этого приема. Он определяет подкласс
<b>NonNullSet</b>
класса
<b>Set</b>
: тип множеств, которые не могут содержать элементы со значениями
<b>null</b>
и
<b>undefined</b>
. Чтобы исключить возможность включения в множество таких элементов, класс
<b>NonNullSet</b>
должен выполнить в методе
<b>add()</b>
проверку значений добавляемых элементов на равенство значениям
<b>null</b>
и
<b>undefined</b>
. Но при этом не требуется включать в класс полную реализацию метода
<b>add()</b>
- можно просто вызвать версию метода из суперкласса. Обратите также внимание, что конструктор
<b>NonNullSet()</b>
тоже не реализует все необходимые операции: он просто передает свои аргументы конструктору суперкласса (вызывая его как функцию, а не как конструктор), чтобы конструктор суперкласса мог инициализировать вновь созданный объект.