В языке Java поддерживается возможность объявлять поля со спецификатором
final
, чтобы показать, что они являются константами, и объявлять поля и методы со спецификатором
private
, чтобы показать, что они являются частными для реализации класса и недоступны пользователям
класса. В языке JavaScript эти ключевые слова отсутствуют, поэтому, чтобы обозначить частные свойства (имена которых начинаются с символа подчеркивания) и свойства, доступные только для чтения (имена которых содержат только заглавные символы), в примере 9.3 используются соглашения по именованию. Мы еще вернемся к этим двум темами ниже в этой главе: частные свойства можно имитировать с помощью локальных переменных в замыканиях (раздел 9.6.6), а возможность определения свойств-констант поддерживается стандартом ECMAScript 5 (раздел 9.8.2).
9.4. Наращивание возможностей классов
Механизм наследования на основе прототипов, используемый в языке JavaScript, имеет динамическую природу: объекты наследуют все свойства своих прототипов, даже если они были добавлены в прототипы уже после создания объектов. Это означает, что в JavaScript имеется возможность наращивать возможности классов простым добавлением новых методов в объекты-прототипы. Ниже приводится фрагмент, который добавляет метод вычисления сопряженного комплексного числа в класс
Complex
из примера 9.3:
// Возвращает комплексное число, которое является сопряженным
// по отношению к текущему.
Complex.prototype.conj = function { return new Complex(this.r, -this.i); };
Объект-прототип встроенных классов JavaScript также «открыт» для подобного наращивания, а это означает, что есть возможность добавлять новые методы к числам, строкам, массивам, функциям и т. д. Данная возможность уже использовалась в примере 8.5. Там мы добавляли метод
bind
к классу функций в реализации ECMAScript 3, где он отсутствует:
, тем самым делая их доступными для всех объектов. Однако делать это не рекомендуется, потому что в реализациях, появившихся до ECMAScript 5, отсутствует возможность сделать эти дополнительные методы неперечислимыми. При добавлении новых свойств в
Object.prototype
они становятся доступны для перечисления в любом цикле
for/in
. В разделе 9.8.1 приводится пример использования метода
Object.defineProperty
, определяемого стандартом ECMAScript 5, для безопасного расширения
Object, prototype
.
Возможность подобного расширения классов, определяемых средой выполнения (такой как веб-броузер), зависит от реализации самой среды. Во многих веб-броузерах, например, допускается добавлять методы в
HTMLElement.prototype
, и такие методы будут наследоваться объектами, представляющими теги HTML в текущем документе. Однако данная возможность не поддерживается в текущей версии Microsoft Internet Explorer, что сильно ограничивает практическую ценность этого приема в клиентских сценариях.
9.5. Классы и типы
В главе 3 уже говорилось, что в языке JavaScript определяется небольшое количество типов:
null, undefined
, логические значения, числа, строки, функции и объекты. Оператор
typeof
(раздел 4.13.2) позволяет отличать эти типы. Однако часто бывает желательно интерпретировать каждый класс как отдельный тип данных и иметь возможность отличать объекты разных классов. Отличать встроенные объекты базового языка JavaScript (и объекты среды выполнения в большинстве реализаций клиентского JavaScript) можно по их атрибуту
class
(раздел 6.8.2), используя прием, реализованный в функции
classof
из примера 6.4. Но когда класс определяется с помощью приемов, продемонстрированных в этой главе, экземпляры объектов всегда содержат в атрибуте
class
значение «Object», поэтому функция
classof
в данной ситуации оказывается бесполезной.
В следующих подразделах описываются три приема определения класса произвольного объекта: оператор
instanceof
, свойство
constructor
и имя функции-конструктора. Однако ни один из этих приемов не дает полной гарантии, поэтому в разделе 9.5.4 мы обсудим прием грубого определения типа (duck-typing) - философии программирования, в которой центральное место отводится возможностям объекта (т. е. наличию тех или иных методов), а не его принадлежности к какому-либо классу.