// Метод contains стал проще: такая реализация пригодна только
// для множества с единственным элементом
contains: function(x) { return
х === this.member; }
});
Класс
SingletonSet
имеет очень простую реализацию, состоящую из пяти простых методов. Этот класс не только реализует пять основных методов класса
Set
, но и наследует от своего суперкласса такие методы, как
toString, toArray и equals.
Возможность наследования методов является одной из основных причин определения подклассов. Метод
equals
класса
Set
(определен в разделе 9.6.4), например, может сравнивать любые экземпляры класса
Set
, имеющие методы
size
и
foreach,
с любыми экземплярами класса
Set,
имеющими методы
size
и
contains.
Поскольку класс
SingletonSet
является подклассом класса
Set
, он автоматически наследует его метод
equals
и не обязан иметь собственную реализацию этого метода. Безусловно, учитывая чрезвычайно упрощенную структуру множества, содержащего единственный элемент, можно было бы реализовать для класса
SingletonSet
более эффективную версию метода
equals:
SingletonSet.prototype.equals = function(that) {
return that instanceof Set && that.size==1 && that.contains(this.member);
};
Обратите внимание, что класс
SingletonSet
не просто заимствует список методов из класса
Set
: он динамически наследует методы класса
Set
. Если в
Set.prototype
добавить новый метод, он тут же станет доступен всем экземплярам классов
Set
и
SingletonSet
(в предположении, что класс
SingletonSet
не определяет собственный метод с таким же именем).
9.7.2. Вызов конструктора и методов базового класса
Класс
SingletonSet
из предыдущего раздела определяет совершенно новый тип множеств и полностью переопределяет основные методы, наследуемые от суперкласса. Однако часто при определении подкласса необходимо лишь расширить или немного изменить поведение методов суперкласса, а не заменить их полностью. В этом случае конструктор и методы подкласса могут вызывать конструктор и методы базового класса.
Пример 9.13 демонстрирует применение этого приема. Он определяет подкласс
NonNullSet
класса
Set
: тип множеств, которые не могут содержать элементы со значениями
null
и
undefined
. Чтобы исключить возможность включения в множество таких элементов, класс
NonNullSet
должен выполнить в методе
add
проверку значений добавляемых элементов на равенство значениям
null
и
undefined
. Но при этом не требуется включать в класс полную реализацию метода
add
– можно просто вызвать версию метода из суперкласса. Обратите также внимание, что конструктор
NonNullSet
тоже не реализует
все необходимые операции: он просто передает свои аргументы конструктору суперкласса (вызывая его как функцию, а не как конструктор), чтобы конструктор суперкласса мог инициализировать вновь созданный объект.
Пример 9.13. Вызов из подкласса конструктора и метода базового суперкласса
/*
* NonNullSet - подкласс класса Set, который не может содержать элементы
* со значениями null и undefined.
*/
function NonNullSet {
// Простое обращение к суперклассу.
// Вызвать конструктор суперкласса как обычную функцию для инициализации
// объекта, который был создан вызовом этого конструктора.
Set.apply(this, arguments);
}
// Сделать класс NonNullSet подклассом класса Set:
NonNullSet.prototype = inherit(Set.prototype);
NonNullSet.prototype.constructor = NonNullSet;
// Чтобы исключить возможность добавления значений null и undefined,
// достаточно переопределить метод add
NonNullSet.prototype.add = function {
// Проверить наличие аргументов со значениями null и undefined
for(var і = 0; і < arguments.length; i++)
if (arguments[i] == null)
throw new Еrror("Нельзя добавить null или undefined в NonNullSet”);
// Вызвать метод базового суперкласса, чтобы фактически добавить элементы
return Set.prototype.add.apply(this, arguments);
};
Теперь обобщим понятие "множество без пустых элементов" до понятия "фильтрованное множество": множество, элементы которого должны пропускаться через функцию-фильтр перед добавлением. Определим фабричную функцию (подобную функции
enumeration
) из примера 9.7), которая будет получать функцию-фильтр и возвращать новый подкласс класса
Set
. В действительности можно пойти еще дальше по пути обобщений и определить фабричную функцию, принимающую два аргумента: наследуемый класс и функцию-фильтр, применяемую к методу
add.
Новой фабричной функции можно было бы дать имя
filteredSetSubclass
и использовать ее, как показано ниже:
// Определить класс множеств, которые могут хранить только строки
var StringSet = filteredSetSubclass(Set,
function(x) {return typeof x===*'string";});
// Определить класс множеств, которые не могут содержать значения null,
// undefined и функции
var MySet = filteredSetSubclass(NonNullSet,
function(x) {return typeof x !== "function";});
Реализация этой фабричной функции приводится в примере 9.14. Обратите внимание, что эта функция вызывает метод и конструктор базового класса подобно тому, как это реализовано в классе
NonNullSet
.
Пример 9.14. Вызов конструктора и метода базового класса
/*
* Эта функция возвращает подкласс указанного класса Set и переопределяет