JavaScript. Подробное руководство, 6-е издание, стр. 126

Пример 9.13. Вызов из подкласса конструктора и метода базового суперкласса

<b>/*</b>

<b> * NonNullSet - подкласс класса Set, который не может содержать элементы</b>

<b> * со значениями null и undefined.</b>

<b>*/</b>

<b>function NonNullSet() {</b>

<b>  // Простое обращение к суперклассу.</b>

<b>  // Вызвать конструктор суперкласса как обычную функцию для инициализации</b>

<b>  // объекта, который был создан вызовом этого конструктора.</b>

<b>  Set.apply(this, arguments);</b>

<b>}</b>

<b>// Сделать класс NonNullSet подклассом класса Set:</b>

<b>NonNullSet.prototype = inherit(Set.prototype);</b>

<b>NonNullSet.prototype.constructor = NonNullSet;</b>

<b>// Чтобы исключить возможность добавления значений null и undefined,</b>

<b>// достаточно переопределить метод add()</b>

<b>NonNullSet.prototype.add = function() {</b>

<b>  // Проверить наличие аргументов со значениями null и undefined</b>

<b>  for(var і = 0; і &lt; arguments.length; i++)</b>

<b>    if (arguments[i] == null)</b>

<b>      throw new Еrror(&quot;Нельзя добавить null или undefined в NonNullSet”);</b>

<b>  // Вызвать метод базового суперкласса, чтобы фактически добавить элементы</b>

<b>  return Set.prototype.add.apply(this, arguments);</b>

<b>};</b>

Теперь обобщим понятие "множество без пустых элементов" до понятия "фильтрованное множество": множество, элементы которого должны пропускаться через функцию-фильтр перед добавлением. Определим фабричную функцию (подобную функции

<b>enumeration</b>
) из примера 9.7), которая будет получать функцию-фильтр и возвращать новый подкласс класса
<b>Set</b>
. В действительности можно пойти еще дальше по пути обобщений и определить фабричную функцию, принимающую два аргумента: наследуемый класс и функцию-фильтр, применяемую к методу
<b>add().</b>
Новой фабричной функции можно было бы дать имя 
<b>filteredSetSubclass()</b>
и использовать ее, как показано ниже:

<b>// Определить класс множеств, которые могут хранить только строки</b>

<b>var StringSet = filteredSetSubclass(Set,</b>

<b>          function(x) {return typeof x===*'string&quot;;});</b>

<b>// Определить класс множеств, которые не могут содержать значения null,</b>

<b>// undefined и функции</b>

<b>var MySet = filteredSetSubclass(NonNullSet,</b>

<b>          function(x) {return typeof x !== &quot;function&quot;;});</b>

Реализация этой фабричной функции приводится в примере 9.14. Обратите внимание, что эта функция вызывает метод и конструктор базового класса подобно тому, как это реализовано в классе

<b>NonNullSet</b>
.

Пример 9.14. Вызов конструктора и метода базового класса

<b>/*</b>

<b>* Эта функция возвращает подкласс указанного класса Set и переопределяет</b>

<b>* метод add() этого класса, применяя указанный фильтр.</b>

<b>*/</b>

<b>function filteredSetSubclass(superclass, filter) {</b>

<b>  var constructor = function() { // Конструктор подкласса</b>

<b>    superclass.apply(this, arguments); // Вызов конструктора базового класса</b>

<b>  };</b>

<b>  var proto = constructor.prototype = inherit(superclass.prototype);</b>

<b>  proto.constructor = constructor; proto.add = function() {</b>

<b>    // Примерить фильтр ко всем аргументам перед добавлением</b>

<b>    for(var і = 0; і &lt; arguments.length; i++) {</b>

<b>      var v = arguments[i];</b>

<b>      if (!filter(v)) throw(&quot;значение + v + отвергнуто фильтром&quot;);</b>

<b>    }</b>

<b>    // Вызвать реализацию метода add из базового класса</b>

<b>    superclass.prototype.add.apply(this, arguments);</b>

<b>  };</b>

<b>  return constructor;</b>

<b>}</b>

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

<b>superclass</b>
в вызовах конструктора и метода базового класса и избежать указания фактического имени суперкласса. Это означает, что в случае изменения имени суперкласса достаточно будет изменить имя в одном месте, а не отыскивать все его упоминания в программном коде. Такого способа стоит придерживаться даже в случаях, не связанных с определением фабричных функций. Например, с помощью функции-обертки можно было бы переписать определение класса
<b>NonNullSet</b>
и метода
<b>Function.prototype.extend()</b>
(пример 9.11), как показано ниже: