Чтение онлайн

ЖАНРЫ

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

В методах 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 + ";

// Теперь обойти все неунаследованные свойства, не являющиеся функциями

var n = 0;

for(var name in this) {

if (!this.hasOwnProperty(name)) continue; // пропустить унаслед.

var value = this[name];

if (typeof value === ’function") continue; // пропустить методы

if (n++) s += ", ";

s += name + '=' + value;

}

return s + ']';

},

//
Проверить равенство, сравнив конструкторы и свойства экземпляров объектов this

// и that. Работает только с классами, свойства экземпляров которых являются

// простыми значениями и могут сравниваться с помощью оператора ===.

// Игнорировать специальное свойство, добавляемое классом Set.

equals: function(that) {

if (that == null) return false;

if (this.constructor !== that.constructor) return false;

for(var name in this) {

if (name === "|**objectid**|") continue; // пропустить спец. св.

if (!this.hasOwnProperty(name)) continue; // пропустить унасл. св.

if (this[name] !== that[name]) return false; // сравнить значения

}

return true; // Объекты равны, если все свойства равны.

}

};

9.6.6. Частные члены

В классическом объектно-ориентированном программировании зачастую целью инкапсуляции, или сокрытия данных объектов внутри объектов, является обеспечение доступа к этим данным только через методы объекта и запрет прямого доступа к важным данным. Для достижения этой цели в таких языках, как Java, поддерживается возможность объявления «частных» (

private
) полей экземпляров класса, доступных только через методы экземпляров класса и невидимые за пределами класса.

Реализовать частные поля экземпляра можно с помощью переменных (или аргументов), хранящихся в замыкании, образуемом вызовом конструктора, который создает экземпляр. Для этого внутри конструктора объявляются функции (благодаря чему она получает доступ к аргументам и локальным переменным конструктора), которые присваиваются свойствам вновь созданного объекта. Этот прием демонстрируется в примере 9.10, где он используется для создания инкапсулированной версии класса

Range
. Вместо простых свойств
from
и
to
, определяющих границы диапазона, экземпляры этой новой версии класса предоставляют методы
from
и
to
, возвращающие значения границ. Методы
from
и
to
не наследуются от прототипа, а определяются отдельно для каждого объекта
Range
. Остальные методы класса
Range
определяются в прототипе как обычно, но изменены так, чтобы вместо чтения значений границ напрямую из свойств они вызывали бы методы
from
и
to.

Пример 9.10. Класс Range со слабо инкапсулированными границами

function Range(from, to) {

// Не сохраняет границы в свойствах объекта. Вместо этого определяет функции доступа,

// возвращающие значения границ. Сами значения хранятся в замыкании,

this.from = function { return from; };

this.to = function { return to; };

Поделиться с друзьями: