, представленный выше, возвращает 0, когда два диапазона имеют одинаковые нижние границы. Это означает, что в данной реализации метод
сотрагеТо
считает равными любые два диапазона, которые имеют одинаковые нижние границы. Однако такое определение равенства не согласуется с определением, положенным в основу метода
equals,
который требует равенства обеих границ. Подобные несоответствия в определениях равенства могут стать причиной опасных ошибок, и было бы лучше привести методы
equals
и
compareTo
в соответствие друг с другом. Ниже приводится обновленная версия метода
compareTo
класса
Range
. Он соответствует методу
equals
и дополнительно возбуждает исключение при передаче ему несопоставимого значения:
// Порядок следования диапазонов определяется их нижними границами
// или верхними границами, если нижние границы равны.Возбуждает исключение,
// если методу передается объект, не являющийся экземпляром класса Range.
// Возвращает 0, только если this.equals(that) возвращает true.
Range.prototype.compareTo = function(that) {
if (!(that instanceof Range))
throw new Еrror("Нельзя сравнить Range c " + that);
var diff = this.from - that.from; // Сравнить нижние границы
if (diff == 0) diff = this.to - that.to; // Если равны, сравнить верхние
return diff;
};
Одна из причин, по которым может потребоваться сравнивать экземпляры класса, - обеспечить возможность сортировки массива экземпляров этого класса. Метод Array.sort может принимать в виде необязательного аргумента функцию сравнения, которая должна следовать тем же соглашениям о возвращаемом значении, что и метод
compareTo.
При наличии метода
compareTo,
представленного выше, достаточно просто организовать сортировку массива объектов
Сортировка имеет настолько большое значение, что следует рассмотреть возможность реализации статического метода сравнения в любом классе, где определен метод экземпляров
compareTo.
Особенно если учесть, что первый может быть легко реализован в терминах второго, например:
При наличии этого метода сортировка массива может быть реализована еще проще:
ranges.sort(Range.byLowerBound);
Некоторые классы могут быть упорядочены более чем одним способом. Например, класс
Card
определяет один метод класса, упорядочивающий карты по масти, а другой - упорядочивающий по значению.
9.6.5. Заимствование методов
В методах JavaScript
нет ничего необычного - это обычные функции, присвоенные свойствам объекта и вызываемые «посредством» или «в контексте» объекта.
Одна и та же функция может быть присвоена двум свойствам и играть роль двух методов. Мы использовали эту возможность в нашем классе
Set
, например, когда скопировали метод
toArray
и заставили его играть вторую роль в виде метода
toJSON
.
Одна и та же функция может даже использоваться как метод сразу нескольких классов. Большинство методов класса
Array
, например, являются достаточно универсальными, чтобы копировать их из
Array.prototype
в прототип своего класса, экземпляры которого являются объектами, подобными массивам. Если вы смотрите на язык JavaScript через призму классических объектно-ориентированных языков, тогда использование методов одного класса в качестве методов другого класса можно рассматривать как разновидность множественного наследования. Однако JavaScript не является классическим объектно-ориентированным языком, поэтому я предпочитаю обозначать такой прием повторного использования методов неофициальным термином заимствование.
Заимствоваться могут не только методы класса
Array
: мы можем реализовать собственные универсальные методы. В примере 9.9 определяются обобщенные методы
toString
и
equals,
которые с успехом могут использоваться в таких классах, как
Range, Complex
и
Card
. Если бы класс Range не имел собственного метода
equals,
мы могли бы заимствовать обобщенный метод
equals
, как показано ниже:
Range.prototype.equals = generic.equals;
Обратите внимание, что метод
generic.equals
выполняет лишь поверхностное сравнение и не подходит для использования в классах, свойства экземпляров которых ссылаются на объекты с их собственными методами
equals.
Отметьте также, что этот метод включает специальный случай для обработки свойства, добавляемого к объектам при включении их в множество Set (пример 9.6).
Пример 9.9. Обобщенные методы, пригодные для заимствования
var generic = {
// Возвращает строку, включающую имя функции-конструктора, если доступно,
// и имена и значения всех неунаследованных свойств, не являющихся функциями.
toString: function {
var s = '[';
// Если объект имеет конструктор и конструктор имеет имя, использовать
// это имя класса как часть возвращаемой строки. Обратите внимание, что
// свойство name функций является нестандартным и не поддерживается повсеместно,
if (this.constructor && this.constructor.name) s += this.constructor.name + ";
// Теперь обойти все неунаследованные свойства, не являющиеся функциями