Опубликован: 10.12.2007 | Доступ: свободный | Студентов: 822 / 20 | Оценка: 5.00 / 5.00 | Длительность: 58:33:00
Лекция 5:

Скрипты

5.3.2. Объекты

В языке JavaScript есть объекты, но в версии 1.5 его еще нельзя назвать полностью объектно-ориентированным. Описание реализации многих концепций объектно-ориентированных систем в JavaScript приведено в таблице 5.2.

Таблица 5.2. Поддержка объектов в JavaScript
Концепция объектных систем Поддержка в синтаксисе Легкость в использовании
Агрегация Хорошая Легко
Включение Хорошая Легко
Делегация Хорошая Несложно
Инкапсуляция Недостаточная Несложно
Наследование Достаточная Сложно
Скрытие данных Недостаточная Несложно
Интерфейсы Нет Сложно
Позднее связывание Хорошая Легко
Основанность на объектах Хорошая Легко
Ориентация на объекты Нет Сложно
Множественное наследование Нет Сложно
Определение типов во время исполнения Хорошая Легко
Шаблоны Нет Сложно

В JavaScript для всех объектов и их атрибутов реализуется позднее связывание. Попытка написать полностью объектно-ориентированный код - технический трюк, которого лучше избегать. JavaScript создан для простого манипулирования объектами, а не для создания сложных (или вообще каких-либо) иерархий классов. Большая часть объектов, используемых в JavaScript, приходят из базового ПО и реализуются в другом языке.

В JavaScript 1.5 нет определений класса; есть только типизация во время выполнения. В обычной объектной системе классов можно создавать объекты с помощью абстрактного описания - класса. Это напоминает выполнение наброска перед изготовлением статуи. Вместо этого в JavaScript для создания объектов используется система прототипов. Это похоже на высечение статуи по образцу. Все объекты JavaScript, кроме объектов из базового ПО, создаются с помощью другого объекта как отправной точки. Такой подход более гибок, чем классовый, но при создании в JavaScript чего-нибудь сложного синтаксис кода будет просто нечитаемым.

5.3.2.1. Простые объекты

В JavaScript элемент данных с типом Object обладает набором свойств, составляющих содержимое объекта. Некоторые свойства содержат функции для работы над этим объектом, называющиеся методами объекта. Свойства особого глобального объекта также называются переменными и функциями. Почти все в JavaScript - свойство какого-нибудь объекта. В этом языке нет скрытия данных: в терминах C++ и Java все свойства публичные.

У всех свойств есть атрибуты, но эти атрибуты - незаметная черта языка, и для работы с ними нет собственного синтаксиса. Можно прочитать об атрибутах в стандарте ECMAScript, но на самом деле действительно не стоит на них отвлекаться. Они отличаются от атрибутов XML.

Чтобы создать собственный объект, можно воспользоваться или оператором new, или литералом объекта, как показано в листинге 5.2.

// явное создание
var obj = new Object;
obj.foreground = "red";
obj.background = "blue";

// создание с помощью литерала
var obj = { foreground:"red", background:"blue" }
Листинг 5.2. Примеры создания объектов в JavaScript

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

function start_it() { this.run = true; }
// явное добавление
var obj = new Object;
obj.start = start_it;
obj.stop = function (){ this.run = false; }

// добавление в литерале
var obj = { start: function (){ this.run = true; },
    stop: function (){ this.run = false; }
}

// выполнение методов
obj.start();
obj.stop();
Листинг 5.3. Примеры создания методов в JavaScript

this относится к объекту, свойством которого является вызываемая функция.

Объекты могут содержать и другие объекты:

var obj = {
  mother:{name:"Jane", age:34},
  father:{name:"John", age:35}
};
var my_ma_name = obj.mother.name;

Включение и объединение в JavaScript - одно и то же, так как реального скрытия данных нет. Некоторое скрытие осуществить можно, повозившись со свойствами объектов Function в цепи прототипов. Таким образом можно создать постоянные свойства, которые будут находиться в области видимости только во время запуска функции - на деле получится локальная переменная. Этот неочевидный технический фокус обычно не нужен. К нему можно прибегнуть, только если вы предоставляете библиотеку готовых объектов для их использования кем-нибудь другим и хотите, чтобы ваша библиотека была надежной как скала. См. также раздел "Расширения языка", где рассказано о функциях чтения и задании значений свойств.

Если нужно создать много объектов с похожими свойствами, то указывать все свойства для каждого из них довольно утомительно. Лучше воспользоваться конструктором объектов. Это функция, вызываемая как аргумент new. В ней задаются все необходимые стандартные свойства объекта. Затем функцию конструктора можно использовать для каждого нового объекта, как показано в листинге 5.4.

function Parents(ma, pa) {
  this.mother = { mother:ma; };
  this.father = { father:pa; };
  this.dog = "Spot";
}
var family1 = new Parents("Jane","John");
var family2 = new Parents("Joanne","Joe");
Листинг 5.4. Примеры создания объектов с помощью функции конструктора

Это решение можно улучшить и дальше. После создания функции можно менять объект-прототип. Объект-прототип - это объект, который предоставляет создаваемому объекту свойства по умолчанию. В листинг 5.4 сразу после определения функции для Parents можно добавить следующие строки:

Parents.prototype.lastname = "Smith";
Parents.prototype.ring = function (){ dial(123456789); };

Тогда при создании family1 и family2 получат помимо трех свойств из тела функции еще и дополнительные свойства из прототипа, то есть всего пять свойств. Так что у них не только будет собака ( dog ) по кличке Спот ( Spot ), но и фамилия у них обоих будет Смит ( Smith ), и номер телефона у них тоже будет один и тот же. В действительности свойства lastname и ring - общие для обоих объектов. Если у одного из них изменится свойство lastname, это новое значение заменит свойство lastname прототипа, и тогда значения данного свойства у объекта и прототипа более не будут общими. Если же изменится свойство lastname прототипа, тогда оно изменится и у всех объектов, для которых это свойство общее. Такие свойства отличаются от свойств, заданных как dog в нашем примере - это свойство уникально для каждого создаваемого объекта.

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

См. также раздел "Цепочки прототипов" в этой лекции.

5.3.3.2. Объекты базового ПО

Объекты базового программного обеспечения существуют вне JavaScript в самом базовом ПО, в которое встроен интерпретатор. В Mozilla такие объекты обычно написаны на C++. Некоторый код на C, связанный с интерпретатором JavaScript, находит их по мере надобности, создает простой внутренний интерфейс, напоминающий объект JavaScript, связывает с этим интерфейсом объект базового ПО и, таким образом, делает этот объект доступным для скрипта. Сложности возникают, прежде всего, при поиске объекта, и JavaScript требуется помощь базового ПО (платформы Mozilla). Объект document - пример описанного выше класса объектов.

Объекты базового ПО для программиста выглядят так же, как и обычные объекты, если только не пытаться превратить их обратно в код с помощью toString(). Так как функции и методы в JavaScript - тоже объекты, эта разница действительна и для них. Например, следующий фрагмент кода пытается получить тело функции alert() (в XUL это метод объекта базового ПО типа ChromeWindow ):

var str = "" + alert;

Результирующая строка, однако, показывает, что у alert() нет исходного текста на JavaScript:

"\nfunction alert() {\n [native code]\n}"
5.3.2.3. Собственные объекты

Интерпретатор JavaScript предоставляет набор собственных объектов. Вот их имена (и типы):

Object Array Boolean Number String Math Date RegExp Function Error

Объекты могут создаваться интерпретатором JavaScript автоматически и во время исполнения, они также могут создаваться явно по указанию программиста, для чего требуется конструктор. Имена этих объектов совпадают с объектами конструктора. Следовательно, чтобы создать объект типа Boolean, нужно воспользоваться объектом конструктора Boolean, который так и называется:

var flag = new Boolean();

Теоретически есть разница, стоит ли после new обращение к объекту- прототипу или объекту конструктора, но практически это одно и то же. В последнем случае требуются скобки, как у функции, тогда как в первом случае это необязательно.

Типы Object и Array уже обсуждались. Для объектов Array предусмотрено свойство length (длина массива), тогда как у объектов Object оно отсутствует.

Объекты Boolean, Number и String соответствуют базовым типам данных с соответствующими именами. JavaScript свободно и автоматически преобразует данные между базовыми типами и соответствующими объектами, даже в случае литералов. Следующий пример демонстрирует автоматические преобразование литералов в объекты, вызывающие некоторый метод.

"Test remark".charAt(3); // результат: "t"
1.2345.toFixed(2); // результат: 1.23
true.toString(); // результат: "true"

Объект Math предоставляет разные математические функции, например, Math.sin().

Объект Date хранит даты и использует несколько методов чтения значений свойств. Объекты этого типа поддерживают только западный григорианский календарь, расширенный во времени вперед и назад. Поддерживаются и даты до наступления "эпохи UNIX" (1 января 1970), и значения, не являющиеся 32-битными time_t. Они хранятся с двойной точностью IEEE и достигают 280000 лет вперед и назад. Даты точны до миллисекунд, если часы компьютера достаточно точны. Нулевое значение даты совпадает с началом "эпохи UNIX", так что у всех значений типа time_t - корректные значения Date. Не следует пользоваться методом getYear(), он устарел, лучше вызывать метод getFullYear().

Объект RegExp хранит шаблон регулярного выражения и флаги. Некоторые методы, относящиеся к регулярным выражениям, доступны и в объекте String.

Объект Function представляет функции и методы. Синтаксис его конструктора несколько странен. Почти всегда вместо new Function рекомендуется использовать анонимные функции или синтаксис с применением ключевого слова function.

Объект Error сообщает об ошибках во время исполнения скрипта и об исключениях, которые не были перехвачены в блоках try или finally. Для программистов он не очень полезен, так как можно просто заглянуть в консоль JavaScript и найти там ту же информацию.

При изучении этих объектов имеет смысл пользоваться стандартом ECMAScript-262 как справочником. Свойства и методы объекта типа X описываются в разделе 15 под заголовком "Properties of the X Prototype Object" и "Properties of X Instances". Это правило работает для всех типов, кроме объектов Math (см. следующий раздел).

5.3.2.4. Встроенные объекты и похожие эффекты

При запуске интерпретатора JavaScript некоторые объекты доступны и без написания какого-либо кода. Если это собственные объекты интерпретатора, то они называются встроенными объектами. Объекты базового ПО также могут быть доступными до написания кода. Такая автоматическая инициализация для удобства выполняется всегда. Яркими примерами могут служить объекты Global, Math и document.

Глобальный объект находится наверху иерархии включения объектов. Это корневой объект в JavaScript. Он не является свойством никакого другого объекта и не может быть создан без отдельной и независимой среды исполнения. В Mozilla объект Window (в HTML) и объекты ChromeWindow (в XUL) - глобальные объекты. Они реализованы так, что обладают свойством window. Это свойство ссылается на глобальный объект (получается цикл). Программисты в своих скриптах пользуются этим объектом window как корневым.

Объект Math также создается при запуске среды исполнения JavaScript. На него ссылается свойство глобального объекта с именем Math. Это допускает следующий сокращенный синтаксис для математических операций:

var one = Math.sin(Math.PI/2);

Если документ загружается в окно Mozilla, процесс загрузки автоматически сделает доступными множество дополнительных объектов. Эти объекты знакомы web-программистам по объектной модели документа DOM 0. В HTML они формируют обширную иерархию включения, которая часто используется следующим образом:

window.document.form3.username.value = "John";

Явное использование префикса window. необязательно. Эквивалентные префиксы - this и self.

По сравнению с HTML у XUL довольно ограниченный набор автоматически создаваемых объектов. В XUL для поиска объектов базового ПО, не создаваемых при запуске, используется служба именования XPCOM-объектов.