JavaScript, несложный язык программирования с синтаксисом, напоминающим C-синтаксис, является существенной частью платформы Mozilla. Программы на JavaScript или их фрагменты называются сценариями, или скриптами. При добавлении скриптов XUL документы из документов, доступных только для чтения, становятся динамическими интерфейсами, которые могут выполнять какие-нибудь действия в ответ на команды пользователя. Написание скриптов - задача программиста, а не автора web-страницы или человека, предоставляющего для нее информацию. Приложения Mozilla могут разрабатываться только программистами.
В этой лекции описывается сам язык JavaScript. Также в ней дается обзор многих служб платформы Mozilla, которые доступны из скриптов JavaScript. В дальнейшем эти функции и службы будут рассматриваться более подробно. О JavaScript написано уже немало. В этой лекции нашего курса дается полное описание языка, хотя и очень сжатое.
Скрипты JavaScript, являющиеся частью приложения Mozilla, могут следовать одному или нескольким более традиционным стилям программирования в зависимости от амбициозности приложения.
Легкие приложения Mozilla содержат скрипты, похожие на скрипты web- страниц. В web-страницах такие скрипты часто добавляют к HTML- содержимому уже в самом конце разработки. Когда эти скрипты разрастаются, их иногда называют динамическим HTML. Но даже тогда они по сути дела просто переставляют части HTML-содержимого так, чтобы в целом все выглядело приятнее. Макросы, использующиеся в продуктах вроде Microsoft Word, такие же легкие.
В приложениях Mozilla
Очень сложные приложения Mozilla вроде среды разработки Komodo от
ActiveState содержат столько скриптов, что они перевешивают базовую
функциональность браузера платформы. В таких случаях JavaScript можно
сравнить с другими самостоятельными средами
Однако чаще всего программисты создают приложения средней сложности. XUL, JavaScript, XPCOM и функциональность платформы, доступная по умолчанию, комбинируются в конечное приложение. JavaScript "цементирует" все используемые технологии.
На рисунке в начале этой лекции показаны те части Mozilla, которые
наиболее важны для поддержки скриптов. Из этого рисунка видно, что
JavaScript равноудален и от пользователя, и от операционной системы.
Это так потому, что JavaScript и его скрипты - встроенные технологии,
скрытые внутри другого программного обеспечения. Скрипты редко
управляют Mozilla извне. Две самые важные части, XPConnect и DOM,
находятся глубоко внутри платформы Mozilla. Они предоставляют большое
число программных интерфейсов (API,
Простейший вариант применения JavaScript требует XML и тега <script>. И для XUL-, и для HTML-документов следующая
строчка изменит слова в строке заголовка окна документа:
<script> window.title = "Scripted Title"; </script>
Скрипты, подобные этому, могут менять любую часть отображаемого
окна Mozilla, в том числе и любую часть его содержимого. Такие скрипты
также неявно взаимодействуют и с внешним миром, в том числе с
Internet. Само по себе содержимое тега <script> очень важно и требует
специальной обработки, как и CSS-код внутри тега <style>.
Платформа Mozilla сама частично написана с использованием скриптов
javaScript. Такие скрипты часто можно найти в
Перед углублением в синтаксис языка стоит задать вопрос: почему выбран JavaScript? Ответ на этот вопрос дается ниже.
JavaScript - член семьи языков с C-подобным синтаксисом. Среди
самых известных его "родственников" C и
У этих языков похожий синтаксис и немного - структура. Все они
являются if. Некоторые из этих языков достаточно развиты и
имеют поддержку объектов. Объекты предоставляют структуры уровнем выше
процедурных шагов.
Качества, присущие JavaScript, делают этот язык (почти) уникальным членом данной группы. Он спроектирован как в перспективе самый доступный и легкий язык в своей семье. Он также создан, чтобы быть универсальным языком. Этот язык легко переносим, требует небольших ресурсов и не нуждается в поддержке традиционного компилятора. Это легкость делает его очень привлекательным для многих задач.
Код JavaScript запускается внутри интерпретатора, виртуальной
машины вроде
Так как JavaScript - довольно ограниченный язык, он во многом зависит от другого программного обеспечения (которое часто называют базовым). В этом проявляется сходство этого языка с C, который тоже мало что может без помощи своих библиотек stdio или их эквивалентов. Платформа Mozilla в виде набора библиотек и исполняемых файлов предоставляет базу для интерпретатора JavaScript.
Так как базовое ПО обычно достаточно велико, большая часть времени,
затраченная на написание JavaScript-скриптов, уходит на то, чтобы
выяснить, что может предложить базовое ПО. В Mozilla JavaScript играет
примерно ту же роль, что и Visual Basic for Applications (
Mozilla поддерживает стандарт
SpiderMonkey также поддерживает работу с более ранними версиями
JavaScript, в том числе несколько необычную версию 1.2. Эта версия
содержала множество новых функций, из которых многие так и не
приобрели популярность. При необходимости можно включить поддержку
более ранних версий, следуя подходу первых браузеров Netscape. В XUL-
приложениях всегда следует использовать последнюю доступную версию
языка. Для других
Вот правильный способ включать JavaScript-код в
<script type="application/x-javascript" src="code.js/>
Использование следующего варианта не приветствуется, поэтому лучше избегать его, хотя это тоже будет работать:
<script type="text/javascript" src="code.js/>
Для выбора конкретной версии JavaScript можно делать так:
<script type="JavaScript1.2" src="code.js/>
Наконец, следующий способ подразумевает, что код скрипта написан на языке JavaScript последней версии:
<script src="code.js/>
Для нового приложения Mozilla идеален синтаксис первого примера.
Последний вариант - полезная альтернатива, но ради аккуратности и
безупречности следует всегда добавлять атрибут type. У всех этих
примеров кодировка по умолчанию - encoding="UTF-8".
В лекции 2, "Проектирование с XUL", в разделе "Хороший стиль
кодирования на XUL" объясняется, почему JavaScript-код всегда должен
храниться вне
Организация Mozilla предоставляет и второй интерпретатор
JavaScript, написанный не на C, а на Java. Эта версия интерпретатора
называется Rhino и не используется и не собирается вместе с платформой
Mozilla; тем не менее, она доступна для загрузки. Она также совместима
со стандартом
В этом разделе описываются особенности языка JavaScript,
соответствующие стандарту
Здесь мы взглянем на текст, написанный на языке JavaScript. Так как
стандарт
JavaScript-скрипты Mozilla хранятся как обычные текстовые файлы. Содержимое этих файлов должно быть в кодировке UTF-8. Это значит, что символы из таблицы ASCII хранятся каждый в одном байте. Этого достаточно, если предпочтительный язык - английский, так как никакая особая подготовка не нужна. Можно просто пользоваться обычным текстовым редактором.
В некоторых системах задействуются и неиспользуемые значения ASCII
от 128 до 255, например, для разных символов языков Западной Европы $$\acute{e}$$
в слове $$r\acute{e}sum\acute{e}$$. Раньше эта традиция была удобным способом создавать
текст на других языках. Но это не соответствует правилам использования
UTF-8, которые требуют двух или более байтов для всех символов не из
таблицы ASCII. Вставка таких 8-битных европейских символов в скрипты
будет безуспешной. Символ $$\acute{e}$$ правильно кодируется в UTF-8 как "é", по
крайней мере, это то, что показывает простенький текстовый редактор,
не знающий о UTF-8.
Но даже у корректно закодированных многобайтовых символов UTF-8
ограниченное применение. Они могут появляться только внутри строковых
литералов JavaScript, в комментариях и иногда в именах переменных. Но
в общем рекомендуется придерживаться в коде программы ASCII-символов;
другие символы следует использовать только для данных. Для европейцев
использование для имен переменных символов таблицы Latin1 в целом
безопасно, если эти символы корректно закодированы в UTF-8. Чтобы
узнать подробнее о поддержке Unicode, см. раздел 7.6 стандарта
В строках по-прежнему можно работать с любыми Unicode-символами. Подробнее об этом рассказано в разделе "Типы данных".
JavaScript - язык, в котором визуальное форматирование большой роли
не играет, как и XML, и C, например. Одна инструкция не обязана
находиться на одной строке. В число распознаваемых 0A в шестнадцатеричном
представлении) и символ 0D в шестнадцатеричном
представлении). Напомним, что в Windows,
Комментарии в JavaScript пишутся в C-стиле. Поддерживаются как однострочные комментарии:
// Однострочный комментарий
так и многострочные:
/* комментарий на несколько строк */
Возможны любые комбинации комментариев и кода, но один
многострочный комментарий не может появиться внутри другого. В
JavaScript нет специальных комментариев, которые используются для
документации. Перед интерпретацией скрипты не проходят предварительную
обработку. для JavaScript не
существует.
Кирпичики, из которых строятся скрипты JavaScript, - инструкции.
Инструкция может завершаться точкой с запятой, но это не обязательно.
Есть только один случай, когда точки с запятой обязательны - между
тремя выражениями инструкции for(;;). В остальных случаях
интерпретатор автоматически будет предполагать, что точка с запятой
поставлена, даже если ее на самом деле нет. Следующие три инструкции
эквивалентны:
x = 5; x = 5 x = 5 // несмотря на комментарий, эта инструкция эквивалентна предыдущей
Возможность опускать точку с запятой нужна для того, чтобы упростить задачу разработчикам, привыкшим к стилю Visual Basic. Тем не менее, рекомендуется всегда использовать точку с запятой. Дело не только в том, что текст так становится понятнее, но и в том, что в будущих версиях JavaScript это может стать необходимостью.
Скриптам не нужна процедура main() или что-то подобное ей. Как и в
Perl, инструкции могут появляться вне функций и объектов начиная с
первой строки. В JavaScript также допустима инструкция, не делающая
ничего:
;
JavaScript поддерживает составные инструкции, оформляющиеся с
помощью фигурных скобок { и }, но они отличаются от своих аналогов в C
(см. раздел "Определение области видимости"). Составные инструкции
сами по себе в JavaScript не так уж полезны, хотя и
поддерживаются:
{ x = 5; y= 5; }
В этой лекции инструкция означает или одиночную инструкцию, завершающуюся точкой с запятой, или составную инструкцию без конечной точки с запятой.
В JavaScript поддерживаются следующие собственные типы данных:
Undefined Null Boolean Number String Object
Существует также скрытый безымянный тип данных, соответствующий 32- битным беззнаковым целым.
В JavaScript типы ассоциируются с элементами данных, а не со
структурами, хранящими эти данные (например, переменными). Можно
привести очень старую аналогию между переменными в программе и
коробками для обуви. По этой аналогии элемент данных соответствует
паре ботинок, а структура, хранящая ее (переменная) - обувной коробке.
В JavaScript информация о типе связывается с ботинками, вы не найдете
ее в коробке. Переменные, хранящие данные типа Boolean, Number или String, подразумевают обувную коробку, в которой находится
единственная пара ботинок определенного типа. Тип Object подразумевает
коробку, в которой может быть много пар, связанных вместе. Тип Null
подразумевает пустую коробку, а тип используется для коробки
с неопределенным содержимым.
Для определения типов можно пользоваться оператором typeof. Для
обычных данных JavaScript он вернет одну из следующих строк, также он
может вернуть другую строку, если тип данных определен в базовом
ПО:
"undefined" "boolean" "number" "string" "object" "function"
При этом если тип данных - Null, возвращается "object". Давайте по
очереди рассмотрим все эти типы.
Тип данных допускает только одно значение
(неопределенное), но литеральная константа для него отсутствует. У
глобального объекта (см. ниже) есть единственное свойство с именем , содержащим это значение. При необходимости это значение
можно генерировать с помощью оператора void или с помощью :
x = void 0; X = undefined;
Тип Null допускает только одно значение: пустое. Существует
литерал null, который может использоваться для опустошения переменной
или в сравнениях:
x = null;
Тип Boolean допускает два значения: истина и ложь. Литералами для
этих значений будут true и false соответственно. false не эквивалентен
0, как в C, но преобразование между этими двумя значениями настолько
незаметно, что мелкие различия обычно можно не учитывать.
x = true;
Тип Number позволяет хранить 64-битные двойной точности числа с
isNaN() и isFinite() соответственно. В
JavaScript для этих значений литералов нет, но у глобального объекта
есть свойства и , кроме того, у объекта Math имеется
несколько других полезных свойств:
POSITIVE_INFINITY NEGATIVE_INFINITY NaN MAX_VALUE MIN_VALUE
Эти свойства можно использовать в сравнениях. Для литералов чисел с
x = -3.141592654; y = 1.0e+23; z = 234.555555E-100;
Числа также можно записывать в шестнадцатеричном виде, указывая
префикс 0x или 0X, за которым должны следовать цифры от 0 до 9 и буквы
от A до F в любом регистре.
x = 0xFEFF;
Метод сравнивания нечисловых значений ( ) в JavaScript
соответствует рекомендациям
Так как
Но в некоторых ситуациях значение может на самом деле храниться как
нецелое число. Вот несколько примеров: если это число с десятичной
точкой; если в результате деления получается остаток; если вызывается
функция, результат которой - sin() );
или если в результате вычислений получается число, большее 2AAAA). Во
всех остальных случаях данные типа Number хранятся как целые.
Если число преобразовывается из целого в нецелое,
Тип String представляет последовательность символов Unicode,
хранящихся в кодировке
\b \t \n \v \f \r \" \' \\ and \x и \u
Выше представлены соответственно символы забоя, табуляции, перевода
строки, вертикальной табуляции, перевода страницы, 0xA0 ). Сюда также входят
символы ISO 8859 (
str = "hello, world\n";
Тип Object будет рассматриваться в отдельном разделе, посвященном
только ему.
В JavaScript предусмотрено
JavaScript - язык третьего поколения поэтому в нем доступны
определяемые пользователем переменные. Имена переменных должны
начинаться с буквы, подчеркивания или знака доллара ($). Последнего
следует избегать, так как он редко используется в коде, не
генерируемом автоматически. Ограничений на длину имен переменных нет.
Далее, после первого символа, в именах переменных можно использовать
буквы, цифры, подчеркивание и знак доллара. Имена переменных
my_variable x counter5 interCapName not$common _secret
Соглашения об именах рекомендуют использовать для имен констант только заглавные буквы, разделяя слова подчеркиванием (как в Java); имена с единственной заглавной буквой в начале следует использовать для конструкторов объектов; подчеркивание в начале имени должно сигнализировать о том, что переменная не предназначена для свободного применения.
Переменные не определены, если они не объявлены с ключевым словом var. Если они используются, не будучи объявленными, возникает
var x; var y = 2, z = 3.45; var product = x * y * z;
Каждая переменная JavaScript хранит один элемент данных. Этот
элемент - или простое значение или ссылка на объект. В разделе
"Объекты" объясняется также, что переменные являются и свойствами. В
JavaScript нет указателей и синтаксиса для явного использования
ссылок. Имена переменных не могут совпадать с зарезервированными
словами вроде if. Переменная с именем this - особая и всегда указывает
на текущий объект.
В JavaScript, как и в C, поддерживаются одномерные массивы, но их
размер может задаваться и неконстантным выражением. Массивы создаются
с помощью ключевого слова new, которое также используется при создании
самых разных объектов. Вот несколько вариантов
var arr1 = new Array(); // массив нулевой длины var arr2 = new Array(5); // массив из 5 элементов var arr3 = new Array(11,12,13); // массив из 3 элементов var arr4 = new Array(2,"red",true); // массив из 3 элементов
Все элементы массива не определены, если только их содержимое не
задано при создании массива. Каждый элемент массива может хранить
данные любого типа. Массив также можно создать с помощью литерала,
заключенного в квадратные скобки, [ ]. Следующие примеры совпадают с
предыдущими и часто предпочтительны, так как метод Array() выглядит не
очень красиво и часто вносит путаницу:
var arr1 = []; // массив нулевой длины var arr2 = [, , , , ,]; // массив из 5 элементов var arr3 = [11,12,13]; // массив из 3 элементов var arr4 = [2,"red",true]; // массив из 3 элементов
Литералы массивов могут быть вложенными, так что элементы массивов могут сами быть массивами:
var arr5 = [ 6, ["red","blue"], 8, [], 10];
На элементы массивов можно ссылаться по их индексам, которые
начинаются с 0. Свойство массива length - целое число, большее индекса
самого последнего элемента в массиве на один. Оно не равно числу
элементов массива и обновляется автоматически:
a[0]; // первый элемент массива a b[2]; // третий элемент массива b c.length; // на один больше самого большого индекса в c c[c.length-1]; // последний элемент массива c d[1][4]; // см. ниже
В последней строке предыдущего примера используется массив d, чей
второй элемент, d[1] сам является массивом. Следовательно, d[1][4] -
пятый элемент массива d[1].
Размер массивов не фиксирован. Свойство length можно менять, или присваивая ему какое-то значение, или определяя элемент с большим индексом.
arr1 = new Array(3); // length - 3 arr1.length = 5; // теперь length - 5; arr1[8] = 42; // теперь length - 9;
Массивы разреженные. В предыдущем примере при определении элемента с индексом 8 элементы между 5 и 8 не создавались, если, конечно, они не использовались в последующих инструкциях. Так как доступны индексы из всего диапазона 32-битных беззнаковых целых, зазоры между элементами могут быть очень большими.
Немного забегая вперед, скажем, что массивы также являются
объектами (типа Array ). Все объекты поддерживают синтаксис,
позволяющий им вести себя как массивам. При этом объект представляется
как массив без свойства length ; просто разрешается использовать
нотацию квадратных скобок. Такой синтаксис может быть полезен при
поиске свойств во всех объектных данных, но эта гибкость не дает
преимуществ массивам, только другим объектам, как видно из
примера:
obj.prop_name == obj["prop_name"] // корректно и всегда истина obj[1] != obj.1 // некорректный синтаксис
Синтаксис в первой строке справа от знака равенства полезен, если нужно создать свойство объекта, чье имя не может быть корректным именем переменной. Например:
obj["A % overhead"] = 20;
Маленькая
obj[12.35] == obj["12.35"];
Результатом этого примера будет задание свойства объекта, а не
элемента массива, так как индексов с 12.35 не является корректным именем
переменной. Мораль такова: при работе с индексами пользуйтесь простыми
вычислениями.
Выражения JavaScript довольно точно соответствуют правилам
выражений в C, C++ и Java и предоставляют возможности выполнять
арифметические и логические вычисления,
| Имя | Бинарный | Приоритет | Символ |
|---|---|---|---|
| Принудительно задать высший приоритет | Унарный | 0 | () |
| Литерал массива | Унарный | 0 | [] |
| Литерал объекта | Унарный | 0 | {} |
| Вызов функции | Унарный | 0 | () |
| Свойство объекта | 1 | . | |
| Элемент массива | Бинарный | 1 | [] |
| Литерал объекта | Унарный | 1 | {} |
| Создание объекта | Унарный | 2 | new |
| Удалить ссылку на свойство | Унарный | 3 | delete |
| Преобразовать в неопределенное | Унарный | 3 | void |
| Получить строку, соответствующую типу | Унарный | 3 | typeof |
| Пре- и постинкремент | Унарный | 3 | ++ |
| Пре- и постдекремент | Унарный | 3 | -- |
| Тот же знак | Унарный | 3 | + |
| Противоположный знак | Унарный | 3 | - |
| 32-битное побитовое отрицание | Унарный | 3 | ~ |
| Логическое отрицание | Унарный | 3 | ! |
| Умножение | 4 | * |
|
| Деление | 4 | / |
|
| Остаток от деления | 4 | % |
|
| Сложение, конкатенация | 5 | + |
|
| Вычитание | 5 | - |
|
| 32-битный сдвиг влево | 6 | << |
|
| 32-битный знаковый сдвиг вправо | 6 | >> |
|
| 32-битный беззнаковый сдвиг вправо | 6 | >>> |
|
| Совпадает с данным типом | 7 | instanceof |
|
| Совпадает со свойством объекта | 7 | in |
|
| Порядковые сравнения | 7 | < > <= >= |
|
| Равенство | 7 | == != |
|
| Строгое равенство | 7 | === !== |
|
| 32-битное побитовое И | 8 | |
|
| 32-битное побитовое |
9 | ^ |
|
| 32-битное побитовое ИЛИ | 10 | | |
|
| Логическое И | 11 | |
|
| 12 | || |
||
| Тернарный | 13 | ?: |
|
| Простое присваивание | 14 | = |
|
| Составное присваивание | 14 | *= /= %= += -= <<= >>= >>>= = ^= |= |
|
| Разделитель элементов списка | 15 | , |
Приоритет 0 - самый высокий. Для операторов с одинаковым
приоритетом JavaScript грубо следует принятым в C соглашениям о
вычислениях слева направо и справа налево. Он также поддерживает
упрощенное вычисление булевых выражений, то есть если выражение
состоит из нескольких операций и ||, они вычисляются слева направо
только до тех пор, пока не будет понятно, что окончательный результат
уже не изменится, а не до тех пор, пока не будет вычислено последнее
условие.
Одна область, в которой логика работы с булевыми выражениями
JavaScript ближе к Perl, чем к C - многозначная семантика. По этому
соглашению выражения с и || играют роль управляющих конструкций
наподобие ?:, а не обычных булевых выражений. Поэтому в инструкции
var x = flag y;
переменная x принимает значение y, если значение flag - true, иначе
она принимает значение false. В C переменная x как значение принимала
бы результат вычисления булевого выражения " flag и y ".
В математических выражениях смесь операндов типа Number, хранящихся
как целых и как нецелых, приведет к тому, что результат будет нецелым.
Попытка применить побитовые операции к нецелым числам приведет к тому,
что эти числа будут обрезаны до 32 бит в общем бесполезным способом.
Убедитесь, что побитовые операции выполняются только для тех операндов
типа Number, которые хранятся как целые.
В JavaScript поддерживаются
if (выражение) инструкция
if (выражение) инструкция else инструкция
while (выражение) инструкция
do инструкция while (выражение)
for (выражение; выражение; выражение) инструкция
switch (выражение) {
case выражение: инструкция; break;
case выражение: инструкция; break; // столько раз, сколько нужно
default: инструкция; break;
}
Аргументом switch() может быть что угодно, не только переменная.
Селекторами case также могут быть не только литералы. Следующие две if -конструкции эквивалентны:
if (a) инструкция else if (b) инструкция else инструкция
if (a) инструкция else {if (b) инструкция else инструкция}
Как и во многих языках с C-подобным синтаксисом, стоит опасаться
if -конструкциями, когда else -часть относится к последнему if
независимо от расстановки отступов; эта
Для инструкции for есть вариация, которая позволяет проходить по
свойствам объекта JavaScript. При этом просматриваются все свойства,
не являющиеся DontEnum (см. раздел 8.6.1 стандарта
for ( имя-переменной in объект ) инструкция
В JavaScript нет инструкции . В нем есть метки, чьи имена
соответствуют именам переменных, но они находятся в отдельном
пространстве имен. continue завершает текущую итерацию цикла; break
прерывает выполнение цикла или выходит из инструкции switch. Метки
можно использовать для прерывания нескольких вложенных друг в друга
циклов:
mylabel: инструкция; break; break label; continue; continue label;
В JavaScript есть также система исключений. Это не необязательный довесок, а часть ядра языка. Она перехватывает ошибки и исключения при исполнении.
try { инструкция; }
catch (переменная) { инструкция; }
finally { инструкция; }
Блоков catch может быть несколько. Блок finally необязателен. Для
генерации исключения используется throw - или внутри блока try или где
угодно:
throw выражение;
Созданное исключение может сравниваться с данными любого типа,
начиная от обычного числа и заканчивая сложным специально созданным
объектом. Чтобы имитировать исключения, генерируемые XPConnect-частью
платформы, нужно всегда генерировать 32-битное целое, желательно одно
из значений объекта Components.results.
Скрипты часто пишутся на скорую руку с целью быстро добиться
какого-то результата, да и try, так
как исключения никогда не должны появляться перед пользователем. Самый
простой и эффективный способ гарантировать это - поместить все в один
большой блок try на самом верхнем уровне.
Инструкция with рассматривается в разделе "Область видимости".
JavaScript поддерживает функции. Функции нетипизированы и могут
работать с переменным числом аргументов, как, например, printf() в C.
Функции могут не иметь имени, тогда они анонимны, см. ниже. В
листинге 5.1 показана типичная функция.
function sum(x, y) {
if (arguments.length != 2) {
return void 0;
}
return x + y;
}
var a = sum(2,3); // a = 5
var b = sum(1,2,3); // b = undefined
var c = sum("red","blue"); // c = "redblue"
var d = sum(5, d); // d = 5 + undefined = NaN
var e = sum; // e теперь функция
var f = e(3,4); // f = 7
Объект аналогичен объекту Array за исключением того, что
он статичен: при добавлении к нему элементов свойство length не
изменится. Этот объект содержит все аргументы, переданные функции.
Функции также могут быть анонимными:
var plus = function (x,y) { return x + y; }
var a = plus(2,3);
Достоинство анонимных функций заключается в том, что они не создают автоматически дополнительную переменную с именем функции. Следовательно, можно создавать методы для объектов без "шлейфа" глобально определенных имен функций. Глобально определенных имен функций также можно избежать, поместив определение именованной функции в выражение:
var five = (function sum(a,b){return a+b;})(2,3);
Если функция вызывается сама по себе, а не как метод объекта, тогда
смысл ключевого слова this выясняется в соответствии с правилами
определения области видимости.
JavaScript поддерживает регулярные выражения Perl5 с некоторыми
мало заметными отличиями. Эти неявные отличия существуют, так как
синтаксис регулярных выражений очень чувствителен к малейшим
неточностям, находится в постоянном развитии и еще до конца не
устоялся. В UNIX-системах есть такие варианты регулярных выражений:
для работы с файлами, обычный и расширенный. Perl и JavaScript
поддерживают egrep(1) или параметру "Wildcards" окна поиска в
Microsoft Word. Написанное в справочной странице ( man(1) ) Perl, perlre, понять легче, чем текст определения , но ненамного.
Поищите какое-нибудь руководство в Internet.
Все операции над регулярными выражениями в JavaScript - методы
объекта String или они не существуют самостоятельно, как
оператор m// в Perl:
match(re) // "red".match(/e/) == ["e"]; replace(re,string) // "red".replace(/e/,"o") == "rod"; replace(re,function) // "red".replace(/e/,myfn); search(re) // "red".search(/e/) == 1; split(re) // "red".split(/e/) == ["r","d"];
replace() возвращает строку; search() - число; и
возвращают массивы строк.
У регулярных выражений есть синтаксис для использования литерала,
который может появиться в любой части кода, где также может появиться
строковый литерал. Такой литерал автоматически преобразовывается в
объект . Этот синтаксис таков:
/выражение/флаги
Здесь выражение - любое запутанное регулярное выражение; флаги -
ноль или более число g (заменять везде), i (не обращать внимания на
регистр) и m (в многострочных целях рассматривать каждую строку
отдельно).
В языке JavaScript есть объекты, но в версии 1.5 его еще нельзя назвать полностью объектно-ориентированным. Описание реализации многих концепций объектно-ориентированных систем в JavaScript приведено в таблице 5.2.
| Концепция объектных систем | Поддержка в синтаксисе | Легкость в использовании |
|---|---|---|
| Хорошая | Легко | |
| Включение | Хорошая | Легко |
| Делегация | Хорошая | Несложно |
| Недостаточная | Несложно | |
| Наследование | Достаточная | Сложно |
| Скрытие данных | Недостаточная | Несложно |
| Интерфейсы | Нет | Сложно |
| Позднее связывание | Хорошая | Легко |
| Основанность на объектах | Хорошая | Легко |
| Ориентация на объекты | Нет | Сложно |
| Нет | Сложно | |
| Определение типов во время исполнения | Хорошая | Легко |
| Шаблоны | Нет | Сложно |
В JavaScript для всех объектов и их атрибутов реализуется позднее
связывание. Попытка написать полностью объектно-ориентированный код -
технический трюк, которого лучше избегать. JavaScript создан для
простого манипулирования объектами, а не для создания сложных (или
вообще каких-либо)
В JavaScript 1.5 нет определений класса; есть только
В JavaScript элемент данных с типом Object обладает набором
свойств, составляющих содержимое объекта. Некоторые свойства содержат
функции для работы над этим объектом, называющиеся методами объекта.
Свойства особого глобального объекта также называются переменными и
функциями. Почти все в JavaScript - свойство какого-нибудь объекта. В
этом языке нет скрытия данных: в терминах C++ и Java все свойства
публичные.
У всех свойств есть атрибуты, но эти атрибуты - незаметная черта
языка, и для работы с ними нет собственного синтаксиса. Можно
прочитать об атрибутах в стандарте
Чтобы создать собственный объект, можно воспользоваться или оператором new, или литералом объекта, как показано в листинге 5.2.
// явное создание
var obj = new Object;
obj.foreground = "red";
obj.background = "blue";
// создание с помощью литерала
var obj = { foreground:"red", background:"blue" }
Чтобы добавить к объекту метод, нужно просто задать функцию или анонимную функцию как значение свойства объекта. Анонимные функции также могут появляться в литералах объектов, как показано в листинге 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();
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");
Это решение можно улучшить и дальше. После
Parents.prototype.lastname = "Smith";
Parents.prototype.ring = function (){ dial(123456789); };
Тогда при создании family1 и family2 получат помимо трех свойств из
тела функции еще и дополнительные свойства из прототипа, то есть всего
пять свойств. Так что у них не только будет собака ( dog ) по кличке
), но и фамилия у них обоих будет Смит ( Smith ), и номер
телефона у них тоже будет один и тот же. В действительности свойства и ring - общие для обоих объектов. Если у одного из них
изменится свойство , это новое значение заменит свойство прототипа, и тогда значения данного свойства у объекта и
прототипа более не будут общими. Если же изменится свойство
прототипа, тогда оно изменится и у всех объектов, для которых это
свойство общее. Такие свойства отличаются от свойств, заданных как dog
в нашем примере - это свойство уникально для каждого создаваемого
объекта.
Цель такой системы - позволить объекту быть тщательно
смоделированным один раз (с помощью объекта-прототипа) и затем иметь
возможность использовать эту модель в конструкторе при создании копии.
Действия конструктора можно ограничить, чтобы он работал только с
параметрами создания нового объекта и вызывал все необходимые для
инициализации методы. К несчастью, система прототипов не до конца
отвечает требованиям
См. также раздел "Цепочки прототипов" в этой лекции.
Объекты базового программного обеспечения существуют вне JavaScript
в самом базовом ПО, в которое встроен интерпретатор. В Mozilla такие
объекты обычно написаны на C++. Некоторый код на C, связанный с
интерпретатором JavaScript, находит их по мере надобности, создает
простой внутренний интерфейс, напоминающий объект JavaScript,
связывает с этим
Объекты базового ПО для программиста выглядят так же, как и обычные
объекты, если только не пытаться превратить их обратно в код с помощью toString(). Так как функции и методы в JavaScript - тоже объекты, эта
разница действительна и для них. Например, следующий фрагмент кода
пытается получить тело функции alert() (в XUL это метод объекта
базового ПО типа ChromeWindow ):
var str = "" + alert;
Результирующая строка, однако, показывает, что у alert() нет
исходного текста на JavaScript:
"\nfunction alert() {\n [native code]\n}"
Интерпретатор 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 соответствуют
"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().
Объект хранит String.
Объект Function представляет функции и методы. Синтаксис его
конструктора несколько странен. Почти всегда вместо new Function
рекомендуется использовать анонимные функции или синтаксис с
применением ключевого слова function.
Объект Error сообщает об ошибках во время исполнения скрипта и об
исключениях, которые не были перехвачены в блоках try или finally. Для
программистов он не очень полезен, так как можно просто заглянуть в
консоль JavaScript и найти там ту же информацию.
При изучении этих объектов имеет смысл пользоваться стандартом
X
описываются в разделе 15 под заголовком "Properties of the X Math (см. следующий раздел).
При запуске интерпретатора JavaScript некоторые объекты доступны и
без написания какого-либо кода. Если это собственные объекты
интерпретатора, то они называются встроенными объектами. Объекты
базового ПО также могут быть доступными до написания кода. Такая
автоматическая инициализация для удобства выполняется всегда. Яркими
примерами могут служить объекты Global, Math и document.
Глобальный объект находится наверху иерархии включения объектов.
Это корневой объект в JavaScript. Он не является свойством никакого
другого объекта и не может быть создан без отдельной и независимой
среды исполнения. В Mozilla объект Window (в HTML) и объекты ChromeWindow (в XUL) - глобальные объекты. Они реализованы так, что
обладают свойством window. Это свойство ссылается на глобальный объект
(получается цикл). Программисты в своих скриптах пользуются этим
объектом window как корневым.
Объект Math также создается при запуске среды исполнения
JavaScript. На него ссылается свойство глобального объекта с именем Math. Это допускает следующий сокращенный синтаксис для математических
операций:
var one = Math.sin(Math.PI/2);
Если документ загружается в окно Mozilla, процесс загрузки
автоматически сделает доступными множество дополнительных объектов.
Эти объекты знакомы web-программистам по
window.document.form3.username.value = "John";
Явное использование префикса window. необязательно. Эквивалентные
префиксы - this и self.
По сравнению с HTML у XUL довольно ограниченный набор автоматически
создаваемых объектов. В XUL для
Синтаксис определяет не только внешний вид кода, но и то, как интерпретатор JavaScript будет обрабатывать ваш скрипт. В этом языке используется несколько совершенно новых концепций.
Приоритет операторов указан в таблице 5.1. Правила вычислений слева направо и справа налево примерно такие же, как и в C, C++ и Java.
Все аргументы функций и методов передаются по ссылкам, за
исключением булевых, численных, неопределенных и null. В этих случаях
аргументы передаются по значению (копируются).
JavaScript автоматически преобразовывает данные между всеми
простыми типами и объектами Number, String и Boolean. Преобразование
типов выполняется так, чтобы выражение могло быть вычислено в любом
случае. Каждый объект в JavaScript обладает методами toNumber() и toString(), которые можно задействовать по мере надобности. Приведение
типов не требуется; оно выполняется в соответствии с расширенным
набором правил стандарта
Правило 1 существует потому, что содержимое строки может быть
некорректным числовым литералом. Тогда JavaScript вернет значение
и, в худшем случае, может аварийно завершить работу:
var str = "123stop45"; var x = str * 3; // str не число.
Аварийное завершение работы происходит только в случае
синтаксических ошибок или ошибок во время исполнения. Чтобы уберечься
от таких вещей, необходимо явно использовать функции parseInt() и parseFloat():
var x = parseInt("123stop45") * 3;
Второе правило существует потому, что операторы сравнения (<, == и т.д.) и оператор + для строк и чисел перегружаются. Правила, определяющие, считать ли оба операнда строками или числами, неочевидны и имеют разный смысл для разных операторов сравнения и конкатенации. Если вы не уверены в результате, следуйте первому правилу.
Область видимости определяет, какие переменные, объекты и свойства доступны в некоторой части кода. У этого процесса в JavaScript есть две стороны.
Первая сторона - традиционное определение области видимости переменных. Оно не отличается от своих аналогов в C и C++, где переменные могут быть локальными для функции или глобальными. В JavaScript в функции у локальных переменных могут быть такие же имена, как и у переменных вне функции. При интерпретации функции используется локальная переменная. При интерпретации инструкций вне функции используется глобальная переменная.
В C и C++, кроме того, каждая составная инструкция (блок) образует
собственную область видимости - внутри блока. То есть область
видимости переменной, объявленной внутри набора инструкций,
заключенных между { и }, отличается от области видимости переменных,
объявленных вне этого блока. В JavaScript такой блок с инструкциями не
создает собственной области видимости. Такой эффект в этом языке
отсутствует.
В C и C++ переменные, объявленные в середине области видимости, действительны далее, начиная с этого места. В JavaScript переменные, объявленные в середине функции (или где-нибудь в области глобальной видимости), действительны для всей области видимости. Следующий фрагмент кода, некорректный для C/C++, корректен в JavaScript:
function f() {
alert(x); // выдаст "undefined"
var x = 42;
alert(x); // выдаст "42";
}
f();
Другая сторона определения области видимости в JavaScript заключается в том, что в этом языке присутствует необычное понятие цепочек областей видимости. Цепочка областей видимости - это упорядоченный список объектов, в конце которого находится глобальный объект. При вызове функции интерпретатор сначала находит соответствующую функцию: для этого он просматривает объекты в цепочке областей видимости. Выполняться для этой функции будет первый найденный объект, у которого существует метод с таким же именем, как и у вызываемой функции. Этот механизм позволяет обработчикам событий на web-страницах вызывать функции, взяв за основу объект окна, даже если текущий объект какой-то другой. Цепочка областей видимостей делает доступными службы от разных объектов в одно и то же время.
Инструкция with в JavaScript временно добавляет объекты в цепочку
областей видимости. В листинге 5.5 показано, что функция toString()
используется несколько раз и каждый раз - для нового объекта. В то же
время переменная myflag всегда находится в объекте window, так как ни
у одного из этих объектов нет свойства myflag.
// цепочка областей видимости = window
var myflag = "Test String";
var x = toString(); // "[object ChromeWindow]"
with (document) {
// цепочка областей видимости = document, window
x = toString(); // "[object HTMLDocument]"
x = new Object;
with (x) {
// цепочка областей видимости = x, document, window
var y = toString(); // "[object Object]"
var x = myflag; // window.myflag
}
}
Цепочки областей видимости - дальние родственницы таблиц виртуальных функций в C++, реализующих наследование, но размеры цепочек областей видимости не фиксированы, и эти цепочки не реализуют наследование объектов в привычном понимании. Для программиста цепочки областей видимости полезны, но о них обычно не стоит задумываться.
В C, C++ и Java для данных используются два варианта размещения в
памяти: в стеке или в свободной памяти (или new или malloc(). В JavaScript нет стека, по крайней
мере, нет стека, с которым программист мог бы работать. Память всегда
выделяется из кучи. Ни для элементов массивов, ни для аргументов
функций не гарантируется последовательное размещение. Стеки
используются для реализации передачи аргументов функциям в Mozilla
SpiderMonkey, но этот механизм не виден программисту на
JavaScript.
JavaScript, как и Java - язык, в котором отсутствует понятие указателей, но есть механизм автоматической очистки памяти (сборка мусора). На каждый запущенный экземпляр среды исполнения JavaScript приходится один сборщик мусора. Объекты одного окна могут содержать ссылки на объекты других окон. При уничтожении окна все определенные пользователем объекты JavaScript помечаются как "мусор". Сборщик мусора JavaScript не запускается в отдельном потоке, как это сделано в Java.
JavaScript обладает возможностью интерпретировать новый код во
время выполнения. Самый удобный способ сделать это - применить метод глобального объекта. может использоваться для выполнения
любого корректного кода JavaScript:
var x = 5; var code = "x = x + 1;"; eval(code); // x = 6 x = x + 1; // x = 7
Другие аналогичные методы не связаны с интерпретацией напрямую. В
Mozilla это методы и , очень ограниченные parseInt(), parseFloat() и URL javascript:. Более подробно о механизме
интерпретации нового кода во время выполнения рассказано в разделе
"Использование XPCOM-компонентов".
JavaScript поддерживает замкнутые выражения. Это демонстрируется в следующем примере:
function accumulate(x) {
return function (y) { return x + y };
}
var subtotal = accumulate(2);
var total = subtotal(3); // total == 5
Каким будет возвращаемое значение при вызове анонимной функции?
Если бы при завершении области действия функции значение
аргумента x очищалось, она бы не участвовала в вычислениях при вызове .
Решение проблемы - замкнутые выражения. Замкнутое выражение - это набор данных, которые нужно сохранить и по завершении функции. Упрощенно это копия переменных из данной области действия, ссылающихся на другие объекты. Эти копии возвращаются, следовательно, данные, на которые они ссылаются, не теряют свою последнюю ссылку, когда исходные объекты очищаются. Таким образом, по завершении области действия сборщик мусора обходит эту группу созданных объектов стороной. Замкнутые выражения для программиста прозрачны. Их наличие имеет смысл для любого языка, поддерживающего вычисления во время выполнения.
Возможно, самая сложная особенность JavaScript - система
прототипов. Она довольно компактно описана в разделе 4.2.1 третьей
редакции стандарта
На самом деле у каждого объекта в JavaScript, включая те, которые
сами выступают как прототипы, есть объект-прототип с именем __proto__.
Поэтому следующий код совершенно корректен:
MyConstructor.prototype.__proto__.value = 5;
Такой набор прототипов, указанный явно или нет, называется цепочкой
прототипов. Эта цепочка заканчивается объектом Object., у
которого нет собственного прототипа. Все звенья этой цепочки добавляют
свойства к конечному создаваемому объекту. Сама цепочка представляет
собой упорядоченный список звеньев, именно в этом порядке свойства
будут добавляться. Одним из следствий является эффект затенения, когда
свойства, добавленные позже, могут переписывать значения добавленных
раньше свойств с теми же именами.
С помощью прототипов можно реализовывать объектно-ориентированное наследование. Самый просто способ это сделать - заменить прототип новым "базовым классом", то есть новым объектом-прототипом. Как видно из листинга 5.4, это можно сделать так:
function Family() { }; // Какой-то конструктор.
Family.prototype = new Parents; // Новый базовый "класс".
Свойство прототипу можно задать и из конструктора, учитывая то, что
различные вариации на тему общих, уникальных и
Приложения Mozilla время от времени пользуются системой прототипов, чтобы создавать собственные объекты. Обычно это делается, когда нужно создать несколько объектов заданного типа. Если явно создавать несколько объектов с помощью литералов объектов, получится слишком многословный код, поэтому обычно создается один конструктор объектов, который и используется для создания объектов.
Интерпретатор Mozilla SpiderMonkey толерантно относится к тому, что составляет корректную инструкцию:
x++ % y++;
Это поведение соответствует стандарту, но другие реализации JavaScript не поддерживают его.
Возможно, самым полезным расширением стандарта является
особенность, позволяющая методам JavaScript get и set быть связанными
со свойством объекта. Другими словами, побочным эффектом при чтении
или задании значения свойства будет вызов целой функции. Данная
функция должна реализовывать и стандартное действие, которое
происходит при чтении или записи свойства. В листинге 5.6
демонстрируется работа этих расширений.
var obj = {
get foo () { effect1(); return this._real_foo; },
set foo (val) { effect2(); return this._real_foo = val;},
_real_foo: "bar"
}
var x = obj.foo; // вызывается effect1() и x = "bar"
obj.foo = "zip"; // вызывается effect2() runs и _real_foo = "zip"
В этом примере свойство _real_foo хранит действительное значение
свойства , которое существует только как интерфейс. В терминах
стандарта [[Get]] и [[Put]], и это будут такие функции, которые
вам нужны.
Mozilla также предоставляет некоторые функции для управления перечисленными выше методами. Это
_defineGetter__("имя-свойства", объект-функция);
_defineSetter__("имя-свойства", объект-функция);
_lookupGetter__("имя-свойства");
_lookupSetter__("имя-свойства");
Первые две функции делают то же, что и функции get и set из
предыдущего примера. Следующие две функции возвращают
Mozilla также поддерживает свойство __parent__, которое влияет на
внутреннее свойство [[ объектов-функций. В JavaScript в Mozilla
это свойство также доступно и всем собственным объектам и объектам
базового ПО.
Наконец, Mozilla предоставляет свойство __proto__, с помощью
которого можно явно задавать или читать внутреннее свойство [[, описанное в стандарте.
Четвертая редакция
Некоторые из предложенных в этом черновике улучшений присутствуют в SpiderMonkey 1.5, но поддержка нового стандарта скоро будет закончена. Начать изучение особенностей четвертой редакции можно на сайте http://www.mozilla.org/js/language/.
Простое расширение из четвертой редакции - поддержка констант:
const varname = 5;
В Mozilla есть настройка javascript.options., которой можно
задать значение true. Ее также можно найти в настройках, связанных с
отладкой, если используется тестовая версия платформы. Это не то же
самое, что режим строго соответствия из черновика стандарта; это
просто дополнительная проверка корректности кода в соответствии с
третьей редакцией стандарта. Разработчикам рекомендуется ею
пользоваться.
Планируется, что SpiderMonkey 2.0 будет соответствовать четвертой редакции стандарта.
Платформа Mozilla запускает интерпретируемые скрипты на компьютере пользователя, что в принципе может быть небезопасно. Платформа предоставляет разные функции обеспечения защиты, и некоторые из них относятся к JavaScript. Более подробно о защите рассказано в лекции 16, "Объекты XPCOM".
Скрипт выдаст ошибку, если во время работы будет превышено довольно
большое число допустимых возвращающих инструкций - 4194304. В
сущности, возвращающая инструкция - любая часть выполняемого скрипта,
которая является возвратом функции, новой итерацией цикла или
прерыванием области действия из-за исключения. Такая ситуация
рассматривается как атака с целью вызвать
Тем не менее, скрипт все равно может выполнять долгую, ресурсоемкую
обработку - достаточно просто разделить работу на несколько частей и
выполнять каждую из них, используя методы и . Таким образом, между выполнением частей скрипта будут
небольшие паузы, в которые введенные пользователем данные могут
обрабатываться, а значит, предупреждающее окно не появится.
Основная стратегия защиты в Mozilla - принцип "с того же сайта". Скрипт, загруженный с одного сайта (домен и путь) не может взаимодействовать с документом или ресурсом другого сайта (менее точный путь). Таким образом, окна Mozilla остаются не зависящими друг от друга. При попытке записи в переменную в окне, куда доступ запрещен из соображений защиты, приводит к генерации исключения (ошибки).
От ограничений из соображений защиты освобождаются скрипты,
хранящиеся в . Самое большое ограничение - свойство Components
объекта Window или ChromeWindow полностью доступно. Это свойство -
дорожка к XPCOM-компонентам Mozilla. Ни один из этих компонентов не
доступен извне . Некоторые из этих объектов лежат в основе
какой-либо функциональности, доступ к которой осуществляется другим
способом. Пример: объект, реализующий объект Form, доступный в HTML-
документах.
скрипты JavaScript могут быть подписаны с помощью технологии
.
Как рука в перчатке, интерпретатор JavaScript и базовое программное
обеспечение, в которое он встроен, работают вместе. В этом разделе
описывается вклад в это
Как и в C, в JavaScript нет операций ввода-вывода. Весь ввод и весь вывод осуществляются через объекты базового ПО. Нужна, по крайней мере, простейшая часть базового ПО, чтобы предоставить эти объекты.
Если основные функции ввода/вывода предоставляются, скрипт может включать другие скрипты откуда-нибудь еще. Теоретически такие скрипты могут использоваться для создания очень больших программ. Это похоже на модульную среду Perl, но в JavaScript такая практика не очень распространена. В JavaScript ожидается, что основная часть функциональности программы будет реализована в объектах базового ПО. Поэтому изучать синтаксис и семантику этого языка очень просто, по сравнению с изучением обширных библиотек объектов.
Объекты базового ПО - концепция JavaScript. Хотя такие вещи появляются внутри скрипта как объекты, службам базового ПО, скрывающимся за ними, совсем необязательно быть объектами. В случае Mozilla функциональность, доступная в скриптах, обеспечивается платформой, которая состоит из набора интерфейсов. Многие из них неотличимы от объектов, и многие из них на самом деле реализованы как объекты C++. Они также могут быть реализованы в чистом JavaScript. Такие вещи называются скриптами, просто чтобы обратить внимание на то, что они, формально, делают функции платформы доступными. Концепция интерфейса в Mozilla следует концепциям интерфейсов в Java и Microsoft COM - некоторый набор (необязательно полный) функций и свойств, предоставляемых объектом.
Все объекты базового ПО с точки зрения скрипта одинаковы, но они приходят из разных мест. Вот некоторые из технологий, которые вносят свой вклад в создание таких объектов:
Все эти интерфейсы комбинируются в среде Mozilla. В частности, они все могут использоваться внутри одного окна, которое представляет собой корневую глобальную переменную объектной системы JavaScript. Вместе они создают многофункциональную среду программирования.
В Mozilla предусмотрена также поддержка JavaScript меньшего масштаба. Например, у системы настроек и системы установки есть собственные, независимые среды JavaScript, которые включают в себя очень маленькое число узкоспециализированных объектов базового ПО. Пользующиеся ими интерпретируемые скрипты отделяются от остальной части платформы и ее служб. В этой лекции такие среды называются изолированными интерфейсами.
Версии стандартов, описывающих
Если в Mozilla реализуется какая-то часть DOM, реализация этой функциональности точно такая, как предписано стандартом. Это очень просто.
Версии этих стандартов в формате File | Work .
Приложения к этим стандартам включают описания JavaScript-вариантов
интерфейсов, но эти описания довольно многословны. Синтаксис
Если вы понимаете синтаксис
Mozilla поддерживает DOM 0 только в HTML-документах. Многие доступные в XUL объекты были созданы как аналоги каких-то объектов DOM 0, так что этот стандарт можно использовать как руководство, поясняющее, чего можно ожидать в XUL.
У DOM версии 0 нет соответствующего стандарта W3C, и он
представляет собой раннюю попытку поддержки JavaScript только для
HTML. Содержание DOM 0 примерно эквивалентно доступным для скриптов
функциям в третьих версиях браузеров. Лучшая документация по этим
функциям находится на сайте Netscape’s DevEdge, где по-прежнему хранятся довольно старые
документы. Руководства к ранним версиям Navigator можно найти в
разделе "
DOM 0 относится к HTML, а не к XML или XUL. Как упоминалось раньше,
он предоставляет стандарт готовых объектов, которые доступны автору
скрипта после загрузки HTML-документа. Самый известный пример - объект
Image, который часто использовался в
Объекты DOM 0 часто точно соответствуют именам HTML-тегов;
например, у тега <input> есть объект InputElement или FormElement. В
XUL при
Mozilla полностью поддерживает DOM 1.
Среди множества предоставляемых стандартом DOM 1 функций в 90%
случаев для написания скрипта нужны только интерфейсы Node, NodeList, Element и Document. Важные свойства и методы этих интерфейсов
перечислены в таблице 5.3.
| Самые полезные функции стандарта DOM 1 | Интерфейс DOM | Описание |
|---|---|---|
parentNode |
Node |
Родительский тег по отношению к текущему |
childNodes |
Node |
Все теги-потомки текущего тега в виде NodeList |
|
Node |
Первый тег- |
nextSibling |
Node |
Следующий тег- |
Node insertBefore( |
Node |
Добавить тег или текст перед указанным тегом-потомком или текстом |
Node removeChild(existingNode) |
Node |
Удалить тег или текст из непосредственного потомка текущего тега |
Node appendChild( |
Node |
Добавить тег или текст в конец списка потомков этого тега |
String getAttribute(attString) |
Element |
Вернуть значение существующего атрибута или "" для текущего тега |
void setAttribute(attString, value) |
Element |
Добавить текущему тегу атрибут с определенным значением |
void removeAttribute(attString) |
Element |
Удалить из тега данный атрибут, если он существует |
Boolean hasAttribute(attString) |
Element |
Определить, существует ли указанный атрибут |
Element createElement(tagString) |
Document |
Создать тег; этот тег будет существовать отдельно от текущего документа |
Node createTextNode(value) |
Document |
Создать текст; этот текст будет существовать отдельно от текущего документа |
Element getElementById(idString) |
Document |
Вернуть тег с указанным идентификатором |
NodeList getElementsByTagName(tagString) |
Element, Document |
Вернуть все теги с указанным именем тега |
Node item(i) |
NodeList |
Вернуть i -й элемент списка |
length |
NodeList |
Вернуть число элементов в списке |
Так как интерфейсы Element и Document также являются Nodes, все
свойства и методы Node относятся и к этим интерфейсам.
Стандарт DOM 1 игнорирует опыт DOM 0 и начинает с чистого листа. Этот стандарт состоит из двух частей.
Первая часть очень общая и относится ко всем
Все в первой части DOM 1 объекты Node. Документы, теги, атрибуты и
содержимое тегов - объекты, являющиеся Node. Дерево
документов - дерево таких объектов, в корне которого находится объект
Document. Это дерево не имеет сведений о конкретных тегах (например, о
XUL-тегах). Поэтому здесь нет объекта <button>. Есть только обобщенный
объект, использующийся для всех тегов. И для XML, и для HTML, и для
XUL обработка одна и та же.
Существует два способа перемещаться по этому дереву. Первый
задействует систему запросов. Можно пользоваться эквивалентом document.all() от Microsoft из DOM 1 или расширением Mozilla:
document.getElementsByTagName("button"); // любой XML
document.getElementsByAttribute("class", "*"); // только XUL
Параметр * свидетельствует о том, что допустимы любые значения
атрибута. Другой способ - обходить все дерево явно, используя
определенные структуры данных и алгоритмы. Например, с помощью этого
кода можно переместиться в документе на два уровня вниз:
document.firstChild().firstChild().getAttribute("type");
В первой части стандарта DOM 1 также предоставляется полный набор функций для вставки, изменения и удаления XML-содержимого, но это должно выполняться путем создания объектов, а не вставки нужных кусков в XML-данные. Если изменение содержимого важно, создание объектов может оказаться длительным и сложным процессом. В Mozilla есть единственное расширение, позволяющее решать эти задачи быстрее - свойство innerHTML. Это улучшение создано как аналог соответствующему свойству в Internet Explorer и позволяет добавлять XML-содержимое прямо к содержимому тега. Несмотря на имя, это свойство можно использовать в XML и XUL наравне с HTML:
tag_object.innerHTML = '<box><button value="On"/></box>'; old_content = tag_object.innerHTML;
Mozilla не поддерживает расширения Internet Explorer innerText, outerHTML и outerText.
Вторая часть стандарта DOM 1 относится только к HTML. В ней
перечислены удобные методы и атрибуты для работы с HTML-документами. У
элемента DOM-дерева, соответствующего HTML-тегу, свойства будут
соответствовать HTML-атрибутам данного тега. Поэтому в дереве у тега <FORM> будет узел со свойствами action, enctype и target, помимо
прочих.
В этой части описываются также два других способа выбирать теги из общего набора:
document.getElementsByName('myform1');
document.getElementById('testid');
getElementById() также поддерживается для XUL и используется везде.
Пользоваться getElementsByName() не рекомендуется даже для HTML, так
как атрибут имени в HTML понемногу убирается из этих стандартов.
Подведем итог. DOM 1 позволяет преобразовать целый XML- или HTML- документ в одну большую полностью изменяемую структуру данных. Если это HTML-документ, атрибуты тегов отображаются как свойства объектов, соответствующих известным тегам. В любом случае можно легко выделить из этой структуры массивы похожих тегов. Интерфейсы DOM 1 можно назвать "каноническими" (общепризнанными).
Mozilla почти полностью поддерживает DOM 2.
DOM 2 заполняет многие пробелы в стандарте DOM 1. Это надмножество DOM 1. DOM 1 написан как один документ, а DOM 2 разбит на шесть. Это:
Первые два - то же, что и стандарт DOM 1. В них есть небольшие исправления, но эти же исправления есть и в последних версиях DOM 1. Кроме того, есть некоторые мелкие дополнения. Все, что есть в DOM 1, в DOM 2 называется "базовым интерфейсом". "Расширенный интерфейс" DOM 2 предоставляет несколько удобных функций, которыми можно пользоваться только в XML, но не в HTML. Это интерфейсы, дающие доступ к:
amp ; ;XSLT ;Стандарт DOM 2 также описывает известные события браузеров. Кроме
событий мыши ( click ) и конкретных событий HTML ( submit ) описываются и
общие события XML ( DOMFocusIn ). Есть и события, которые появляются во
время изменения keydown ). Стандарт также определяет перемещения событий по
дереву документа. Более подробно мы обсудим события в лекции 6,
"События".
Стандарт DOM 2 Style дополняет стандарт CSS 2, предоставляя доступ
к стилям из JavaScript. В DOM 1 лучшее, что можно сделать - это
изменить атрибут style у тега (встроенный стиль). Стандарт DOM 2
позволяет работать с правилами стилей так, как если бы они были
объектами, а не просто текстом. Он также позволяет проверять и менять
полностью каскадный стиль конкретного тега. Следующий код
document.getElementById("greeting").style.color ="red";
окрашивает часть текста в красный, независимо от существующего для него набора внешних и встроенных стилей. Однако этот интерфейс не поддерживается в XUL. Он поддерживается только в HTML.
Стандарт DOM 2 делает возможными операции
вырезания и вставки данных, выполняемые пользователем. Чтобы понять,
какие теги пользователь выбрал, требуется тщательный осмотр дерева
документа. Для такого осмотра нужны удобные способы прохода по дереву.
Именно они описываются в части , где вводятся новые объекты, и TreeWalker. В части Range создается набор объектов для
выделенных мышью тегов. В Mozilla технология выделения и вставки
мышью, там, где она существует, реализована с помощью этого стандарта.
Также в этой части описывается немного сложный процесс обработки
тегов. Теги можно разбивать на два, вставлять содержимое между тегами
и делать другие полезные вещи. Более простой способ вставлять
содержимое в
Стандарт DOM 2 в основном направлен на создание
нового программного обеспечения. Это же утверждение верно и для
стандарта DOM 2 Views. Его цель - предоставить интерфейсы, позволяющие
дереву документа отображаться сразу же в нескольких окнах. Самое
очевидное применение этой возможности - в HomeSite, где одновременно доступны предварительный просмотр, обзор
исходного кода и Views описывает необходимые для создания
такого редактора интерфейсы.
Из шести документов стандарта DOM 2 два почти копируют стандарт DOM 1, два очень полезны (события и стили), применение оставшихся двух менее очевидно. Стандарт DOM 2 можно называть стандартом, определяющим взаимодействие с пользователем в том смысле, что он предоставляет функции, специально предназначенные для обработки ввода и отображения страниц.
Mozilla поддерживает только небольшую часть стандартов DOM 3.
Это новые стандарты, но на момент написания книги они уже находились в стадии завершения. Большая часть описанной в них функциональности в браузерах пока недоступна. Вот пять частей этого стандарта:
В DOM 3
document.getInterface("SVG");
позволяет другим реализованным в браузере стандартам быть
доступными в скриптах. Это аналог use в Perl. Если поддержка
Второе важное изменение показано в следующем примере:
document.setNormalizationFeature("format-pretty-print");
Оно дает возможность дополнительного контроля над процессором XML.
Механизм обработки XML во время загрузки XUL- или HTML-файла принимает
множество решений, например, что делать с пробельными вставками и
когда сообщать об ошибках. После реализации DOM 3
Стандарт DOM 3 Events дополнительно описывает события клавиатуры
(также называемые текстовыми событиями), отсутствующие в DOM 2 Events.
Описывается большая часть клавиш, например, Alt, и F12.
Также вводятся перехватчики для обработчиков событий, что позволяет
запускать два обработчика событий для одного тега при возникновении
только одного события. Это единственная часть DOM 3, которую Mozilla
почти поддерживает.
Стандарт с длинным именем DOM 3
- еще одно двухчастное произведение. Часть
предоставляет полный доступ на чтение и запись к Load and Save описывает, как переходить от XML-текста к дереву
документа и обратно. Это стандарт, определяющий innerHTML.
DOM 3 добавляет к операциям над документами систему
ограничений. С помощью других стандартов DOM можно изменить документ
так, что он больше не будет соответствовать своему типу документов.
Например, с помощью интерфейсов DOM 1 можно добавить к содержимому
HTML-документа тег <, хотя это и не тег HTML. Стандарт DOM 3 проверяет или XSchema ) при каждом изменении документа с помощью DOM-интерфейсов.
Если какое-то изменение неявно нарушит правила типа документа, оно не
будет допущено. Наконец, DOM 3 описывает еще один метод прохода
через делает доступной в JavaScript систему .
Стандарты DOM 3 можно назвать "тяжелой артиллерией" среди всех
стандартов DOM. Вероятно, до их массового использования в XML-страницах
пройдет много времени. С другой стороны, они жизненно важны
для разработчиков приложений, работающих над инструментами высокого
уровня вроде визуальных
Сомнительно, чтобы в ближайшее время появился набор стандартов DOM 4, если он вообще будет когда-либо существовать.
Стандарты DOM предоставляют интерфейс DOMImplementation с методом hasFeature(). Этот метод может быть вызван из JavaScript, чтобы
выяснить, какая функциональность в Mozilla заявлена как
поддерживаемая. Вот подходящая для этого строка кода:
f = document.implementation.hasFeature("XML","1.0");
В базе данных ошибок Mozilla хранится старое обсуждение метода hasFeature(). В таблице 5.4 приведены результаты для Mozilla 1.0 и
1.4. Слово "FALSE" ("ложь") написано большими буквами лишь для
облегчения чтения.
| Функциональность | Версия | Результат hasFeature() |
|---|---|---|
HTML |
1.0 | true |
XML |
1.0 | true |
|
2.0 | true |
HTML |
2.0 | true |
XML |
2.0 | true |
Views |
2.0 | true |
StyleSheets |
2.0 | true |
CSS |
2.0 | true |
|
2.0 | true |
Events |
2.0 | true |
UIEvents |
2.0 | FALSE |
MouseEvents |
2.0 | true |
MouseScrollEvents |
Не описывается стандартом W3C | true |
MutationEvents |
2.0 | FALSE |
HTMLEvents |
2.0 | true |
Range |
2.0 | true |
|
2.0 | FALSE |
|
3.0 | true |
All other DOM 3 |
3.0 | FALSE |
Другие функции DOM 3
Результаты одинаковы для скриптов и в XUL, и в HTML, что делает
некорректным результат запроса со строкой "HTML" в XUL-документе. В
XUL есть также собственная поддержка так называемых KeyEvents, но hasFeature() еще не сообщает о них.
Результат false для какой-то конкретной функции - еще не конец
света. Если стандарт реализован полностью, за исключением какой-либо
одной функции, то это корректный ответ. Если вам известна
реализованная часть стандарта, вы все равно можете ею пользоваться.
Выявлять такие функции - дело чтения и поиска.
Если поддерживается какая-то часть DOM, то в скриптах доступны
соответствующие интерфейсы. Интерфейсы используются в языках
программирования. Однако есть и другие способы взаимодействия со
службами браузера. Два примера тому - XML и таблицы стилей. Браузер
может реализовать поддержку таблиц стилей, но не поддержку DOM, и
наоборот. Метод hasFeature() возвращает результаты только о поддержке
DOM.
Некоторая функциональность браузера может когда-нибудь сама попасть в стандарты DOM. Функции Mozilla 1.0, которые вероятнее всего привлекут внимание стандартов - поддержка перетаскивания мышью, события клавиатуры, события прокрутки и клавиатурные сокращения.
Отдельно от DOM 0 для HTML существует поддержка DOM 0 для XUL. У DOM 0 для XUL нет стандарта или основного документа, описывающего его структуру. Он не документирован, за исключением замечаний о некоторых тегах в этой книге.
DOM-объекты относятся к Document и переменные документа, указывающие на него.
Есть и другие объекты, которые могут быть полезны в скриптах, но не имеют ничего общего с документами: скажем, окно, в котором отображается документ. Может понадобиться изменить текст его заголовка, открыть модальный диалог или заменить отображаемый документ другим.
Эти объекты, не относящиеся к документам - часть объектной модели
браузера ( ), существующей в большинстве
браузеров. Эта модель по сравнению с DOM очень маленькая и состоит из
иерархии всего лишь нескольких объектов. В самом ее верху находится
так называемый объект Window, представляющий окно на рабочем столе, в
котором отображается документ. Переменная окна указывает на этот
объект. Небольшая иерархия будет знакома web-разработчикам и
содержит такие объекты:
navigator screen history location frames
В этой книге мы не будем надолго задерживаться на подобных объектах (в любой книге по JavaScript для web-страниц они подробно рассматриваются). Лишь в лекции 10, "Окна и панели", мы продолжим обсуждение объекта окна.
Платформа Mozilla - это не только браузер, но и основа для
приложений. При отображении XUL-документов более логично говорить об
объектной модели приложения ( Application ), чем о . Окна XUL также начинаются с переменной окна, самого верхнего
объекта в иерархии приложения, но соответствующий ему тип - окно , а не просто окно. На рисунке 5.1 показана иерархия приложения
для окна .
(рис 5.1) Объектная модель XUL-приложения MozillaКаждое слово на рисунке 5.1 - свойство объекта, само содержащее
объект, хотя иногда там может находиться null или строка. Иерархия,
начинающаяся с окна ( window ), присутствует всегда. Иерархия,
начинающаяся с Element, существует для каждого объекта Element DOM 1.
Темные ребра на рисунке показывают существование и для XUL, и для
HTML; светлые - только для XUL. Прерывистая линия означает, что объект
доступен, только если присутствуют защитные фильтры. Термины,
совпадающие с терминами
Краткая характеристика AOM-объектов, специфичных для XUL, звучит
так: аргументы ( ) передаются при открытии окна; содержимое
( content ) указывает на документ, содержащийся в теге <browser>
XUL-страницы; набор управляющих команд ( controllers ) существует или для
окна, или для одного тега; диспетчер команд ( commandDispatcher ) -
также часть ) отвечает за точку фокуса;
объект блока ( boxObject ) содержит всю информацию о фрейме конкретного
тега и для обычных XUL-тегов, и для особых тегов вроде <scrollbox> ; builder предоставляет механизм постройки дерева для тега <tree> ; база
данных ( database ) и источник данных ( ) - части системы
шаблонов XUL.
Кроме этих объектов в лекции 10, "Окна и панели", рассказывается и о некоторых менее явных. Многие из этих объектов связаны с общими механизмами отображения XUL- или HTML-документов.
Объект окна XUL очень похож на объект окна HTML. Например, у него
тоже есть свойство history. Такое положение дел несколько сбивает с
толку, так как подразумевается, что эти свойства что-то делают. А они
не делают ничего. Они существуют потому, что многие скрипты и XUL-документы,
составляющие приложение браузера, ожидают, что эти свойства
будут работать. В нормальных условиях (классический браузер), чтобы
эти свойства функционировали, необходима дополнительная поддержка в
скриптах. В обычных окнах XUL этой поддержки нет, если только XUL-приложение
не собирается копировать функциональность браузера. Как и в
случае HTML, все реализованные интерфейсы DOM напрямую доступны из
XUL. Свойства и методы объекта window.document - входные точки для
этих интерфейсов.
И HTML-, и XUL-окна обладают одним особым свойством - свойством Components. Оно является шлюзом для доступа к большинству уникальных
интерфейсов Mozilla. Эти интерфейсы мы и рассмотрим далее.
Стандарты DOM предоставляют интерфейсы, которые делают содержимое доступным из скриптов. Похожим образом XPConnect и XPCOM предоставляют интерфейсы, которые делают доступными из скриптов составные части самой платформы Mozilla.
XPCOM - полностью внутренняя часть платформы Mozilla. XPConnect - соединительное звено, превращающее XPCOM-объекты и их интерфейсы в объекты базового ПО для JavaScript. В лекции 16, "Объекты XPCOM", подробно описывается работа с этой системой с точки зрения приложения. В текущей лекции дается только обзор этих двух технологий.
С точки зрения программиста приложения, система XPCOM в основном
представляется объектом window.Components (см. рисунок 5.1). Свойства
этого объекта classes и interfaces - списки всех доступных
идентификаторов контрактов XPCOM-объектов и имена их интерфейсов.
Свойство manager - менеджер XPCOM-компонентов. Свойство results -
список всех исключений, которые могут быть выброшены, и значений
результатов, которые могут возвращаться. Результаты - 32-битные
числа.
Теоретически эти списки могут динамически меняться, но в стандартной платформе этого не происходит. Стандартная платформа регистрирует все доступные компоненты во время запуска, и большая часть этой избыточной регистрации проводится во время первого запуска платформы. Набор зарегистрированных компонентов может быть пересмотрен при запуске инструмента regxpcom (в Microsoft Windows - regxpcom.exe). Эта маленькая программа устанавливается вместе со стандартной платформой и необходима только при создании или установке новых XPCOM- объектов (например, целых модулей, а не просто объектов; см. лекцию 16, "Объекты XPCOM").
С точки зрения программиста приложений, система XPConnect, делающая
написанные на C/C++ компоненты доступными из JavaScript, почти
невидима. Чтобы выполнить свою задачу, система XPConnect опирается на
набор библиотек типов (с расширениями .xpt), которые хранятся в
подкаталоге components каталога установки платформы. XPConnect также
предоставляет доступ из скриптов к модулям браузера, интерфейсам DOM,
Java и Java-
Все XPCOM-компоненты, предоставляющие более одного XPCOM-
интерфейса, должны реализовывать интерфейс nsISupports. Этот интерфейс
предоставляет метод QueryInterface(), с помощью которого можно
находить другие поддерживаемые объектом интерфейсы.
Платформа Mozilla очень велика, и многие ее части разбиты на составные блоки, каждый из которых является XPCOM-объектом с какими-то собственными уникальными характеристиками. Не вся Mozilla разделена на такие блоки; в ней все еще есть анонимные куски, скрытые внутри платформы.
Существует, по крайней мере, тысяча XPCOM-компонентов, каждый из которых предоставляет один или несколько интерфейсов. Это огромное число компонентов и этот набор эквивалентны очень большой библиотеке классов. Такое количество компонентов создает трудности для новичков. На первый взгляд, доступные функции Mozilla кажутся никак не связанными между собой, однако это не так. Просто все необходимые знания за один присест получить не удастся, а структура предмета неочевидна. Поиск документации по всем компонентам Mozilla также непрост. По две страницы на компонент - итого две тысячи страниц. Когда-нибудь такая книга, может, и будет написана, но пока ее еще нет.
Чтобы найти подходящий компонент, придется приложить усилия. То, что нам в конце концов нужно - объект, который можно использовать для решения конкретной задачи. Не ко всем объектам необходимо обращаться напрямую через XPCOM-интерфейсы. Многие объекты существуют в других формах. Перед погружением в XPCOM задайте себе вопрос: может ли нужный вам объект появиться в одной из этих форм?
Date или Math ). Если да, загляните в стандарт Image и Option в HTML, XMLHttpRequest и SOAPService в HTML и XUL).Если же не остается ничего, кроме как искать XPCOM-объект, то вот что следует делать. Объекты создаются из XPCOM-интерфейсов и XPCOM-компонентов, реализующих эти интерфейсы. У интерфейсов есть имена; у компонентов есть идентификаторы контракта (они же и их имена). Для поиска этой информации подойдет любая из следующих стратегий:
Из всех этих стратегий наиболее результативными будут чтение этой книги и загрузка копии XPIDL-файлов и других кратких описаний.
Наконец, имена почти всех (но не всех) полезных интерфейсов начинается с префикса:
nsI
а идентификаторы контракта всех компонентов начинаются с префикса:
@mozilla.org/
После того, как вы найдете пару из нужных вам интерфейса и компонента, требуется создать объект. Делается это так.
Вот простой пример использования XPCOM-компонента. В языке Perl есть ключевое слово require, позволяющее одному скрипту на Perl загрузить содержимое другого скрипта. В JavaScript нет аналога этой инструкции, но в Mozilla для этого можно использовать компоненты. Посмотрим на пример в листинге 5.7.
var comp_name = "@mozilla.org/moz/jssubscript-loader;1";
var comp_obj = Components.classes[comp_name];
var if_obj = Components.interfaces.mozIJSSubScriptLoader;
var final_obj = comp_obj.createInstance(if_obj);
final_obj.loadSubScript("file:///tmp/extras.js");
Этот скрипт эквивалентен использованию с содержимым файла
/tmp/
Для создания объекта необходимы две вещи: имя компонента ( строка,
comp_name ) и интерфейс, который можно использовать ( имя свойства,
if_obj ). После получения объекта ( final_obj ) можно задействовать его
методы. В нашем случае метод loadSubScript() импортирует содержимое
файла .
Характерный для многих компонентов Mozilla комментарий в XPIDL-определении mozIJSSubScriptLoader объясняет, что загружаемый файл
JavaScript должен быть расположен на локальном компьютере. Краткие и
рассредоточенные пояснения обычны для компонентов Mozilla.
В листинге 5.7 для создания объекта использовался метод createInstance(). Это одна из основных альтернатив. Другая - метод getService(). Он используется, когда данный XPCOM-компонент реализован
так, чтобы предоставлять только единственный экземпляр объекта
(одиночный объект). Такие объекты могут содержать статическую или
глобальную информацию, которую нельзя синхронизировать между
различными экземплярами объекта. В листинге 5.7 строку с createInstance() можно заменить на следующую:
var final_obj = comp_obj.getService(if_obj);
Как определить, какой из методов следует использовать? Если
идентификатор контракта или имя интерфейса содержат слово "service" (в
любом регистре), пользуйтесь getService(), иначе - createInstance().
Эти методы можно вызывать и без аргументов. В этом случае у
создаваемого или возвращаемого объекта есть интерфейс nsISupports.
Затем с помощью данного интерфейса можно получить любой другой нужный
интерфейс. Чтобы проиллюстрировать эту ситуацию, мы можем изменить
листинг 5.7:
var anon_obj = comp_obj.createInstance(); var final_obj = anon_obj.QueryInterface(if_obj);
Если нужно создать несколько однотипных объектов, можно сначала создать конструктор, а потом с его помощью сгенерировать отдельные объекты. Следующий код заменяет первые четыре строчки листинга 5.7:
var Includer = Components.Constructor( "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader", null ); var final_obj = new Includer(); // var another_obj = new Includer()
Обратите внимание, что в этом случае имя интерфейса представляет
собой строку. Третий аргумент метода - необязательная
строка. Если она есть, она означает имя метода, который будет вызван
как метод инициализации при создании экземпляра объекта. Все
аргументы, переданные конструктору объекта Includer(), будут
передаваться этому методу инициализации.
Наконец, можно создавать объекты с XPCOM-интерфейсами прямо в JavaScript. Это можно делать только для простых интерфейсов, для которых не предусмотрена какая-либо особая обработка. Продолжая вариации на тему листинга 5.7, мы можем создать объект, используя код листинга 5.8.
var final_obj = {
// интерфейс nsISupports
QueryInterface : function(iid) {
var Ci = Components.interfaces;
if ( !iid.equals(Ci.nsISupports)
!iid.equals(Ci.mozIJsSubSCriptLoader )
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
// интерфейс mozIJSSubScriptLoader
loadSubScript : function (url) {
// здесь идет код для запуска скрипта
return;
}
};
Этот объект поддерживает и интерфейс nsISupports, и mozIJsSub-ScriptLoader.
Если бы мы могли быть уверены, что для этого объекта
никогда не будет вызван метод QueryInterface(), мы могли бы не
использовать часть объекта с nsISupports. Конечно, с этим объектом
связана одна большая проблема: как правильно реализовать второй
интерфейс? Эта реализация будет содержать длинный код, открывающий
файл в файловой системе, читающий его содержимое, передавая его . Для этого можно использовать уже существующую реализацию
объекта. Такое создание объектов вручную иногда очень полезно. Чаще
всего оно бывает необходимо при создании возвращаемых объектов для
разных отслеживающих функций, см. лекцию 6, "События".
Метод createInstance() сам реализуется интерфейсом nsIComponentManager ; getService() реализуется интерфейсом nsIServiceManager.
К модулям браузера можно обращаться из скриптов, начиная с третьих версий браузеров. В то время Netscape расширила версию 1.1 стандарта модулей NPAPI, чтобы сделать его доступным из скриптов, и для работы с ним были написаны сотни модулей. Mozilla до сих пор поддерживает этот стандарт, но чтобы все работало без заминок, нужны последние версии и платформы, и модулей (особенно это касается Macromedia's Flash).
Для модулей существуют наборы XPIDL-интерфейсов. С их помощью
отдельными модулями можно управлять прямо из JavaScript. По-прежнему
самый простой способ работать с модулями - пользоваться HTML-тегами <embed> (не рекомендуется) или <object> (рекомендуется) и массивом plugins DOM 0. Обсуждение комбинирования HTML и XUL можно найти в
лекции 10, "Окна и панели".
Наборы интерфейсов, доступных в скриптах, также существуют в
стандартах DOM, так что интерфейсы DOM можно получить и с помощью
объекта Components. Однако это не имеет особого смысла, так как такие
интерфейсы автоматически доступны для объекта window, а также для всех
DOM-объектов, созданных и полученных с помощью данного объекта.
Браузеры поддерживают доступ к Java-
В Netscape Communicator 4.x браузер полностью зависел от модели защиты Java 1.1. Чтобы браузер мог принять какое-то решение относительно защиты, нужно было послать запрос Java с целью проверить специальную библиотеку классов, предоставляемую Netscape. Java и JavaScript были связаны с помощью технологии LiveConnect.
В Mozilla этой зависимости от Java больше нет. Netscape отвечает за
свою защиту сам. Java не требуется и она уже не интегрирована в такой
степени в платформу. LiveConnect в его первоначальном смысле не
существует; есть только XPConnect. Интеграция, предоставляемая
LiveConnect, все еще существует, но она находится ниже XPCOM под
слоями кода. Явная поддержка Java в Mozilla теперь состоит из набора
XPCOM-интерфейсов, как и многое другое, а поддержка Java по статусу
считается близкой к модулям. Функциональность LiveConnect по-прежнему
проявляется, когда создаются
Mozilla предоставляет две технологии для адаптации Java. Первая -
OJI (Open Java Inteface, открытый интерфейс к Java). Это программный
интерфейс, напоминающий интерфейс NPAPI (для модулей), который
предназначен для работы с платформой Java любого производителя.
XPCOM-интерфейсы предоставляют доступ к OJI. Вторая технология - эмулятор
LiveConnect для nsIJRILLiveConnectPlugin. Этот эмулятор
обрабатывает C/C++-запросы Mozilla, которые раньше отправлялись
Netscape 4.x виртуальной машиной Java.
Как и в случае с модулями, по-прежнему простейший способ
использовать Java внутри Mozilla - теги <
Некоторые программисты, пришедшие в web-программирование, уже обладают знанием Java. Если у вас есть сложная Java-среда, которую предстоит интегрировать с Mozilla, будьте осторожны. С одной стороны, самые простые и очевидные взаимодействия между JavaScript и Java будут работать прекрасно. С другой стороны, JavaScript, Java и Mozilla - очень сложные системы, и идеальное их взаимодействие на любом уровне - глобальная цель. В случае более сложных взаимодействий по-прежнему возникают проблемы, и рекомендуется внимательно изучить ошибки, связанные с Java в базе данных ошибок, Bugzilla.
Лучше всего остановиться на реализации Java от Sun Microsystems и затем пользоваться только версией 1.4 или более поздней. Чтобы свести к минимуму вероятность нетривиальных ошибок, рекомендуется пользоваться последними версиями Mozilla.
За исключением некоторых замечаний в лекции 10, "Окна и панели", в этом курсе разговор о Java завершен.
Чтобы использовать чью-либо еще функциональность, Mozilla опирается на систему XPCOM, но существуют и небольшие библиотеки, написанные полностью на JavaScript. Эти библиотеки пытаются упростить взаимодействие с отдельными частями XPCOM-системы.
Самые полезные из этих библиотек - JSLib и RDFLib. На самом деле
RDFLib - лишь подмножество JSLib. JSLib упрощает работу с файлами и
папками. Ее часть RDFLib предназначена для облегчения работы с
источниками данных
Эту библиотеку можно загрузить с сайта http://jslib.mozdev.org. Ее
файлы написаны в кодировке ASCII с
Следует иметь в виду, что эти библиотеки обновляются нерегулярно. При их загрузке нужно обращать внимание на заголовки, чтобы понять, как давно они обновлялись.
Чтобы пользоваться библиотекой, необходимо включить файл верхнего
уровня jslib.js, предоставляющий полезные процедуры и константы. Самая
важная из этих процедур - функция include(), которая выполняет те же
функции, что и require в Perl (или #include в io (для
работы с файлами) и (для работы с
(рис 5.2) Зависимости между файлами в JSLib и RDFLibСкрипты, из которых выходят стрелки, требуют, чтобы скрипты, к
которым эти стрелки направлены, были загружены первыми. Файлы без
зависимостей не показаны. Очевидно, что библиотека разделена на две
основные части, которые в этой книге будут называться RDFLib и JSLib.
Существует две независимые реализации доступа к
Библиотека задействует метод window.Components., чтобы
создавать конструкторы объектов для XPCOM-компонентов (описание того,
как это делается, дано в разделе "Другие варианты создания объектов").
Кроме того, она реализует конструкторы объектов JavaScript с помощью
системы прототипов JavaScript. Конструктор на основе прототипов
используется для создания нового объекта JavaScript. Во время процесса
создания конструкторы XPCOM-объектов используются для задания свойств
нового объекта JavaScript. Каждое такое свойство хранит один XPCOM-
объект. Поэтому окончательный объект JavaScript содержит (или
использует) один или несколько XPCOM-объектов и является точкой
доступа к этим объектам или их "фасадом". Это очень простая и широко
распространенная методика.
Такие новые объекты JavaScript содержат код для проверки ошибок,
| Конструктор | Файл с исходным кодом | Использует объекты | Цель |
|---|---|---|---|
include() method |
jslib.js | Включать другие файлы .js | |
Dir(filePath) |
io/dir.js | FileSystem() |
Работать с папкой на локальном диске |
DirUtils() |
io/dirUtils | Находить каталоги, специфичные для установки | |
File(filePath) |
io/file.js | FileSystem() |
Работать с файлом на локальном диске |
FileSystem(filePath) |
io/filesystem.js | Работать с любым элементом файловой системы на локальном диске | |
|
io/ |
Записать или прочитать один |
|
RDFFile(file_url, |
io/ |
|
Записать или прочитать один |
Socket() |
network/socket.js | Создать и читать из сокета или писать в него | |
SocketListener() |
network/socket.js | Обеспечить |
|
|
RDFBase() |
Читать записи из |
|
RDFBase( |
Читать записи из |
||
RDFContainer(type ownerURI, subjectURI, |
RDFResource() |
Упорядочивать или создавать <Seq>, < или <alt>, используя записи |
|
RDFResource(type, ownerURI, subjectURI, |
RDFBase() |
Изменить или создать |
|
|
Проиграть звук, находящийся по этому адресу |
В таблице 5.5 аргумент - объект nsIRDFDataSource, не URL
Zip, определенный в файле zip/zip.js, выполняет
только часть работы, необходимой для извлечения файла из архива.
Почти все файлы из библиотеки JSLib снабжены хорошей документацией в виде предварительных комментариев. Эти комментарии демонстрируют, как пользоваться предоставляемыми объектами.
В JavaScript также доступны интерфейсы, предоставляемые XBL-определениями.
Эти определения включаются в XML-файл со свойством
стиля CSS 2 -moz-. Такая связь добавляет определение тегу или
тегам. Хотя XBL-определения могут включать какое-то содержимое, можно
создавать и определения, содержащие только функциональность -
интерфейс. Возможность добавлять любую функциональность к любому тегу
- очень мощная функция.
Если для получения объекта Element, соответствующего тегу с XBL-определением,
используется стандарт DOM 1, результирующий объект
JavaScript будет включать свойства и методы, подразумеваемые этим
определением. Этими свойствами и методами затем можно пользоваться как
обычно для объектов базового ПО. Платформа автоматически выполнит все
необходимые действия.
Таким образом, XBL-определения расширяют функциональность некоторых DOM-объектов. Самими XBL-определениями можно пользоваться и как XPCOM-компонентами, поэтому они доступны через XPConnect. Единственная причина делать это - если XBL-компонент должен быть доступен из кода на C/C++.
В лекции 15, "XBL-связки", XBL рассматривается более подробно.
В Mozilla существует три изолированных интерфейса, доступных из скриптов. У этих интерфейсов есть собственные интерпретаторы JavaScript и собственные глобальные объекты. Так как они изолированы от остальной части Mozilla, у них имеется собственный AOM.
Система настроек Mozilla - первый из этих интерфейсов. Ее AOM
состоит из единственного объекта PrefConfig, который играет роль
глобального объекта. У него есть всего два свойства, два метода: pref() и user_pref().
@mozilla.org/preferences-service;1 @mozilla.org/preferences;1
Когда к этим компонентам происходит обращение из обычной JavaScript-среды Mozilla, настройки можно менять, но файл prefs.js не может быть изменен напрямую. Изменения этих настроек записываются в файлы prefs.js во время завершения работы платформы.
Второй такой интерфейс - система сетевой установки компонентов Mozilla. В лекции 17, "Система распространения и установки - XPInstall", полностью описывается эта среда и ее AOM. Эта система также предоставляет XPCOM-компоненты для доступа из обычной среды, но эти интерфейсы низкоуровневые и мало полезны для решения обычных задач. Они позволяют уведомлять систему XPInstall о появлении содержимого откуда-нибудь еще (например, из Internet) и принимать это содержимое для обработки. Эти ограниченные интерфейсы свидетельствуют, что XPInstall стоит использовать только для процесса установки.
Третий изолированный интерфейс Mozilla - xpcshell. Это отдельная от
платформы Mozilla программа, недоступная в обычных стабильных сборках.
Она доступна в нестабильных сборках или если вы собираете Mozilla
самостоятельно (требует параметра --enable-debug ). xpcshell -
самостоятельный интерпретатор JavaScript, использующийся для
тестирования скриптов, интерпретатора JavaScript и системы
XPConnect/XPCOM. Его AOM состоит из единственного глобального объекта
и объектов, связанных с XPCOM. Самые полезные свойства глобального
объекта перечислены в таблице 5.6.
| Свойство | Описание |
|---|---|
Components |
Массив компонентов XPConnect/XPCOM, предоставляет доступ к большей части компонентов платформы Mozilla |
|
Преобразует аргумент в строку Unicode и отправляет ее необработанной на |
load(arg1,arg2,...) |
Пытается загрузить и интерпретировать указанные файлы (не URL) |
print(arg1,args2,...) |
Преобразует аргументы в строки и отправляет их, слегка отформатировав, на printf("%s") |
quit() |
Завершает работу xpcshell |
Массив Components делает xpcshell отличным местом для тестирования
скриптов, которые используют большое число компонентов Mozilla. Так
как xpcshell пользуется обычными stdin и stdout,
эту программу легко запускать в пакетном режиме автоматическими
тестовыми системами. xpcshell - это JavaScript-эквивалент
интерпретатора Perl, только он немногим больше программы
тестирования.
Теоретически скрипт, запущенный в xpcshell, может создавать и
использовать достаточно компонентов для "постройки" целого браузера.
Практически же исполняемый файл Mozilla выполняет во время
инициализации действия на низком уровне, которые xpcshell не
использует. Следовательно, xpcshell лучше оставить для более простых
задач.
В этом разделе мы добавим в окно редактирования ( Edit ) NoteTaker
несколько скриптов. Эти скрипты будут менять способ работы XUL-
документа после его отображения платформой. Во время работы мы будем
манипулировать несколькими типами объектов, описанных в этой
лекции.
Первое изменение NoteTaker будет касаться содержимого окна с
ключевыми словами. Это окно на самом деле поддерживает две отдельные
панели. Каждая панель представляется одним из тегов <toolbarbutton>: "Edit" и ". Мы бы хотели отображать окно редактирования так,
чтобы видимой была только одна из этих панелей. Так как содержимое
панели с ключевыми словами еще не определено, мы используем
"заполнитель".
Чтобы добавить поддержку двухпанельной системы, воспользуемся тегом <. Содержимое панели "Edit" будет появляться как одна карта
колоды, а содержимое панели "Keyword" будет другой картой. Ранее
содержимое, скрывающееся под двумя тегами <toolbarbutton>, было для
кнопки "Edit". Теперь мы хотим поместить это содержимое на одну карту,
а для другой используем что-нибудь вроде:
<hbox flex="1"> <description>Добавим потом.</description> </hbox>
На рисунке 5.3. показана структура XUL-документа до и после изменения.
(рис 5.3) Добавление <deck> в NoteTakerВсе, что мы сделали - добавили в нужные места < и новое
содержимое. Это можно было сделать и в лекции 2, "Проектирование с
XUL", однако переключаться между двумя картами < без скрипта
невозможно. Теперь у нас есть JavaScript, и мы можем реализовать
переключение. Слегка забегая вперед (в лекцию 6, "События"), мы
отметим, что теги <toolbarbutton> поддерживают обработчик событий onclick. Мы добавим скрипт так, чтобы с ним было интересно
повозиться.
Существует множество способов решить нашу задачу, но все они
сводятся к добавлению тегам атрибутов id ; написанию функции, которую
мы назовем action(), и добавлению обработчика onclick. Обработчики onclick выглядят примерно так:
<toolbarbutton label="Edit" onclick="action('edit')"/>
<toolbarbutton label="Edit" onclick="action('keywords')"/>
Новые идентификаторы будут такие:
<deck flex="1" id="dialog.deck"> <hbox flex="1" id="dialog.edit"> <hbox flex="1" id="dialog.keywords">
Функция action() ; приведена в листинге 5.9.
function action(task) {
var card = document.getElementById("dialog." + task);
if (!card || ( task != "edit" task != "keywords") )
throw("Unknown Edit Task");
var deck = card.parentNode;
var index = 0;
if ( task == "edit" ) index = 0;
if ( task == "keywords") index = 1;
deck.setAttribute("selectedIndex",index);
}
Мы хотим научиться передавать функциям аргументы, похожие на
команды, потому что это общая практика для приложения Mozilla. Если мы
включим этот код в наш XUL-файл, с тегом <script>, мы получим
непонятные ошибки, описанные в разделе "Отладка" этой лекции, если
только не будем осторожны и не воспользуемся содержимым <![CDATA[]]>.
Лучше всего поместить наш скрипт в отдельный файл с самого начала и
включить его примерно так:
<script src="editDialog.js"/>
Функция action() вызывает метод window.document.getElementById().
Этот метод - самая распространенная входная точка для доступа к DOM из
скриптов. Ей передается значение HTML- или XUL-атрибута id, и она
возвращает объект для тега с этим идентификатором. Далее мы можем
работать с этим объектом через его свойства и методы.
Оставшаяся часть функции проходит через тег < с помощью
свойства DOM parentNode и устанавливает там значение атрибута selectedIndex с помощью свойства DOM setAttribute(). Так как это
свойство имеет смысл для тега <, система отображения XUL
отреагирует на изменения автоматически, и отобразится нужная
карта.
Если мы не будем пользоваться аргументами, похожими на команды,
можно достигнуть того же эффекта быстрее, но не очень аккуратно.
Эквивалентные обработчики onclick, которые по-прежнему используют
интерфейсы DOM, выглядят так:
document.getElementById("dialog.deck").setAttribute("selectedIndex",0);
document.getElementById("dialog.deck").setAttribute("selectedIndex",1);
Независимо от того, какой из двух подходов выбрать, для решения
большей части задачи потребуются всего лишь три интерфейса стандарта
DOM 1 Document, Element и Node.
Использование < - лишь один из многих способов "оживить"
XUL-документ скриптами. Другой, не менее правильный способ - менять CSS-стили после загрузки документа.
Начнем с примера <, удалив его открывающие и <hbox>, можно
интерпретировать как HTML-тег <div>. В листинге 5.10 показана другая
версия метода action().
function action(task) {
var card = document.getElementById("dialog." + task);
if (!card || ( task != "edit" task != "keywords") )
throw("Unknown Edit Task");
var oldcard; // the content to remove
if (task == "edit")
oldcard = document.getElementById("dialog.keywords");
else
oldcard = document.getElementById("dialog.edit");
oldcard.setAttribute("style","visibility:collapse");
card.removeAttribute("style");
}
Здесь используются те же интерфейсы DOM, что и в листинге 5.9, но
меняются атрибуты в DOM-иерархии окна. При изменении правил стиля
содержимого система отображения внутри платформы автоматически
обновляет отображаемый документ. Использовать атрибут style не очень
удобно. В XUL есть более удобный атрибут , который можно
менять, не трогая при этом никакие другие атрибуты или встроенные
стили, которые может добавлять скрипт. Две последние инструкции
скрипта нужно заменить следующими строчками:
oldcard.setAttribute("collapsed","true");
card.removeAttribute("collapsed");
Атрибут XUL hidden не должен обновляться автоматически, поскольку
он может нанести вред XBL-связям (см. лекцию 15, "XBL-связки").
Кроме использования < и стилей, можно использовать
DOM-интерфейсы и для физического удаления/вставки частей в иерархию DOM
документа. Это третий подход, использующийся для скрытия и отображения
содержимого, но он слишком сложен для столь простой задачи.
При преобразовании XUL-документа в иерархию DOM предоставляется больше информации, чем предусмотрено стандартными интерфейсами DOM. Существует еще и набор интерфейсов XBL-системы Mozilla. Эти дополнительные интерфейсы добавляют свойства и методы специфичным для XUL DOM-объектам. Эти свойства и методы чрезвычайно удобны и упрощают написание скриптов. Иногда использование только стандартов DOM делает код менее простым и понятным.
Давайте посмотрим, можно ли с помощью одного из этих интерфейсов
улучшить наше решение с использованием тега <. Вероятнее всего,
тег, с которым мы свяжем наш скрипт, будет <. Для начала заглянем
в файл xul.css. Этот файл хранится в архиве
deck {
display: -moz-deck;
-moz-binding: url("chrome://global/content/bindings/
general.xml#deck");
}
Строка -moz- сообщает нам о том, что для этого тега есть
связь, так что нам есть на что посмотреть. Связь называется " и
описана в файле general.xml. Откроем этот файл и поищем такую
строку:
<binding id="deck">
Этого достаточно, связь найдена. Вот одна из частей ее определения:
<binding id="deck"> <implementation> <property name="selectedIndex" ...
Это все, что нам нужно. Имена свойства в XBL и имена атрибутов в
XUL обычно совпадают, более того, они обычно совпадают с именами HTML-
объектов. Свойство selectedIndex совпадает с атрибутом selectedIndex
тега <. Его мы и используем. В методе action() заменим строку
deck.setAttribute("selectedIndex",index);
на
deck.selectedIndex = index;
Это тривиальное изменение, но наш код становится достаточно
коротким, чтобы мы могли выкинуть переменную index и избавиться еще от
пары строк. Если бы в определении связки для < был бы, например,
метод setCard(), мы бы могли использовать его вместо написания action(). Последние строки нашей функции можно сократить так:
var deck = card.parentNode; if ( task == "edit" ) deck.selectedIndex = 0; if ( task == "keywords") deck.selectedIndex = 1;
Второе изменение, которое мы сделаем в NoteTaker - внесем внешние данные в отображаемое окно извне XUL-документа. То есть мы загрузим содержимое, которое будет появляться внутри прямоугольных областей. В дальнейшем мы еще изменим способ загрузки, сохранения и отображения информации несколько раз.
Чтобы получить доступ вовне, нам не нужны XPCOM-компоненты. Мы могли бы задействовать URL, указанный в лекции 7, "Формы и меню". Но здесь мы воспользуемся наборами строк.
Мы можем манипулировать наборами строк (файлами properties ) из XUL
или из JavaScript. Если мы выберем первый путь, будет то же, что и при
написании скрипта для <: мы добавим несколько тегов и поищем
XBL-определение, которое предоставляет полезные интерфейсы. Но сейчас мы
будем работать с наборами строк напрямую из JavaScript. Так как для
этого нужен XPCOM, нам придется хранить файлы внутри
Нам нужен объект, который умел бы работать с файлами properties.
Другими словами, нам требуется некий полезный XPCOM-интерфейс и
компонент, реализующий этот интерфейс. Если мы будем искать его в
XPIDL-файлах (или по индексу этой книги), легко заметить интерфейсы в
файле nsIStringBundle.nsIStringBundleService и nsIStringBundle. Так как в имени первого
встретилось слово "Service" (служба), это, вероятно, XPCOM-служба; для
нас это отправная точка. Вспомним, что службы создают объект с помощью getService() ; в остальных случаях используется createObject().
Мы также обратим внимание на то, что метод createBundle() этого
интерфейса выглядит так:
nsIStringBundle createBundle(in string aURLSpec);
Итак, этот метод создает объект с интерфейсом nsIStringBundle из
URL. У интерфейса nsIStringBundle есть метод getStringFromName(),
извлекающий строку из файла с набором строк. Нам не так важно, что в
XPIDL используются собственные типы wstring и string ; как нам
известно, XPConnect преобразует их во что-то, что JavaScript может
понять - обычное строковое значение, которое появится как объект String.
В файле интерфейса также в самом верху заявляется, что связанный с ним идентификатор контракта таков (теперь у нас получается пара из интерфейса и XPCOM-компонента):
@mozilla.org/intl/stringbundle;1 nsIStringBundleService
Нам не нужен идентификатор контракта для интерфейса nsIStringBundle, так как служба создаст объекты с этим интерфейсом для
нас, когда мы вызовем createBundle(). Но нам нужно сначала получить
объект службы. Так как мы нашли пару целиком, это легко:
var Cc = Components.classes; var Ci = Components.interfaces; var cls = Cc["@mozilla.org/intl/stringbundle;1"]; var svc = cls.getService(Ci.nsIStringBundleService);
Если в Консоли JavaScript никаких ошибок не появится (а их быть не
должно), тогда теперь переменная
var Cc = Components.classes;
var Ci = Components.interfaces;
var cls = Cc["@mozilla.org/intl/stringbundle;1"];
var svc = cls.getService(Ci.nsIStringBundleService);
var URL = "chrome://notetaker/locale/dialog.properties";
var sbundle = svc.createBundle(URL);
function load_data() {
var names = ["summary", "details", "chop-query", "home-page", "width",
"height", "top", "left"];
var box, desc, value;
for (var i = names.length-1; i>0; i--) {
value = sbundle.GetStringFromName("dialog."+ names[i]);
desc = document.createElement("description");
desc.setAttribute("value",value);
box = document.getElementById("dialog." + names[i]);
box.appendChild(desc);
}
}
Переменная sbundle содержит XPCOM-объект, указанный нашим URL. Файл
по выбранному нами URL должен соответствовать правилам файла
properties. Функция load_data() считывает строки из этого файла и
задействует DOM так, что тег <description value="строка"> добавляется
как содержимое в конец всякого блока-заглушки <box>. Обратите
внимание, что объект <description> строится и добавляется к блоку в
самом конце. Это более эффективно, чем пошаговое добавление новых
данных в существующую структуру DOM.
Этот код также подразумевает, что в XUL-документе в ожидаемых местах есть идентификаторы. Нам нужно добавить их туда вручную. Для этого требуется заменить каждую строку
<box class="temporary"/>
на что-то вроде
<box class="temporary" id="dialog.summary"/>
Наконец, мы вызовем функцию load_data() из другого обработчика
событий, который подсмотрели в лекции 6, "События": атрибут onload тега <window>. Таким образом, мы будем уверены, что документ уже будет
существовать к тому времени, когда мы начнем манипулировать им:
<window xmlns= "http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul" onload="load_data()>
Так как файл хранится в части
dialog.summary=My Summary dialog.details=My Details dialog.chop-query=true dialog.home-page=false dialog.width=100 dialog.height=90 dialog.top=80 dialog.left=70
Путь к этому файлу относительно корневого каталога
notetaker/locale/en-US/dialog.properties
Теперь диалог NoteTaker должен походить на снимок на риcунке 5.4.
(рис 5.4) Диалог NoteTaker cо строками, загружаемыми скриптомНам удалось успешно использовать XPCOM для взаимодействия с
ресурсами вне загруженного документа. Это важный шаг, даже если все,
что мы сделали -
Подведем итог проделанной работы: мы работали с DOM-, AOM-, XBL- и
XPCOM-объектами. JavaScript предоставляет полный доступ к загруженному
содержимому и, если наше приложение установлено в
JavaScript - некомпилируемый язык; следовательно, на долю программиста приходится довольно много работы по выявлению ошибок. Так как переменные, свойства и методы определяются во время выполнения, многие ошибки могут оставаться незамеченными до самой последней стадии разработки. Важно, чтобы у процесса тестирования и отладки была определенная структура и чтобы он не сводился к догадкам. В данном разделе описывается, какие инструменты можно для этого использовать.
Лучшая защита от ошибок - хороший стиль написания кода. Держите
свои скрипты отдельно от документов. Не забывайте о завершающих точках
с запятой и отступах, пользуйтесь содержательными именами переменных.
Всегда проверяйте аргументы, передаваемые функции, и возвращайте
какое-нибудь осмысленное значение. Всегда пользуйтесь блоками try,
если используемые интерфейсы Mozilla могут генерировать исключения.
Обратите внимание на советы о настройках в первой лекции, "Основные
концепции".
Метод объекта окна - очень полезное средство. При его
использовании можно запустить Mozilla с ключом -console, тогда
вспомогательная информация будет выводиться в отдельное окно и не
повлияет на собственные окна платформы. Этот вывод также можно
сохранить в файл журнала. В UNIX запуск с этим ключом возможен только
из командной строки. Если скрипты используют выполнение через
промежутки времени, либо создаваемые либо получаемые, такой вывод -
иногда единственный способ узнать, какой же на самом деле была
последовательность событий во время обработки.
Mozilla также поддерживает URL javascript:. Этот URL может
пригодиться для быстрой проверки состояния документа. Если документ
загружен в окно Навигатора (XUL-документы могут быть загружены так
наравне с web-страницами), такие URL можно использовать для проверки
содержимого документа. Это наиболее эффективно, когда пользователь
вводит в документ большое
javascript:var x=window.state.formProgress; alert(x);
Здесь alert() - небольшое окно, отображающее одну или несколько
строк текста. Эту инструкцию можно поместить куда угодно в скрипт,
связанный с окном Mozilla, таким образом вы получите базовую
информацию о состоянии среды скрипта и ее содержимого. alert() также
приостанавливает интерпретатор JavaScript; это неплохо при простой
обработке данных, но неудачно, если предполагается, что информация
должна обрабатываться скриптом за очень быстрое время (например, при
использовании потокового аудио/видео). alert() - простейший инструмент
отладки.
Контрольные точки - еще одно простое, но очень полезное средство
отладки. У каждого созданного объекта JavaScript есть метод watch().
Этот метод используется для добавления к свойствам объекта одной
скрытой функции. Синтаксис этой скрытой функции напоминает синтаксис " в Mozilla. Пример приводится в листинге 5.13.
function report(prop,oldval,newval) {
dump(prop + "old: " + oldval + "; new: " + newval);
return newval; // гарантирует, что отслеживаемый код еще выполняется
};
var obj = { test:"before" };
obj.watch("test",report);
obj.test = "after"; // вызов report()
obj.unwatch("test");
Аргументы функции предоставляются функцией watch(). При
отслеживании свойства test каждое его изменение в качестве побочного
эффекта приводит к вызову функции .
При снятии отслеживания этот побочный эффект исчезает. Это тоже тактика отслеживания изменений, когда они происходят.
Наконец, в любую часть скрипта можно вставить ключевое слово . При этом откроется Отладчик JavaScript, о командах которого
можно узнать, набрав /help в командной строке в нижней части окна.
Отладчик основывается на этой XPCOM-паре:
@mozilla.org/js/jsd/debugger-service;1 jsdIDebuggerService
Если не хочется использовать Отладчик JavaScript, можно реализовать
что-нибудь поверх этого компонента и его интерфейса jsdIDebuggerService для себя.
При возникновении непонятных проблем следует полностью закрыть
Mozilla, запустить ее заново и провести проверку снова. Нужно всегда
держать открытой Консоль JavaScript ( Tools | Web Development ) и всегда
очищать ее журнал перед загрузкой нового документа, чтобы ошибки были
заметны сразу. Время от времени необходимо проверять, не появились ли
"умершие" процессы Mozilla без окон, к которым их можно отнести.
Язык JavaScript невелик и прост в освоении. Он во многом похож на многие другие языки из семьи языков с C-подобным синтаксисом, за исключением уникальной работы с объектами. Цепочки областей видимости и прототипов - любопытные и сложные особенности языка, и на искусное создание кода, работающего с ними, можно потратить часы. В JavaScript 2.0 цепочки прототипов будут заменены классами.
По сравнению с основной частью языка, объем интерфейсов, используемых в JavaScript, очень велик. Интерфейсы варьируются от простых и удобных до совершенно непонятных и окружают Java, модули и поддержку ранних браузеров.
Опытные web-разработчики заметят, что поддержка DOM-интерфейсов в Mozilla проста в использовании, знакома и функциональна. Без дополнительных усилий она применима и к XUL-приложениям. Хорошая поддержка стандартов - настоящий праздник после многих лет проверок на совместимость с различными браузерами. Начинающим XML-программистам рекомендуется внимательно изучить первую часть стандарта DOM 1, пока понятия узла, элемента, документа и фабричного метода не уложатся в голове.
Инфраструктура, состоящая из XPConnect и XPCOM - новый мир Mozilla. Имея более тысячи компонентов (и интерфейсов), нельзя получить все сразу. Возможно, некоторые интерфейсы вам не придется задействовать никогда; использование других будет во многом зависеть от того, как сильно требуется изменить платформу и насколько амбициозно ваше приложение.
XPCOM предоставляет в распоряжение пользователя целый мир объектов и позволяет сделать программирование на JavaScript таким же развитым, как и с применением языков Java, C или C++. У всех XPCOM-интерфейсов есть XPIDL-определение; отыскав нужное, вы прочитаете его без труда. Обзор этих интерфейсов может дать некоторое представление обо всех возможностях Mozilla.
Но вместо того, чтобы погрузиться в XPCOM, мы сначала вернемся к миру интерфейсов и поговорим о пользовательском вводе.
JavaScript, несложный язык программирования с синтаксисом, напоминающим C-синтаксис, является существенной частью платформы Mozilla. Программы на JavaScript или их фрагменты называются сценариями, или скриптами. При добавлении скриптов XUL документы из документов, доступных только для чтения, становятся динамическими интерфейсами, которые могут выполнять какие-нибудь действия в ответ на команды пользователя. Написание скриптов - задача программиста, а не автора web-страницы или человека, предоставляющего для нее информацию. Приложения Mozilla могут разрабатываться только программистами.
В этой лекции описывается сам язык JavaScript. Также в ней дается обзор многих служб платформы Mozilla, которые доступны из скриптов JavaScript. В дальнейшем эти функции и службы будут рассматриваться более подробно. О JavaScript написано уже немало. В этой лекции нашего курса дается полное описание языка, хотя и очень сжатое.
Скрипты JavaScript, являющиеся частью приложения Mozilla, могут следовать одному или нескольким более традиционным стилям программирования в зависимости от амбициозности приложения.
Легкие приложения Mozilla содержат скрипты, похожие на скрипты web- страниц. В web-страницах такие скрипты часто добавляют к HTML- содержимому уже в самом конце разработки. Когда эти скрипты разрастаются, их иногда называют динамическим HTML. Но даже тогда они по сути дела просто переставляют части HTML-содержимого так, чтобы в целом все выглядело приятнее. Макросы, использующиеся в продуктах вроде Microsoft Word, такие же легкие.
В приложениях Mozilla
Очень сложные приложения Mozilla вроде среды разработки Komodo от
ActiveState содержат столько скриптов, что они перевешивают базовую
функциональность браузера платформы. В таких случаях JavaScript можно
сравнить с другими самостоятельными средами
Однако чаще всего программисты создают приложения средней сложности. XUL, JavaScript, XPCOM и функциональность платформы, доступная по умолчанию, комбинируются в конечное приложение. JavaScript "цементирует" все используемые технологии.
На рисунке в начале этой лекции показаны те части Mozilla, которые
наиболее важны для поддержки скриптов. Из этого рисунка видно, что
JavaScript равноудален и от пользователя, и от операционной системы.
Это так потому, что JavaScript и его скрипты - встроенные технологии,
скрытые внутри другого программного обеспечения. Скрипты редко
управляют Mozilla извне. Две самые важные части, XPConnect и DOM,
находятся глубоко внутри платформы Mozilla. Они предоставляют большое
число программных интерфейсов (API,
Простейший вариант применения JavaScript требует XML и тега <script>. И для XUL-, и для HTML-документов следующая
строчка изменит слова в строке заголовка окна документа:
<script> window.title = "Scripted Title"; </script>
Скрипты, подобные этому, могут менять любую часть отображаемого
окна Mozilla, в том числе и любую часть его содержимого. Такие скрипты
также неявно взаимодействуют и с внешним миром, в том числе с
Internet. Само по себе содержимое тега <script> очень важно и требует
специальной обработки, как и CSS-код внутри тега <style>.
Платформа Mozilla сама частично написана с использованием скриптов
javaScript. Такие скрипты часто можно найти в
Перед углублением в синтаксис языка стоит задать вопрос: почему выбран JavaScript? Ответ на этот вопрос дается ниже.
JavaScript - член семьи языков с C-подобным синтаксисом. Среди
самых известных его "родственников" C и
У этих языков похожий синтаксис и немного - структура. Все они
являются if. Некоторые из этих языков достаточно развиты и
имеют поддержку объектов. Объекты предоставляют структуры уровнем выше
процедурных шагов.
Качества, присущие JavaScript, делают этот язык (почти) уникальным членом данной группы. Он спроектирован как в перспективе самый доступный и легкий язык в своей семье. Он также создан, чтобы быть универсальным языком. Этот язык легко переносим, требует небольших ресурсов и не нуждается в поддержке традиционного компилятора. Это легкость делает его очень привлекательным для многих задач.
Код JavaScript запускается внутри интерпретатора, виртуальной
машины вроде
Так как JavaScript - довольно ограниченный язык, он во многом зависит от другого программного обеспечения (которое часто называют базовым). В этом проявляется сходство этого языка с C, который тоже мало что может без помощи своих библиотек stdio или их эквивалентов. Платформа Mozilla в виде набора библиотек и исполняемых файлов предоставляет базу для интерпретатора JavaScript.
Так как базовое ПО обычно достаточно велико, большая часть времени,
затраченная на написание JavaScript-скриптов, уходит на то, чтобы
выяснить, что может предложить базовое ПО. В Mozilla JavaScript играет
примерно ту же роль, что и Visual Basic for Applications (
Mozilla поддерживает стандарт
SpiderMonkey также поддерживает работу с более ранними версиями
JavaScript, в том числе несколько необычную версию 1.2. Эта версия
содержала множество новых функций, из которых многие так и не
приобрели популярность. При необходимости можно включить поддержку
более ранних версий, следуя подходу первых браузеров Netscape. В XUL-
приложениях всегда следует использовать последнюю доступную версию
языка. Для других
Вот правильный способ включать JavaScript-код в
<script type="application/x-javascript" src="code.js/>
Использование следующего варианта не приветствуется, поэтому лучше избегать его, хотя это тоже будет работать:
<script type="text/javascript" src="code.js/>
Для выбора конкретной версии JavaScript можно делать так:
<script type="JavaScript1.2" src="code.js/>
Наконец, следующий способ подразумевает, что код скрипта написан на языке JavaScript последней версии:
<script src="code.js/>
Для нового приложения Mozilla идеален синтаксис первого примера.
Последний вариант - полезная альтернатива, но ради аккуратности и
безупречности следует всегда добавлять атрибут type. У всех этих
примеров кодировка по умолчанию - encoding="UTF-8".
В лекции 2, "Проектирование с XUL", в разделе "Хороший стиль
кодирования на XUL" объясняется, почему JavaScript-код всегда должен
храниться вне
Организация Mozilla предоставляет и второй интерпретатор
JavaScript, написанный не на C, а на Java. Эта версия интерпретатора
называется Rhino и не используется и не собирается вместе с платформой
Mozilla; тем не менее, она доступна для загрузки. Она также совместима
со стандартом
В этом разделе описываются особенности языка JavaScript,
соответствующие стандарту
Здесь мы взглянем на текст, написанный на языке JavaScript. Так как
стандарт
JavaScript-скрипты Mozilla хранятся как обычные текстовые файлы. Содержимое этих файлов должно быть в кодировке UTF-8. Это значит, что символы из таблицы ASCII хранятся каждый в одном байте. Этого достаточно, если предпочтительный язык - английский, так как никакая особая подготовка не нужна. Можно просто пользоваться обычным текстовым редактором.
В некоторых системах задействуются и неиспользуемые значения ASCII
от 128 до 255, например, для разных символов языков Западной Европы $$\acute{e}$$
в слове $$r\acute{e}sum\acute{e}$$. Раньше эта традиция была удобным способом создавать
текст на других языках. Но это не соответствует правилам использования
UTF-8, которые требуют двух или более байтов для всех символов не из
таблицы ASCII. Вставка таких 8-битных европейских символов в скрипты
будет безуспешной. Символ $$\acute{e}$$ правильно кодируется в UTF-8 как "é", по
крайней мере, это то, что показывает простенький текстовый редактор,
не знающий о UTF-8.
Но даже у корректно закодированных многобайтовых символов UTF-8
ограниченное применение. Они могут появляться только внутри строковых
литералов JavaScript, в комментариях и иногда в именах переменных. Но
в общем рекомендуется придерживаться в коде программы ASCII-символов;
другие символы следует использовать только для данных. Для европейцев
использование для имен переменных символов таблицы Latin1 в целом
безопасно, если эти символы корректно закодированы в UTF-8. Чтобы
узнать подробнее о поддержке Unicode, см. раздел 7.6 стандарта
В строках по-прежнему можно работать с любыми Unicode-символами. Подробнее об этом рассказано в разделе "Типы данных".
JavaScript - язык, в котором визуальное форматирование большой роли
не играет, как и XML, и C, например. Одна инструкция не обязана
находиться на одной строке. В число распознаваемых 0A в шестнадцатеричном
представлении) и символ 0D в шестнадцатеричном
представлении). Напомним, что в Windows,
Комментарии в JavaScript пишутся в C-стиле. Поддерживаются как однострочные комментарии:
// Однострочный комментарий
так и многострочные:
/* комментарий на несколько строк */
Возможны любые комбинации комментариев и кода, но один
многострочный комментарий не может появиться внутри другого. В
JavaScript нет специальных комментариев, которые используются для
документации. Перед интерпретацией скрипты не проходят предварительную
обработку. для JavaScript не
существует.
Кирпичики, из которых строятся скрипты JavaScript, - инструкции.
Инструкция может завершаться точкой с запятой, но это не обязательно.
Есть только один случай, когда точки с запятой обязательны - между
тремя выражениями инструкции for(;;). В остальных случаях
интерпретатор автоматически будет предполагать, что точка с запятой
поставлена, даже если ее на самом деле нет. Следующие три инструкции
эквивалентны:
x = 5; x = 5 x = 5 // несмотря на комментарий, эта инструкция эквивалентна предыдущей
Возможность опускать точку с запятой нужна для того, чтобы упростить задачу разработчикам, привыкшим к стилю Visual Basic. Тем не менее, рекомендуется всегда использовать точку с запятой. Дело не только в том, что текст так становится понятнее, но и в том, что в будущих версиях JavaScript это может стать необходимостью.
Скриптам не нужна процедура main() или что-то подобное ей. Как и в
Perl, инструкции могут появляться вне функций и объектов начиная с
первой строки. В JavaScript также допустима инструкция, не делающая
ничего:
;
JavaScript поддерживает составные инструкции, оформляющиеся с
помощью фигурных скобок { и }, но они отличаются от своих аналогов в C
(см. раздел "Определение области видимости"). Составные инструкции
сами по себе в JavaScript не так уж полезны, хотя и
поддерживаются:
{ x = 5; y= 5; }
В этой лекции инструкция означает или одиночную инструкцию, завершающуюся точкой с запятой, или составную инструкцию без конечной точки с запятой.
В JavaScript поддерживаются следующие собственные типы данных:
Undefined Null Boolean Number String Object
Существует также скрытый безымянный тип данных, соответствующий 32- битным беззнаковым целым.
В JavaScript типы ассоциируются с элементами данных, а не со
структурами, хранящими эти данные (например, переменными). Можно
привести очень старую аналогию между переменными в программе и
коробками для обуви. По этой аналогии элемент данных соответствует
паре ботинок, а структура, хранящая ее (переменная) - обувной коробке.
В JavaScript информация о типе связывается с ботинками, вы не найдете
ее в коробке. Переменные, хранящие данные типа Boolean, Number или String, подразумевают обувную коробку, в которой находится
единственная пара ботинок определенного типа. Тип Object подразумевает
коробку, в которой может быть много пар, связанных вместе. Тип Null
подразумевает пустую коробку, а тип используется для коробки
с неопределенным содержимым.
Для определения типов можно пользоваться оператором typeof. Для
обычных данных JavaScript он вернет одну из следующих строк, также он
может вернуть другую строку, если тип данных определен в базовом
ПО:
"undefined" "boolean" "number" "string" "object" "function"
При этом если тип данных - Null, возвращается "object". Давайте по
очереди рассмотрим все эти типы.
Тип данных допускает только одно значение
(неопределенное), но литеральная константа для него отсутствует. У
глобального объекта (см. ниже) есть единственное свойство с именем , содержащим это значение. При необходимости это значение
можно генерировать с помощью оператора void или с помощью :
x = void 0; X = undefined;
Тип Null допускает только одно значение: пустое. Существует
литерал null, который может использоваться для опустошения переменной
или в сравнениях:
x = null;
Тип Boolean допускает два значения: истина и ложь. Литералами для
этих значений будут true и false соответственно. false не эквивалентен
0, как в C, но преобразование между этими двумя значениями настолько
незаметно, что мелкие различия обычно можно не учитывать.
x = true;
Тип Number позволяет хранить 64-битные двойной точности числа с
isNaN() и isFinite() соответственно. В
JavaScript для этих значений литералов нет, но у глобального объекта
есть свойства и , кроме того, у объекта Math имеется
несколько других полезных свойств:
POSITIVE_INFINITY NEGATIVE_INFINITY NaN MAX_VALUE MIN_VALUE
Эти свойства можно использовать в сравнениях. Для литералов чисел с
x = -3.141592654; y = 1.0e+23; z = 234.555555E-100;
Числа также можно записывать в шестнадцатеричном виде, указывая
префикс 0x или 0X, за которым должны следовать цифры от 0 до 9 и буквы
от A до F в любом регистре.
x = 0xFEFF;
Метод сравнивания нечисловых значений ( ) в JavaScript
соответствует рекомендациям
Так как
Но в некоторых ситуациях значение может на самом деле храниться как
нецелое число. Вот несколько примеров: если это число с десятичной
точкой; если в результате деления получается остаток; если вызывается
функция, результат которой - sin() );
или если в результате вычислений получается число, большее 2AAAA). Во
всех остальных случаях данные типа Number хранятся как целые.
Если число преобразовывается из целого в нецелое,
Тип String представляет последовательность символов Unicode,
хранящихся в кодировке
\b \t \n \v \f \r \" \' \\ and \x и \u
Выше представлены соответственно символы забоя, табуляции, перевода
строки, вертикальной табуляции, перевода страницы, 0xA0 ). Сюда также входят
символы ISO 8859 (
str = "hello, world\n";
Тип Object будет рассматриваться в отдельном разделе, посвященном
только ему.
В JavaScript предусмотрено
JavaScript - язык третьего поколения поэтому в нем доступны
определяемые пользователем переменные. Имена переменных должны
начинаться с буквы, подчеркивания или знака доллара ($). Последнего
следует избегать, так как он редко используется в коде, не
генерируемом автоматически. Ограничений на длину имен переменных нет.
Далее, после первого символа, в именах переменных можно использовать
буквы, цифры, подчеркивание и знак доллара. Имена переменных
my_variable x counter5 interCapName not$common _secret
Соглашения об именах рекомендуют использовать для имен констант только заглавные буквы, разделяя слова подчеркиванием (как в Java); имена с единственной заглавной буквой в начале следует использовать для конструкторов объектов; подчеркивание в начале имени должно сигнализировать о том, что переменная не предназначена для свободного применения.
Переменные не определены, если они не объявлены с ключевым словом var. Если они используются, не будучи объявленными, возникает
var x; var y = 2, z = 3.45; var product = x * y * z;
Каждая переменная JavaScript хранит один элемент данных. Этот
элемент - или простое значение или ссылка на объект. В разделе
"Объекты" объясняется также, что переменные являются и свойствами. В
JavaScript нет указателей и синтаксиса для явного использования
ссылок. Имена переменных не могут совпадать с зарезервированными
словами вроде if. Переменная с именем this - особая и всегда указывает
на текущий объект.
В JavaScript, как и в C, поддерживаются одномерные массивы, но их
размер может задаваться и неконстантным выражением. Массивы создаются
с помощью ключевого слова new, которое также используется при создании
самых разных объектов. Вот несколько вариантов
var arr1 = new Array(); // массив нулевой длины var arr2 = new Array(5); // массив из 5 элементов var arr3 = new Array(11,12,13); // массив из 3 элементов var arr4 = new Array(2,"red",true); // массив из 3 элементов
Все элементы массива не определены, если только их содержимое не
задано при создании массива. Каждый элемент массива может хранить
данные любого типа. Массив также можно создать с помощью литерала,
заключенного в квадратные скобки, [ ]. Следующие примеры совпадают с
предыдущими и часто предпочтительны, так как метод Array() выглядит не
очень красиво и часто вносит путаницу:
var arr1 = []; // массив нулевой длины var arr2 = [, , , , ,]; // массив из 5 элементов var arr3 = [11,12,13]; // массив из 3 элементов var arr4 = [2,"red",true]; // массив из 3 элементов
Литералы массивов могут быть вложенными, так что элементы массивов могут сами быть массивами:
var arr5 = [ 6, ["red","blue"], 8, [], 10];
На элементы массивов можно ссылаться по их индексам, которые
начинаются с 0. Свойство массива length - целое число, большее индекса
самого последнего элемента в массиве на один. Оно не равно числу
элементов массива и обновляется автоматически:
a[0]; // первый элемент массива a b[2]; // третий элемент массива b c.length; // на один больше самого большого индекса в c c[c.length-1]; // последний элемент массива c d[1][4]; // см. ниже
В последней строке предыдущего примера используется массив d, чей
второй элемент, d[1] сам является массивом. Следовательно, d[1][4] -
пятый элемент массива d[1].
Размер массивов не фиксирован. Свойство length можно менять, или присваивая ему какое-то значение, или определяя элемент с большим индексом.
arr1 = new Array(3); // length - 3 arr1.length = 5; // теперь length - 5; arr1[8] = 42; // теперь length - 9;
Массивы разреженные. В предыдущем примере при определении элемента с индексом 8 элементы между 5 и 8 не создавались, если, конечно, они не использовались в последующих инструкциях. Так как доступны индексы из всего диапазона 32-битных беззнаковых целых, зазоры между элементами могут быть очень большими.
Немного забегая вперед, скажем, что массивы также являются
объектами (типа Array ). Все объекты поддерживают синтаксис,
позволяющий им вести себя как массивам. При этом объект представляется
как массив без свойства length ; просто разрешается использовать
нотацию квадратных скобок. Такой синтаксис может быть полезен при
поиске свойств во всех объектных данных, но эта гибкость не дает
преимуществ массивам, только другим объектам, как видно из
примера:
obj.prop_name == obj["prop_name"] // корректно и всегда истина obj[1] != obj.1 // некорректный синтаксис
Синтаксис в первой строке справа от знака равенства полезен, если нужно создать свойство объекта, чье имя не может быть корректным именем переменной. Например:
obj["A % overhead"] = 20;
Маленькая
obj[12.35] == obj["12.35"];
Результатом этого примера будет задание свойства объекта, а не
элемента массива, так как индексов с 12.35 не является корректным именем
переменной. Мораль такова: при работе с индексами пользуйтесь простыми
вычислениями.
Выражения JavaScript довольно точно соответствуют правилам
выражений в C, C++ и Java и предоставляют возможности выполнять
арифметические и логические вычисления,
| Имя | Бинарный | Приоритет | Символ |
|---|---|---|---|
| Принудительно задать высший приоритет | Унарный | 0 | () |
| Литерал массива | Унарный | 0 | [] |
| Литерал объекта | Унарный | 0 | {} |
| Вызов функции | Унарный | 0 | () |
| Свойство объекта | 1 | . | |
| Элемент массива | Бинарный | 1 | [] |
| Литерал объекта | Унарный | 1 | {} |
| Создание объекта | Унарный | 2 | new |
| Удалить ссылку на свойство | Унарный | 3 | delete |
| Преобразовать в неопределенное | Унарный | 3 | void |
| Получить строку, соответствующую типу | Унарный | 3 | typeof |
| Пре- и постинкремент | Унарный | 3 | ++ |
| Пре- и постдекремент | Унарный | 3 | -- |
| Тот же знак | Унарный | 3 | + |
| Противоположный знак | Унарный | 3 | - |
| 32-битное побитовое отрицание | Унарный | 3 | ~ |
| Логическое отрицание | Унарный | 3 | ! |
| Умножение | 4 | * |
|
| Деление | 4 | / |
|
| Остаток от деления | 4 | % |
|
| Сложение, конкатенация | 5 | + |
|
| Вычитание | 5 | - |
|
| 32-битный сдвиг влево | 6 | << |
|
| 32-битный знаковый сдвиг вправо | 6 | >> |
|
| 32-битный беззнаковый сдвиг вправо | 6 | >>> |
|
| Совпадает с данным типом | 7 | instanceof |
|
| Совпадает со свойством объекта | 7 | in |
|
| Порядковые сравнения | 7 | < > <= >= |
|
| Равенство | 7 | == != |
|
| Строгое равенство | 7 | === !== |
|
| 32-битное побитовое И | 8 | |
|
| 32-битное побитовое |
9 | ^ |
|
| 32-битное побитовое ИЛИ | 10 | | |
|
| Логическое И | 11 | |
|
| 12 | || |
||
| Тернарный | 13 | ?: |
|
| Простое присваивание | 14 | = |
|
| Составное присваивание | 14 | *= /= %= += -= <<= >>= >>>= = ^= |= |
|
| Разделитель элементов списка | 15 | , |
Приоритет 0 - самый высокий. Для операторов с одинаковым
приоритетом JavaScript грубо следует принятым в C соглашениям о
вычислениях слева направо и справа налево. Он также поддерживает
упрощенное вычисление булевых выражений, то есть если выражение
состоит из нескольких операций и ||, они вычисляются слева направо
только до тех пор, пока не будет понятно, что окончательный результат
уже не изменится, а не до тех пор, пока не будет вычислено последнее
условие.
Одна область, в которой логика работы с булевыми выражениями
JavaScript ближе к Perl, чем к C - многозначная семантика. По этому
соглашению выражения с и || играют роль управляющих конструкций
наподобие ?:, а не обычных булевых выражений. Поэтому в инструкции
var x = flag y;
переменная x принимает значение y, если значение flag - true, иначе
она принимает значение false. В C переменная x как значение принимала
бы результат вычисления булевого выражения " flag и y ".
В математических выражениях смесь операндов типа Number, хранящихся
как целых и как нецелых, приведет к тому, что результат будет нецелым.
Попытка применить побитовые операции к нецелым числам приведет к тому,
что эти числа будут обрезаны до 32 бит в общем бесполезным способом.
Убедитесь, что побитовые операции выполняются только для тех операндов
типа Number, которые хранятся как целые.
В JavaScript поддерживаются
if (выражение) инструкция
if (выражение) инструкция else инструкция
while (выражение) инструкция
do инструкция while (выражение)
for (выражение; выражение; выражение) инструкция
switch (выражение) {
case выражение: инструкция; break;
case выражение: инструкция; break; // столько раз, сколько нужно
default: инструкция; break;
}
Аргументом switch() может быть что угодно, не только переменная.
Селекторами case также могут быть не только литералы. Следующие две if -конструкции эквивалентны:
if (a) инструкция else if (b) инструкция else инструкция
if (a) инструкция else {if (b) инструкция else инструкция}
Как и во многих языках с C-подобным синтаксисом, стоит опасаться
if -конструкциями, когда else -часть относится к последнему if
независимо от расстановки отступов; эта
Для инструкции for есть вариация, которая позволяет проходить по
свойствам объекта JavaScript. При этом просматриваются все свойства,
не являющиеся DontEnum (см. раздел 8.6.1 стандарта
for ( имя-переменной in объект ) инструкция
В JavaScript нет инструкции . В нем есть метки, чьи имена
соответствуют именам переменных, но они находятся в отдельном
пространстве имен. continue завершает текущую итерацию цикла; break
прерывает выполнение цикла или выходит из инструкции switch. Метки
можно использовать для прерывания нескольких вложенных друг в друга
циклов:
mylabel: инструкция; break; break label; continue; continue label;
В JavaScript есть также система исключений. Это не необязательный довесок, а часть ядра языка. Она перехватывает ошибки и исключения при исполнении.
try { инструкция; }
catch (переменная) { инструкция; }
finally { инструкция; }
Блоков catch может быть несколько. Блок finally необязателен. Для
генерации исключения используется throw - или внутри блока try или где
угодно:
throw выражение;
Созданное исключение может сравниваться с данными любого типа,
начиная от обычного числа и заканчивая сложным специально созданным
объектом. Чтобы имитировать исключения, генерируемые XPConnect-частью
платформы, нужно всегда генерировать 32-битное целое, желательно одно
из значений объекта Components.results.
Скрипты часто пишутся на скорую руку с целью быстро добиться
какого-то результата, да и try, так
как исключения никогда не должны появляться перед пользователем. Самый
простой и эффективный способ гарантировать это - поместить все в один
большой блок try на самом верхнем уровне.
Инструкция with рассматривается в разделе "Область видимости".
JavaScript поддерживает функции. Функции нетипизированы и могут
работать с переменным числом аргументов, как, например, printf() в C.
Функции могут не иметь имени, тогда они анонимны, см. ниже. В
листинге 5.1 показана типичная функция.
function sum(x, y) {
if (arguments.length != 2) {
return void 0;
}
return x + y;
}
var a = sum(2,3); // a = 5
var b = sum(1,2,3); // b = undefined
var c = sum("red","blue"); // c = "redblue"
var d = sum(5, d); // d = 5 + undefined = NaN
var e = sum; // e теперь функция
var f = e(3,4); // f = 7
Объект аналогичен объекту Array за исключением того, что
он статичен: при добавлении к нему элементов свойство length не
изменится. Этот объект содержит все аргументы, переданные функции.
Функции также могут быть анонимными:
var plus = function (x,y) { return x + y; }
var a = plus(2,3);
Достоинство анонимных функций заключается в том, что они не создают автоматически дополнительную переменную с именем функции. Следовательно, можно создавать методы для объектов без "шлейфа" глобально определенных имен функций. Глобально определенных имен функций также можно избежать, поместив определение именованной функции в выражение:
var five = (function sum(a,b){return a+b;})(2,3);
Если функция вызывается сама по себе, а не как метод объекта, тогда
смысл ключевого слова this выясняется в соответствии с правилами
определения области видимости.
JavaScript поддерживает регулярные выражения Perl5 с некоторыми
мало заметными отличиями. Эти неявные отличия существуют, так как
синтаксис регулярных выражений очень чувствителен к малейшим
неточностям, находится в постоянном развитии и еще до конца не
устоялся. В UNIX-системах есть такие варианты регулярных выражений:
для работы с файлами, обычный и расширенный. Perl и JavaScript
поддерживают egrep(1) или параметру "Wildcards" окна поиска в
Microsoft Word. Написанное в справочной странице ( man(1) ) Perl, perlre, понять легче, чем текст определения , но ненамного.
Поищите какое-нибудь руководство в Internet.
Все операции над регулярными выражениями в JavaScript - методы
объекта String или они не существуют самостоятельно, как
оператор m// в Perl:
match(re) // "red".match(/e/) == ["e"]; replace(re,string) // "red".replace(/e/,"o") == "rod"; replace(re,function) // "red".replace(/e/,myfn); search(re) // "red".search(/e/) == 1; split(re) // "red".split(/e/) == ["r","d"];
replace() возвращает строку; search() - число; и
возвращают массивы строк.
У регулярных выражений есть синтаксис для использования литерала,
который может появиться в любой части кода, где также может появиться
строковый литерал. Такой литерал автоматически преобразовывается в
объект . Этот синтаксис таков:
/выражение/флаги
Здесь выражение - любое запутанное регулярное выражение; флаги -
ноль или более число g (заменять везде), i (не обращать внимания на
регистр) и m (в многострочных целях рассматривать каждую строку
отдельно).
В языке JavaScript есть объекты, но в версии 1.5 его еще нельзя назвать полностью объектно-ориентированным. Описание реализации многих концепций объектно-ориентированных систем в JavaScript приведено в таблице 5.2.
| Концепция объектных систем | Поддержка в синтаксисе | Легкость в использовании |
|---|---|---|
| Хорошая | Легко | |
| Включение | Хорошая | Легко |
| Делегация | Хорошая | Несложно |
| Недостаточная | Несложно | |
| Наследование | Достаточная | Сложно |
| Скрытие данных | Недостаточная | Несложно |
| Интерфейсы | Нет | Сложно |
| Позднее связывание | Хорошая | Легко |
| Основанность на объектах | Хорошая | Легко |
| Ориентация на объекты | Нет | Сложно |
| Нет | Сложно | |
| Определение типов во время исполнения | Хорошая | Легко |
| Шаблоны | Нет | Сложно |
В JavaScript для всех объектов и их атрибутов реализуется позднее
связывание. Попытка написать полностью объектно-ориентированный код -
технический трюк, которого лучше избегать. JavaScript создан для
простого манипулирования объектами, а не для создания сложных (или
вообще каких-либо)
В JavaScript 1.5 нет определений класса; есть только
В JavaScript элемент данных с типом Object обладает набором
свойств, составляющих содержимое объекта. Некоторые свойства содержат
функции для работы над этим объектом, называющиеся методами объекта.
Свойства особого глобального объекта также называются переменными и
функциями. Почти все в JavaScript - свойство какого-нибудь объекта. В
этом языке нет скрытия данных: в терминах C++ и Java все свойства
публичные.
У всех свойств есть атрибуты, но эти атрибуты - незаметная черта
языка, и для работы с ними нет собственного синтаксиса. Можно
прочитать об атрибутах в стандарте
Чтобы создать собственный объект, можно воспользоваться или оператором new, или литералом объекта, как показано в листинге 5.2.
// явное создание
var obj = new Object;
obj.foreground = "red";
obj.background = "blue";
// создание с помощью литерала
var obj = { foreground:"red", background:"blue" }
Чтобы добавить к объекту метод, нужно просто задать функцию или анонимную функцию как значение свойства объекта. Анонимные функции также могут появляться в литералах объектов, как показано в листинге 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();
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");
Это решение можно улучшить и дальше. После
Parents.prototype.lastname = "Smith";
Parents.prototype.ring = function (){ dial(123456789); };
Тогда при создании family1 и family2 получат помимо трех свойств из
тела функции еще и дополнительные свойства из прототипа, то есть всего
пять свойств. Так что у них не только будет собака ( dog ) по кличке
), но и фамилия у них обоих будет Смит ( Smith ), и номер
телефона у них тоже будет один и тот же. В действительности свойства и ring - общие для обоих объектов. Если у одного из них
изменится свойство , это новое значение заменит свойство прототипа, и тогда значения данного свойства у объекта и
прототипа более не будут общими. Если же изменится свойство
прототипа, тогда оно изменится и у всех объектов, для которых это
свойство общее. Такие свойства отличаются от свойств, заданных как dog
в нашем примере - это свойство уникально для каждого создаваемого
объекта.
Цель такой системы - позволить объекту быть тщательно
смоделированным один раз (с помощью объекта-прототипа) и затем иметь
возможность использовать эту модель в конструкторе при создании копии.
Действия конструктора можно ограничить, чтобы он работал только с
параметрами создания нового объекта и вызывал все необходимые для
инициализации методы. К несчастью, система прототипов не до конца
отвечает требованиям
См. также раздел "Цепочки прототипов" в этой лекции.
Объекты базового программного обеспечения существуют вне JavaScript
в самом базовом ПО, в которое встроен интерпретатор. В Mozilla такие
объекты обычно написаны на C++. Некоторый код на C, связанный с
интерпретатором JavaScript, находит их по мере надобности, создает
простой внутренний интерфейс, напоминающий объект JavaScript,
связывает с этим
Объекты базового ПО для программиста выглядят так же, как и обычные
объекты, если только не пытаться превратить их обратно в код с помощью toString(). Так как функции и методы в JavaScript - тоже объекты, эта
разница действительна и для них. Например, следующий фрагмент кода
пытается получить тело функции alert() (в XUL это метод объекта
базового ПО типа ChromeWindow ):
var str = "" + alert;
Результирующая строка, однако, показывает, что у alert() нет
исходного текста на JavaScript:
"\nfunction alert() {\n [native code]\n}"
Интерпретатор 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 соответствуют
"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().
Объект хранит String.
Объект Function представляет функции и методы. Синтаксис его
конструктора несколько странен. Почти всегда вместо new Function
рекомендуется использовать анонимные функции или синтаксис с
применением ключевого слова function.
Объект Error сообщает об ошибках во время исполнения скрипта и об
исключениях, которые не были перехвачены в блоках try или finally. Для
программистов он не очень полезен, так как можно просто заглянуть в
консоль JavaScript и найти там ту же информацию.
При изучении этих объектов имеет смысл пользоваться стандартом
X
описываются в разделе 15 под заголовком "Properties of the X Math (см. следующий раздел).
При запуске интерпретатора JavaScript некоторые объекты доступны и
без написания какого-либо кода. Если это собственные объекты
интерпретатора, то они называются встроенными объектами. Объекты
базового ПО также могут быть доступными до написания кода. Такая
автоматическая инициализация для удобства выполняется всегда. Яркими
примерами могут служить объекты Global, Math и document.
Глобальный объект находится наверху иерархии включения объектов.
Это корневой объект в JavaScript. Он не является свойством никакого
другого объекта и не может быть создан без отдельной и независимой
среды исполнения. В Mozilla объект Window (в HTML) и объекты ChromeWindow (в XUL) - глобальные объекты. Они реализованы так, что
обладают свойством window. Это свойство ссылается на глобальный объект
(получается цикл). Программисты в своих скриптах пользуются этим
объектом window как корневым.
Объект Math также создается при запуске среды исполнения
JavaScript. На него ссылается свойство глобального объекта с именем Math. Это допускает следующий сокращенный синтаксис для математических
операций:
var one = Math.sin(Math.PI/2);
Если документ загружается в окно Mozilla, процесс загрузки
автоматически сделает доступными множество дополнительных объектов.
Эти объекты знакомы web-программистам по
window.document.form3.username.value = "John";
Явное использование префикса window. необязательно. Эквивалентные
префиксы - this и self.
По сравнению с HTML у XUL довольно ограниченный набор автоматически
создаваемых объектов. В XUL для
Синтаксис определяет не только внешний вид кода, но и то, как интерпретатор JavaScript будет обрабатывать ваш скрипт. В этом языке используется несколько совершенно новых концепций.
Приоритет операторов указан в таблице 5.1. Правила вычислений слева направо и справа налево примерно такие же, как и в C, C++ и Java.
Все аргументы функций и методов передаются по ссылкам, за
исключением булевых, численных, неопределенных и null. В этих случаях
аргументы передаются по значению (копируются).
JavaScript автоматически преобразовывает данные между всеми
простыми типами и объектами Number, String и Boolean. Преобразование
типов выполняется так, чтобы выражение могло быть вычислено в любом
случае. Каждый объект в JavaScript обладает методами toNumber() и toString(), которые можно задействовать по мере надобности. Приведение
типов не требуется; оно выполняется в соответствии с расширенным
набором правил стандарта
Правило 1 существует потому, что содержимое строки может быть
некорректным числовым литералом. Тогда JavaScript вернет значение
и, в худшем случае, может аварийно завершить работу:
var str = "123stop45"; var x = str * 3; // str не число.
Аварийное завершение работы происходит только в случае
синтаксических ошибок или ошибок во время исполнения. Чтобы уберечься
от таких вещей, необходимо явно использовать функции parseInt() и parseFloat():
var x = parseInt("123stop45") * 3;
Второе правило существует потому, что операторы сравнения (<, == и т.д.) и оператор + для строк и чисел перегружаются. Правила, определяющие, считать ли оба операнда строками или числами, неочевидны и имеют разный смысл для разных операторов сравнения и конкатенации. Если вы не уверены в результате, следуйте первому правилу.
Область видимости определяет, какие переменные, объекты и свойства доступны в некоторой части кода. У этого процесса в JavaScript есть две стороны.
Первая сторона - традиционное определение области видимости переменных. Оно не отличается от своих аналогов в C и C++, где переменные могут быть локальными для функции или глобальными. В JavaScript в функции у локальных переменных могут быть такие же имена, как и у переменных вне функции. При интерпретации функции используется локальная переменная. При интерпретации инструкций вне функции используется глобальная переменная.
В C и C++, кроме того, каждая составная инструкция (блок) образует
собственную область видимости - внутри блока. То есть область
видимости переменной, объявленной внутри набора инструкций,
заключенных между { и }, отличается от области видимости переменных,
объявленных вне этого блока. В JavaScript такой блок с инструкциями не
создает собственной области видимости. Такой эффект в этом языке
отсутствует.
В C и C++ переменные, объявленные в середине области видимости, действительны далее, начиная с этого места. В JavaScript переменные, объявленные в середине функции (или где-нибудь в области глобальной видимости), действительны для всей области видимости. Следующий фрагмент кода, некорректный для C/C++, корректен в JavaScript:
function f() {
alert(x); // выдаст "undefined"
var x = 42;
alert(x); // выдаст "42";
}
f();
Другая сторона определения области видимости в JavaScript заключается в том, что в этом языке присутствует необычное понятие цепочек областей видимости. Цепочка областей видимости - это упорядоченный список объектов, в конце которого находится глобальный объект. При вызове функции интерпретатор сначала находит соответствующую функцию: для этого он просматривает объекты в цепочке областей видимости. Выполняться для этой функции будет первый найденный объект, у которого существует метод с таким же именем, как и у вызываемой функции. Этот механизм позволяет обработчикам событий на web-страницах вызывать функции, взяв за основу объект окна, даже если текущий объект какой-то другой. Цепочка областей видимостей делает доступными службы от разных объектов в одно и то же время.
Инструкция with в JavaScript временно добавляет объекты в цепочку
областей видимости. В листинге 5.5 показано, что функция toString()
используется несколько раз и каждый раз - для нового объекта. В то же
время переменная myflag всегда находится в объекте window, так как ни
у одного из этих объектов нет свойства myflag.
// цепочка областей видимости = window
var myflag = "Test String";
var x = toString(); // "[object ChromeWindow]"
with (document) {
// цепочка областей видимости = document, window
x = toString(); // "[object HTMLDocument]"
x = new Object;
with (x) {
// цепочка областей видимости = x, document, window
var y = toString(); // "[object Object]"
var x = myflag; // window.myflag
}
}
Цепочки областей видимости - дальние родственницы таблиц виртуальных функций в C++, реализующих наследование, но размеры цепочек областей видимости не фиксированы, и эти цепочки не реализуют наследование объектов в привычном понимании. Для программиста цепочки областей видимости полезны, но о них обычно не стоит задумываться.
В C, C++ и Java для данных используются два варианта размещения в
памяти: в стеке или в свободной памяти (или new или malloc(). В JavaScript нет стека, по крайней
мере, нет стека, с которым программист мог бы работать. Память всегда
выделяется из кучи. Ни для элементов массивов, ни для аргументов
функций не гарантируется последовательное размещение. Стеки
используются для реализации передачи аргументов функциям в Mozilla
SpiderMonkey, но этот механизм не виден программисту на
JavaScript.
JavaScript, как и Java - язык, в котором отсутствует понятие указателей, но есть механизм автоматической очистки памяти (сборка мусора). На каждый запущенный экземпляр среды исполнения JavaScript приходится один сборщик мусора. Объекты одного окна могут содержать ссылки на объекты других окон. При уничтожении окна все определенные пользователем объекты JavaScript помечаются как "мусор". Сборщик мусора JavaScript не запускается в отдельном потоке, как это сделано в Java.
JavaScript обладает возможностью интерпретировать новый код во
время выполнения. Самый удобный способ сделать это - применить метод глобального объекта. может использоваться для выполнения
любого корректного кода JavaScript:
var x = 5; var code = "x = x + 1;"; eval(code); // x = 6 x = x + 1; // x = 7
Другие аналогичные методы не связаны с интерпретацией напрямую. В
Mozilla это методы и , очень ограниченные parseInt(), parseFloat() и URL javascript:. Более подробно о механизме
интерпретации нового кода во время выполнения рассказано в разделе
"Использование XPCOM-компонентов".
JavaScript поддерживает замкнутые выражения. Это демонстрируется в следующем примере:
function accumulate(x) {
return function (y) { return x + y };
}
var subtotal = accumulate(2);
var total = subtotal(3); // total == 5
Каким будет возвращаемое значение при вызове анонимной функции?
Если бы при завершении области действия функции значение
аргумента x очищалось, она бы не участвовала в вычислениях при вызове .
Решение проблемы - замкнутые выражения. Замкнутое выражение - это набор данных, которые нужно сохранить и по завершении функции. Упрощенно это копия переменных из данной области действия, ссылающихся на другие объекты. Эти копии возвращаются, следовательно, данные, на которые они ссылаются, не теряют свою последнюю ссылку, когда исходные объекты очищаются. Таким образом, по завершении области действия сборщик мусора обходит эту группу созданных объектов стороной. Замкнутые выражения для программиста прозрачны. Их наличие имеет смысл для любого языка, поддерживающего вычисления во время выполнения.
Возможно, самая сложная особенность JavaScript - система
прототипов. Она довольно компактно описана в разделе 4.2.1 третьей
редакции стандарта
На самом деле у каждого объекта в JavaScript, включая те, которые
сами выступают как прототипы, есть объект-прототип с именем __proto__.
Поэтому следующий код совершенно корректен:
MyConstructor.prototype.__proto__.value = 5;
Такой набор прототипов, указанный явно или нет, называется цепочкой
прототипов. Эта цепочка заканчивается объектом Object., у
которого нет собственного прототипа. Все звенья этой цепочки добавляют
свойства к конечному создаваемому объекту. Сама цепочка представляет
собой упорядоченный список звеньев, именно в этом порядке свойства
будут добавляться. Одним из следствий является эффект затенения, когда
свойства, добавленные позже, могут переписывать значения добавленных
раньше свойств с теми же именами.
С помощью прототипов можно реализовывать объектно-ориентированное наследование. Самый просто способ это сделать - заменить прототип новым "базовым классом", то есть новым объектом-прототипом. Как видно из листинга 5.4, это можно сделать так:
function Family() { }; // Какой-то конструктор.
Family.prototype = new Parents; // Новый базовый "класс".
Свойство прототипу можно задать и из конструктора, учитывая то, что
различные вариации на тему общих, уникальных и
Приложения Mozilla время от времени пользуются системой прототипов, чтобы создавать собственные объекты. Обычно это делается, когда нужно создать несколько объектов заданного типа. Если явно создавать несколько объектов с помощью литералов объектов, получится слишком многословный код, поэтому обычно создается один конструктор объектов, который и используется для создания объектов.
Интерпретатор Mozilla SpiderMonkey толерантно относится к тому, что составляет корректную инструкцию:
x++ % y++;
Это поведение соответствует стандарту, но другие реализации JavaScript не поддерживают его.
Возможно, самым полезным расширением стандарта является
особенность, позволяющая методам JavaScript get и set быть связанными
со свойством объекта. Другими словами, побочным эффектом при чтении
или задании значения свойства будет вызов целой функции. Данная
функция должна реализовывать и стандартное действие, которое
происходит при чтении или записи свойства. В листинге 5.6
демонстрируется работа этих расширений.
var obj = {
get foo () { effect1(); return this._real_foo; },
set foo (val) { effect2(); return this._real_foo = val;},
_real_foo: "bar"
}
var x = obj.foo; // вызывается effect1() и x = "bar"
obj.foo = "zip"; // вызывается effect2() runs и _real_foo = "zip"
В этом примере свойство _real_foo хранит действительное значение
свойства , которое существует только как интерфейс. В терминах
стандарта [[Get]] и [[Put]], и это будут такие функции, которые
вам нужны.
Mozilla также предоставляет некоторые функции для управления перечисленными выше методами. Это
_defineGetter__("имя-свойства", объект-функция);
_defineSetter__("имя-свойства", объект-функция);
_lookupGetter__("имя-свойства");
_lookupSetter__("имя-свойства");
Первые две функции делают то же, что и функции get и set из
предыдущего примера. Следующие две функции возвращают
Mozilla также поддерживает свойство __parent__, которое влияет на
внутреннее свойство [[ объектов-функций. В JavaScript в Mozilla
это свойство также доступно и всем собственным объектам и объектам
базового ПО.
Наконец, Mozilla предоставляет свойство __proto__, с помощью
которого можно явно задавать или читать внутреннее свойство [[, описанное в стандарте.
Четвертая редакция
Некоторые из предложенных в этом черновике улучшений присутствуют в SpiderMonkey 1.5, но поддержка нового стандарта скоро будет закончена. Начать изучение особенностей четвертой редакции можно на сайте http://www.mozilla.org/js/language/.
Простое расширение из четвертой редакции - поддержка констант:
const varname = 5;
В Mozilla есть настройка javascript.options., которой можно
задать значение true. Ее также можно найти в настройках, связанных с
отладкой, если используется тестовая версия платформы. Это не то же
самое, что режим строго соответствия из черновика стандарта; это
просто дополнительная проверка корректности кода в соответствии с
третьей редакцией стандарта. Разработчикам рекомендуется ею
пользоваться.
Планируется, что SpiderMonkey 2.0 будет соответствовать четвертой редакции стандарта.
Платформа Mozilla запускает интерпретируемые скрипты на компьютере пользователя, что в принципе может быть небезопасно. Платформа предоставляет разные функции обеспечения защиты, и некоторые из них относятся к JavaScript. Более подробно о защите рассказано в лекции 16, "Объекты XPCOM".
Скрипт выдаст ошибку, если во время работы будет превышено довольно
большое число допустимых возвращающих инструкций - 4194304. В
сущности, возвращающая инструкция - любая часть выполняемого скрипта,
которая является возвратом функции, новой итерацией цикла или
прерыванием области действия из-за исключения. Такая ситуация
рассматривается как атака с целью вызвать
Тем не менее, скрипт все равно может выполнять долгую, ресурсоемкую
обработку - достаточно просто разделить работу на несколько частей и
выполнять каждую из них, используя методы и . Таким образом, между выполнением частей скрипта будут
небольшие паузы, в которые введенные пользователем данные могут
обрабатываться, а значит, предупреждающее окно не появится.
Основная стратегия защиты в Mozilla - принцип "с того же сайта". Скрипт, загруженный с одного сайта (домен и путь) не может взаимодействовать с документом или ресурсом другого сайта (менее точный путь). Таким образом, окна Mozilla остаются не зависящими друг от друга. При попытке записи в переменную в окне, куда доступ запрещен из соображений защиты, приводит к генерации исключения (ошибки).
От ограничений из соображений защиты освобождаются скрипты,
хранящиеся в . Самое большое ограничение - свойство Components
объекта Window или ChromeWindow полностью доступно. Это свойство -
дорожка к XPCOM-компонентам Mozilla. Ни один из этих компонентов не
доступен извне . Некоторые из этих объектов лежат в основе
какой-либо функциональности, доступ к которой осуществляется другим
способом. Пример: объект, реализующий объект Form, доступный в HTML-
документах.
скрипты JavaScript могут быть подписаны с помощью технологии
.
Как рука в перчатке, интерпретатор JavaScript и базовое программное
обеспечение, в которое он встроен, работают вместе. В этом разделе
описывается вклад в это
Как и в C, в JavaScript нет операций ввода-вывода. Весь ввод и весь вывод осуществляются через объекты базового ПО. Нужна, по крайней мере, простейшая часть базового ПО, чтобы предоставить эти объекты.
Если основные функции ввода/вывода предоставляются, скрипт может включать другие скрипты откуда-нибудь еще. Теоретически такие скрипты могут использоваться для создания очень больших программ. Это похоже на модульную среду Perl, но в JavaScript такая практика не очень распространена. В JavaScript ожидается, что основная часть функциональности программы будет реализована в объектах базового ПО. Поэтому изучать синтаксис и семантику этого языка очень просто, по сравнению с изучением обширных библиотек объектов.
Объекты базового ПО - концепция JavaScript. Хотя такие вещи появляются внутри скрипта как объекты, службам базового ПО, скрывающимся за ними, совсем необязательно быть объектами. В случае Mozilla функциональность, доступная в скриптах, обеспечивается платформой, которая состоит из набора интерфейсов. Многие из них неотличимы от объектов, и многие из них на самом деле реализованы как объекты C++. Они также могут быть реализованы в чистом JavaScript. Такие вещи называются скриптами, просто чтобы обратить внимание на то, что они, формально, делают функции платформы доступными. Концепция интерфейса в Mozilla следует концепциям интерфейсов в Java и Microsoft COM - некоторый набор (необязательно полный) функций и свойств, предоставляемых объектом.
Все объекты базового ПО с точки зрения скрипта одинаковы, но они приходят из разных мест. Вот некоторые из технологий, которые вносят свой вклад в создание таких объектов:
Все эти интерфейсы комбинируются в среде Mozilla. В частности, они все могут использоваться внутри одного окна, которое представляет собой корневую глобальную переменную объектной системы JavaScript. Вместе они создают многофункциональную среду программирования.
В Mozilla предусмотрена также поддержка JavaScript меньшего масштаба. Например, у системы настроек и системы установки есть собственные, независимые среды JavaScript, которые включают в себя очень маленькое число узкоспециализированных объектов базового ПО. Пользующиеся ими интерпретируемые скрипты отделяются от остальной части платформы и ее служб. В этой лекции такие среды называются изолированными интерфейсами.
Версии стандартов, описывающих
Если в Mozilla реализуется какая-то часть DOM, реализация этой функциональности точно такая, как предписано стандартом. Это очень просто.
Версии этих стандартов в формате File | Work .
Приложения к этим стандартам включают описания JavaScript-вариантов
интерфейсов, но эти описания довольно многословны. Синтаксис
Если вы понимаете синтаксис
Mozilla поддерживает DOM 0 только в HTML-документах. Многие доступные в XUL объекты были созданы как аналоги каких-то объектов DOM 0, так что этот стандарт можно использовать как руководство, поясняющее, чего можно ожидать в XUL.
У DOM версии 0 нет соответствующего стандарта W3C, и он
представляет собой раннюю попытку поддержки JavaScript только для
HTML. Содержание DOM 0 примерно эквивалентно доступным для скриптов
функциям в третьих версиях браузеров. Лучшая документация по этим
функциям находится на сайте Netscape’s DevEdge, где по-прежнему хранятся довольно старые
документы. Руководства к ранним версиям Navigator можно найти в
разделе "
DOM 0 относится к HTML, а не к XML или XUL. Как упоминалось раньше,
он предоставляет стандарт готовых объектов, которые доступны автору
скрипта после загрузки HTML-документа. Самый известный пример - объект
Image, который часто использовался в
Объекты DOM 0 часто точно соответствуют именам HTML-тегов;
например, у тега <input> есть объект InputElement или FormElement. В
XUL при
Mozilla полностью поддерживает DOM 1.
Среди множества предоставляемых стандартом DOM 1 функций в 90%
случаев для написания скрипта нужны только интерфейсы Node, NodeList, Element и Document. Важные свойства и методы этих интерфейсов
перечислены в таблице 5.3.
| Самые полезные функции стандарта DOM 1 | Интерфейс DOM | Описание |
|---|---|---|
parentNode |
Node |
Родительский тег по отношению к текущему |
childNodes |
Node |
Все теги-потомки текущего тега в виде NodeList |
|
Node |
Первый тег- |
nextSibling |
Node |
Следующий тег- |
Node insertBefore( |
Node |
Добавить тег или текст перед указанным тегом-потомком или текстом |
Node removeChild(existingNode) |
Node |
Удалить тег или текст из непосредственного потомка текущего тега |
Node appendChild( |
Node |
Добавить тег или текст в конец списка потомков этого тега |
String getAttribute(attString) |
Element |
Вернуть значение существующего атрибута или "" для текущего тега |
void setAttribute(attString, value) |
Element |
Добавить текущему тегу атрибут с определенным значением |
void removeAttribute(attString) |
Element |
Удалить из тега данный атрибут, если он существует |
Boolean hasAttribute(attString) |
Element |
Определить, существует ли указанный атрибут |
Element createElement(tagString) |
Document |
Создать тег; этот тег будет существовать отдельно от текущего документа |
Node createTextNode(value) |
Document |
Создать текст; этот текст будет существовать отдельно от текущего документа |
Element getElementById(idString) |
Document |
Вернуть тег с указанным идентификатором |
NodeList getElementsByTagName(tagString) |
Element, Document |
Вернуть все теги с указанным именем тега |
Node item(i) |
NodeList |
Вернуть i -й элемент списка |
length |
NodeList |
Вернуть число элементов в списке |
Так как интерфейсы Element и Document также являются Nodes, все
свойства и методы Node относятся и к этим интерфейсам.
Стандарт DOM 1 игнорирует опыт DOM 0 и начинает с чистого листа. Этот стандарт состоит из двух частей.
Первая часть очень общая и относится ко всем
Все в первой части DOM 1 объекты Node. Документы, теги, атрибуты и
содержимое тегов - объекты, являющиеся Node. Дерево
документов - дерево таких объектов, в корне которого находится объект
Document. Это дерево не имеет сведений о конкретных тегах (например, о
XUL-тегах). Поэтому здесь нет объекта <button>. Есть только обобщенный
объект, использующийся для всех тегов. И для XML, и для HTML, и для
XUL обработка одна и та же.
Существует два способа перемещаться по этому дереву. Первый
задействует систему запросов. Можно пользоваться эквивалентом document.all() от Microsoft из DOM 1 или расширением Mozilla:
document.getElementsByTagName("button"); // любой XML
document.getElementsByAttribute("class", "*"); // только XUL
Параметр * свидетельствует о том, что допустимы любые значения
атрибута. Другой способ - обходить все дерево явно, используя
определенные структуры данных и алгоритмы. Например, с помощью этого
кода можно переместиться в документе на два уровня вниз:
document.firstChild().firstChild().getAttribute("type");
В первой части стандарта DOM 1 также предоставляется полный набор функций для вставки, изменения и удаления XML-содержимого, но это должно выполняться путем создания объектов, а не вставки нужных кусков в XML-данные. Если изменение содержимого важно, создание объектов может оказаться длительным и сложным процессом. В Mozilla есть единственное расширение, позволяющее решать эти задачи быстрее - свойство innerHTML. Это улучшение создано как аналог соответствующему свойству в Internet Explorer и позволяет добавлять XML-содержимое прямо к содержимому тега. Несмотря на имя, это свойство можно использовать в XML и XUL наравне с HTML:
tag_object.innerHTML = '<box><button value="On"/></box>'; old_content = tag_object.innerHTML;
Mozilla не поддерживает расширения Internet Explorer innerText, outerHTML и outerText.
Вторая часть стандарта DOM 1 относится только к HTML. В ней
перечислены удобные методы и атрибуты для работы с HTML-документами. У
элемента DOM-дерева, соответствующего HTML-тегу, свойства будут
соответствовать HTML-атрибутам данного тега. Поэтому в дереве у тега <FORM> будет узел со свойствами action, enctype и target, помимо
прочих.
В этой части описываются также два других способа выбирать теги из общего набора:
document.getElementsByName('myform1');
document.getElementById('testid');
getElementById() также поддерживается для XUL и используется везде.
Пользоваться getElementsByName() не рекомендуется даже для HTML, так
как атрибут имени в HTML понемногу убирается из этих стандартов.
Подведем итог. DOM 1 позволяет преобразовать целый XML- или HTML- документ в одну большую полностью изменяемую структуру данных. Если это HTML-документ, атрибуты тегов отображаются как свойства объектов, соответствующих известным тегам. В любом случае можно легко выделить из этой структуры массивы похожих тегов. Интерфейсы DOM 1 можно назвать "каноническими" (общепризнанными).
Mozilla почти полностью поддерживает DOM 2.
DOM 2 заполняет многие пробелы в стандарте DOM 1. Это надмножество DOM 1. DOM 1 написан как один документ, а DOM 2 разбит на шесть. Это:
Первые два - то же, что и стандарт DOM 1. В них есть небольшие исправления, но эти же исправления есть и в последних версиях DOM 1. Кроме того, есть некоторые мелкие дополнения. Все, что есть в DOM 1, в DOM 2 называется "базовым интерфейсом". "Расширенный интерфейс" DOM 2 предоставляет несколько удобных функций, которыми можно пользоваться только в XML, но не в HTML. Это интерфейсы, дающие доступ к:
amp ; ;XSLT ;Стандарт DOM 2 также описывает известные события браузеров. Кроме
событий мыши ( click ) и конкретных событий HTML ( submit ) описываются и
общие события XML ( DOMFocusIn ). Есть и события, которые появляются во
время изменения keydown ). Стандарт также определяет перемещения событий по
дереву документа. Более подробно мы обсудим события в лекции 6,
"События".
Стандарт DOM 2 Style дополняет стандарт CSS 2, предоставляя доступ
к стилям из JavaScript. В DOM 1 лучшее, что можно сделать - это
изменить атрибут style у тега (встроенный стиль). Стандарт DOM 2
позволяет работать с правилами стилей так, как если бы они были
объектами, а не просто текстом. Он также позволяет проверять и менять
полностью каскадный стиль конкретного тега. Следующий код
document.getElementById("greeting").style.color ="red";
окрашивает часть текста в красный, независимо от существующего для него набора внешних и встроенных стилей. Однако этот интерфейс не поддерживается в XUL. Он поддерживается только в HTML.
Стандарт DOM 2 делает возможными операции
вырезания и вставки данных, выполняемые пользователем. Чтобы понять,
какие теги пользователь выбрал, требуется тщательный осмотр дерева
документа. Для такого осмотра нужны удобные способы прохода по дереву.
Именно они описываются в части , где вводятся новые объекты, и TreeWalker. В части Range создается набор объектов для
выделенных мышью тегов. В Mozilla технология выделения и вставки
мышью, там, где она существует, реализована с помощью этого стандарта.
Также в этой части описывается немного сложный процесс обработки
тегов. Теги можно разбивать на два, вставлять содержимое между тегами
и делать другие полезные вещи. Более простой способ вставлять
содержимое в
Стандарт DOM 2 в основном направлен на создание
нового программного обеспечения. Это же утверждение верно и для
стандарта DOM 2 Views. Его цель - предоставить интерфейсы, позволяющие
дереву документа отображаться сразу же в нескольких окнах. Самое
очевидное применение этой возможности - в HomeSite, где одновременно доступны предварительный просмотр, обзор
исходного кода и Views описывает необходимые для создания
такого редактора интерфейсы.
Из шести документов стандарта DOM 2 два почти копируют стандарт DOM 1, два очень полезны (события и стили), применение оставшихся двух менее очевидно. Стандарт DOM 2 можно называть стандартом, определяющим взаимодействие с пользователем в том смысле, что он предоставляет функции, специально предназначенные для обработки ввода и отображения страниц.
Mozilla поддерживает только небольшую часть стандартов DOM 3.
Это новые стандарты, но на момент написания книги они уже находились в стадии завершения. Большая часть описанной в них функциональности в браузерах пока недоступна. Вот пять частей этого стандарта:
В DOM 3
document.getInterface("SVG");
позволяет другим реализованным в браузере стандартам быть
доступными в скриптах. Это аналог use в Perl. Если поддержка
Второе важное изменение показано в следующем примере:
document.setNormalizationFeature("format-pretty-print");
Оно дает возможность дополнительного контроля над процессором XML.
Механизм обработки XML во время загрузки XUL- или HTML-файла принимает
множество решений, например, что делать с пробельными вставками и
когда сообщать об ошибках. После реализации DOM 3
Стандарт DOM 3 Events дополнительно описывает события клавиатуры
(также называемые текстовыми событиями), отсутствующие в DOM 2 Events.
Описывается большая часть клавиш, например, Alt, и F12.
Также вводятся перехватчики для обработчиков событий, что позволяет
запускать два обработчика событий для одного тега при возникновении
только одного события. Это единственная часть DOM 3, которую Mozilla
почти поддерживает.
Стандарт с длинным именем DOM 3
- еще одно двухчастное произведение. Часть
предоставляет полный доступ на чтение и запись к Load and Save описывает, как переходить от XML-текста к дереву
документа и обратно. Это стандарт, определяющий innerHTML.
DOM 3 добавляет к операциям над документами систему
ограничений. С помощью других стандартов DOM можно изменить документ
так, что он больше не будет соответствовать своему типу документов.
Например, с помощью интерфейсов DOM 1 можно добавить к содержимому
HTML-документа тег <, хотя это и не тег HTML. Стандарт DOM 3 проверяет или XSchema ) при каждом изменении документа с помощью DOM-интерфейсов.
Если какое-то изменение неявно нарушит правила типа документа, оно не
будет допущено. Наконец, DOM 3 описывает еще один метод прохода
через делает доступной в JavaScript систему .
Стандарты DOM 3 можно назвать "тяжелой артиллерией" среди всех
стандартов DOM. Вероятно, до их массового использования в XML-страницах
пройдет много времени. С другой стороны, они жизненно важны
для разработчиков приложений, работающих над инструментами высокого
уровня вроде визуальных
Сомнительно, чтобы в ближайшее время появился набор стандартов DOM 4, если он вообще будет когда-либо существовать.
Стандарты DOM предоставляют интерфейс DOMImplementation с методом hasFeature(). Этот метод может быть вызван из JavaScript, чтобы
выяснить, какая функциональность в Mozilla заявлена как
поддерживаемая. Вот подходящая для этого строка кода:
f = document.implementation.hasFeature("XML","1.0");
В базе данных ошибок Mozilla хранится старое обсуждение метода hasFeature(). В таблице 5.4 приведены результаты для Mozilla 1.0 и
1.4. Слово "FALSE" ("ложь") написано большими буквами лишь для
облегчения чтения.
| Функциональность | Версия | Результат hasFeature() |
|---|---|---|
HTML |
1.0 | true |
XML |
1.0 | true |
|
2.0 | true |
HTML |
2.0 | true |
XML |
2.0 | true |
Views |
2.0 | true |
StyleSheets |
2.0 | true |
CSS |
2.0 | true |
|
2.0 | true |
Events |
2.0 | true |
UIEvents |
2.0 | FALSE |
MouseEvents |
2.0 | true |
MouseScrollEvents |
Не описывается стандартом W3C | true |
MutationEvents |
2.0 | FALSE |
HTMLEvents |
2.0 | true |
Range |
2.0 | true |
|
2.0 | FALSE |
|
3.0 | true |
All other DOM 3 |
3.0 | FALSE |
Другие функции DOM 3
Результаты одинаковы для скриптов и в XUL, и в HTML, что делает
некорректным результат запроса со строкой "HTML" в XUL-документе. В
XUL есть также собственная поддержка так называемых KeyEvents, но hasFeature() еще не сообщает о них.
Результат false для какой-то конкретной функции - еще не конец
света. Если стандарт реализован полностью, за исключением какой-либо
одной функции, то это корректный ответ. Если вам известна
реализованная часть стандарта, вы все равно можете ею пользоваться.
Выявлять такие функции - дело чтения и поиска.
Если поддерживается какая-то часть DOM, то в скриптах доступны
соответствующие интерфейсы. Интерфейсы используются в языках
программирования. Однако есть и другие способы взаимодействия со
службами браузера. Два примера тому - XML и таблицы стилей. Браузер
может реализовать поддержку таблиц стилей, но не поддержку DOM, и
наоборот. Метод hasFeature() возвращает результаты только о поддержке
DOM.
Некоторая функциональность браузера может когда-нибудь сама попасть в стандарты DOM. Функции Mozilla 1.0, которые вероятнее всего привлекут внимание стандартов - поддержка перетаскивания мышью, события клавиатуры, события прокрутки и клавиатурные сокращения.
Отдельно от DOM 0 для HTML существует поддержка DOM 0 для XUL. У DOM 0 для XUL нет стандарта или основного документа, описывающего его структуру. Он не документирован, за исключением замечаний о некоторых тегах в этой книге.
DOM-объекты относятся к Document и переменные документа, указывающие на него.
Есть и другие объекты, которые могут быть полезны в скриптах, но не имеют ничего общего с документами: скажем, окно, в котором отображается документ. Может понадобиться изменить текст его заголовка, открыть модальный диалог или заменить отображаемый документ другим.
Эти объекты, не относящиеся к документам - часть объектной модели
браузера ( ), существующей в большинстве
браузеров. Эта модель по сравнению с DOM очень маленькая и состоит из
иерархии всего лишь нескольких объектов. В самом ее верху находится
так называемый объект Window, представляющий окно на рабочем столе, в
котором отображается документ. Переменная окна указывает на этот
объект. Небольшая иерархия будет знакома web-разработчикам и
содержит такие объекты:
navigator screen history location frames
В этой книге мы не будем надолго задерживаться на подобных объектах (в любой книге по JavaScript для web-страниц они подробно рассматриваются). Лишь в лекции 10, "Окна и панели", мы продолжим обсуждение объекта окна.
Платформа Mozilla - это не только браузер, но и основа для
приложений. При отображении XUL-документов более логично говорить об
объектной модели приложения ( Application ), чем о . Окна XUL также начинаются с переменной окна, самого верхнего
объекта в иерархии приложения, но соответствующий ему тип - окно , а не просто окно. На рисунке 5.1 показана иерархия приложения
для окна .
(рис 5.1) Объектная модель XUL-приложения MozillaКаждое слово на рисунке 5.1 - свойство объекта, само содержащее
объект, хотя иногда там может находиться null или строка. Иерархия,
начинающаяся с окна ( window ), присутствует всегда. Иерархия,
начинающаяся с Element, существует для каждого объекта Element DOM 1.
Темные ребра на рисунке показывают существование и для XUL, и для
HTML; светлые - только для XUL. Прерывистая линия означает, что объект
доступен, только если присутствуют защитные фильтры. Термины,
совпадающие с терминами
Краткая характеристика AOM-объектов, специфичных для XUL, звучит
так: аргументы ( ) передаются при открытии окна; содержимое
( content ) указывает на документ, содержащийся в теге <browser>
XUL-страницы; набор управляющих команд ( controllers ) существует или для
окна, или для одного тега; диспетчер команд ( commandDispatcher ) -
также часть ) отвечает за точку фокуса;
объект блока ( boxObject ) содержит всю информацию о фрейме конкретного
тега и для обычных XUL-тегов, и для особых тегов вроде <scrollbox> ; builder предоставляет механизм постройки дерева для тега <tree> ; база
данных ( database ) и источник данных ( ) - части системы
шаблонов XUL.
Кроме этих объектов в лекции 10, "Окна и панели", рассказывается и о некоторых менее явных. Многие из этих объектов связаны с общими механизмами отображения XUL- или HTML-документов.
Объект окна XUL очень похож на объект окна HTML. Например, у него
тоже есть свойство history. Такое положение дел несколько сбивает с
толку, так как подразумевается, что эти свойства что-то делают. А они
не делают ничего. Они существуют потому, что многие скрипты и XUL-документы,
составляющие приложение браузера, ожидают, что эти свойства
будут работать. В нормальных условиях (классический браузер), чтобы
эти свойства функционировали, необходима дополнительная поддержка в
скриптах. В обычных окнах XUL этой поддержки нет, если только XUL-приложение
не собирается копировать функциональность браузера. Как и в
случае HTML, все реализованные интерфейсы DOM напрямую доступны из
XUL. Свойства и методы объекта window.document - входные точки для
этих интерфейсов.
И HTML-, и XUL-окна обладают одним особым свойством - свойством Components. Оно является шлюзом для доступа к большинству уникальных
интерфейсов Mozilla. Эти интерфейсы мы и рассмотрим далее.
Стандарты DOM предоставляют интерфейсы, которые делают содержимое доступным из скриптов. Похожим образом XPConnect и XPCOM предоставляют интерфейсы, которые делают доступными из скриптов составные части самой платформы Mozilla.
XPCOM - полностью внутренняя часть платформы Mozilla. XPConnect - соединительное звено, превращающее XPCOM-объекты и их интерфейсы в объекты базового ПО для JavaScript. В лекции 16, "Объекты XPCOM", подробно описывается работа с этой системой с точки зрения приложения. В текущей лекции дается только обзор этих двух технологий.
С точки зрения программиста приложения, система XPCOM в основном
представляется объектом window.Components (см. рисунок 5.1). Свойства
этого объекта classes и interfaces - списки всех доступных
идентификаторов контрактов XPCOM-объектов и имена их интерфейсов.
Свойство manager - менеджер XPCOM-компонентов. Свойство results -
список всех исключений, которые могут быть выброшены, и значений
результатов, которые могут возвращаться. Результаты - 32-битные
числа.
Теоретически эти списки могут динамически меняться, но в стандартной платформе этого не происходит. Стандартная платформа регистрирует все доступные компоненты во время запуска, и большая часть этой избыточной регистрации проводится во время первого запуска платформы. Набор зарегистрированных компонентов может быть пересмотрен при запуске инструмента regxpcom (в Microsoft Windows - regxpcom.exe). Эта маленькая программа устанавливается вместе со стандартной платформой и необходима только при создании или установке новых XPCOM- объектов (например, целых модулей, а не просто объектов; см. лекцию 16, "Объекты XPCOM").
С точки зрения программиста приложений, система XPConnect, делающая
написанные на C/C++ компоненты доступными из JavaScript, почти
невидима. Чтобы выполнить свою задачу, система XPConnect опирается на
набор библиотек типов (с расширениями .xpt), которые хранятся в
подкаталоге components каталога установки платформы. XPConnect также
предоставляет доступ из скриптов к модулям браузера, интерфейсам DOM,
Java и Java-
Все XPCOM-компоненты, предоставляющие более одного XPCOM-
интерфейса, должны реализовывать интерфейс nsISupports. Этот интерфейс
предоставляет метод QueryInterface(), с помощью которого можно
находить другие поддерживаемые объектом интерфейсы.
Платформа Mozilla очень велика, и многие ее части разбиты на составные блоки, каждый из которых является XPCOM-объектом с какими-то собственными уникальными характеристиками. Не вся Mozilla разделена на такие блоки; в ней все еще есть анонимные куски, скрытые внутри платформы.
Существует, по крайней мере, тысяча XPCOM-компонентов, каждый из которых предоставляет один или несколько интерфейсов. Это огромное число компонентов и этот набор эквивалентны очень большой библиотеке классов. Такое количество компонентов создает трудности для новичков. На первый взгляд, доступные функции Mozilla кажутся никак не связанными между собой, однако это не так. Просто все необходимые знания за один присест получить не удастся, а структура предмета неочевидна. Поиск документации по всем компонентам Mozilla также непрост. По две страницы на компонент - итого две тысячи страниц. Когда-нибудь такая книга, может, и будет написана, но пока ее еще нет.
Чтобы найти подходящий компонент, придется приложить усилия. То, что нам в конце концов нужно - объект, который можно использовать для решения конкретной задачи. Не ко всем объектам необходимо обращаться напрямую через XPCOM-интерфейсы. Многие объекты существуют в других формах. Перед погружением в XPCOM задайте себе вопрос: может ли нужный вам объект появиться в одной из этих форм?
Date или Math ). Если да, загляните в стандарт Image и Option в HTML, XMLHttpRequest и SOAPService в HTML и XUL).Если же не остается ничего, кроме как искать XPCOM-объект, то вот что следует делать. Объекты создаются из XPCOM-интерфейсов и XPCOM-компонентов, реализующих эти интерфейсы. У интерфейсов есть имена; у компонентов есть идентификаторы контракта (они же и их имена). Для поиска этой информации подойдет любая из следующих стратегий:
Из всех этих стратегий наиболее результативными будут чтение этой книги и загрузка копии XPIDL-файлов и других кратких описаний.
Наконец, имена почти всех (но не всех) полезных интерфейсов начинается с префикса:
nsI
а идентификаторы контракта всех компонентов начинаются с префикса:
@mozilla.org/
После того, как вы найдете пару из нужных вам интерфейса и компонента, требуется создать объект. Делается это так.
Вот простой пример использования XPCOM-компонента. В языке Perl есть ключевое слово require, позволяющее одному скрипту на Perl загрузить содержимое другого скрипта. В JavaScript нет аналога этой инструкции, но в Mozilla для этого можно использовать компоненты. Посмотрим на пример в листинге 5.7.
var comp_name = "@mozilla.org/moz/jssubscript-loader;1";
var comp_obj = Components.classes[comp_name];
var if_obj = Components.interfaces.mozIJSSubScriptLoader;
var final_obj = comp_obj.createInstance(if_obj);
final_obj.loadSubScript("file:///tmp/extras.js");
Этот скрипт эквивалентен использованию с содержимым файла
/tmp/
Для создания объекта необходимы две вещи: имя компонента ( строка,
comp_name ) и интерфейс, который можно использовать ( имя свойства,
if_obj ). После получения объекта ( final_obj ) можно задействовать его
методы. В нашем случае метод loadSubScript() импортирует содержимое
файла .
Характерный для многих компонентов Mozilla комментарий в XPIDL-определении mozIJSSubScriptLoader объясняет, что загружаемый файл
JavaScript должен быть расположен на локальном компьютере. Краткие и
рассредоточенные пояснения обычны для компонентов Mozilla.
В листинге 5.7 для создания объекта использовался метод createInstance(). Это одна из основных альтернатив. Другая - метод getService(). Он используется, когда данный XPCOM-компонент реализован
так, чтобы предоставлять только единственный экземпляр объекта
(одиночный объект). Такие объекты могут содержать статическую или
глобальную информацию, которую нельзя синхронизировать между
различными экземплярами объекта. В листинге 5.7 строку с createInstance() можно заменить на следующую:
var final_obj = comp_obj.getService(if_obj);
Как определить, какой из методов следует использовать? Если
идентификатор контракта или имя интерфейса содержат слово "service" (в
любом регистре), пользуйтесь getService(), иначе - createInstance().
Эти методы можно вызывать и без аргументов. В этом случае у
создаваемого или возвращаемого объекта есть интерфейс nsISupports.
Затем с помощью данного интерфейса можно получить любой другой нужный
интерфейс. Чтобы проиллюстрировать эту ситуацию, мы можем изменить
листинг 5.7:
var anon_obj = comp_obj.createInstance(); var final_obj = anon_obj.QueryInterface(if_obj);
Если нужно создать несколько однотипных объектов, можно сначала создать конструктор, а потом с его помощью сгенерировать отдельные объекты. Следующий код заменяет первые четыре строчки листинга 5.7:
var Includer = Components.Constructor( "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader", null ); var final_obj = new Includer(); // var another_obj = new Includer()
Обратите внимание, что в этом случае имя интерфейса представляет
собой строку. Третий аргумент метода - необязательная
строка. Если она есть, она означает имя метода, который будет вызван
как метод инициализации при создании экземпляра объекта. Все
аргументы, переданные конструктору объекта Includer(), будут
передаваться этому методу инициализации.
Наконец, можно создавать объекты с XPCOM-интерфейсами прямо в JavaScript. Это можно делать только для простых интерфейсов, для которых не предусмотрена какая-либо особая обработка. Продолжая вариации на тему листинга 5.7, мы можем создать объект, используя код листинга 5.8.
var final_obj = {
// интерфейс nsISupports
QueryInterface : function(iid) {
var Ci = Components.interfaces;
if ( !iid.equals(Ci.nsISupports)
!iid.equals(Ci.mozIJsSubSCriptLoader )
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
// интерфейс mozIJSSubScriptLoader
loadSubScript : function (url) {
// здесь идет код для запуска скрипта
return;
}
};
Этот объект поддерживает и интерфейс nsISupports, и mozIJsSub-ScriptLoader.
Если бы мы могли быть уверены, что для этого объекта
никогда не будет вызван метод QueryInterface(), мы могли бы не
использовать часть объекта с nsISupports. Конечно, с этим объектом
связана одна большая проблема: как правильно реализовать второй
интерфейс? Эта реализация будет содержать длинный код, открывающий
файл в файловой системе, читающий его содержимое, передавая его . Для этого можно использовать уже существующую реализацию
объекта. Такое создание объектов вручную иногда очень полезно. Чаще
всего оно бывает необходимо при создании возвращаемых объектов для
разных отслеживающих функций, см. лекцию 6, "События".
Метод createInstance() сам реализуется интерфейсом nsIComponentManager ; getService() реализуется интерфейсом nsIServiceManager.
К модулям браузера можно обращаться из скриптов, начиная с третьих версий браузеров. В то время Netscape расширила версию 1.1 стандарта модулей NPAPI, чтобы сделать его доступным из скриптов, и для работы с ним были написаны сотни модулей. Mozilla до сих пор поддерживает этот стандарт, но чтобы все работало без заминок, нужны последние версии и платформы, и модулей (особенно это касается Macromedia's Flash).
Для модулей существуют наборы XPIDL-интерфейсов. С их помощью
отдельными модулями можно управлять прямо из JavaScript. По-прежнему
самый простой способ работать с модулями - пользоваться HTML-тегами <embed> (не рекомендуется) или <object> (рекомендуется) и массивом plugins DOM 0. Обсуждение комбинирования HTML и XUL можно найти в
лекции 10, "Окна и панели".
Наборы интерфейсов, доступных в скриптах, также существуют в
стандартах DOM, так что интерфейсы DOM можно получить и с помощью
объекта Components. Однако это не имеет особого смысла, так как такие
интерфейсы автоматически доступны для объекта window, а также для всех
DOM-объектов, созданных и полученных с помощью данного объекта.
Браузеры поддерживают доступ к Java-
В Netscape Communicator 4.x браузер полностью зависел от модели защиты Java 1.1. Чтобы браузер мог принять какое-то решение относительно защиты, нужно было послать запрос Java с целью проверить специальную библиотеку классов, предоставляемую Netscape. Java и JavaScript были связаны с помощью технологии LiveConnect.
В Mozilla этой зависимости от Java больше нет. Netscape отвечает за
свою защиту сам. Java не требуется и она уже не интегрирована в такой
степени в платформу. LiveConnect в его первоначальном смысле не
существует; есть только XPConnect. Интеграция, предоставляемая
LiveConnect, все еще существует, но она находится ниже XPCOM под
слоями кода. Явная поддержка Java в Mozilla теперь состоит из набора
XPCOM-интерфейсов, как и многое другое, а поддержка Java по статусу
считается близкой к модулям. Функциональность LiveConnect по-прежнему
проявляется, когда создаются
Mozilla предоставляет две технологии для адаптации Java. Первая -
OJI (Open Java Inteface, открытый интерфейс к Java). Это программный
интерфейс, напоминающий интерфейс NPAPI (для модулей), который
предназначен для работы с платформой Java любого производителя.
XPCOM-интерфейсы предоставляют доступ к OJI. Вторая технология - эмулятор
LiveConnect для nsIJRILLiveConnectPlugin. Этот эмулятор
обрабатывает C/C++-запросы Mozilla, которые раньше отправлялись
Netscape 4.x виртуальной машиной Java.
Как и в случае с модулями, по-прежнему простейший способ
использовать Java внутри Mozilla - теги <
Некоторые программисты, пришедшие в web-программирование, уже обладают знанием Java. Если у вас есть сложная Java-среда, которую предстоит интегрировать с Mozilla, будьте осторожны. С одной стороны, самые простые и очевидные взаимодействия между JavaScript и Java будут работать прекрасно. С другой стороны, JavaScript, Java и Mozilla - очень сложные системы, и идеальное их взаимодействие на любом уровне - глобальная цель. В случае более сложных взаимодействий по-прежнему возникают проблемы, и рекомендуется внимательно изучить ошибки, связанные с Java в базе данных ошибок, Bugzilla.
Лучше всего остановиться на реализации Java от Sun Microsystems и затем пользоваться только версией 1.4 или более поздней. Чтобы свести к минимуму вероятность нетривиальных ошибок, рекомендуется пользоваться последними версиями Mozilla.
За исключением некоторых замечаний в лекции 10, "Окна и панели", в этом курсе разговор о Java завершен.
Чтобы использовать чью-либо еще функциональность, Mozilla опирается на систему XPCOM, но существуют и небольшие библиотеки, написанные полностью на JavaScript. Эти библиотеки пытаются упростить взаимодействие с отдельными частями XPCOM-системы.
Самые полезные из этих библиотек - JSLib и RDFLib. На самом деле
RDFLib - лишь подмножество JSLib. JSLib упрощает работу с файлами и
папками. Ее часть RDFLib предназначена для облегчения работы с
источниками данных
Эту библиотеку можно загрузить с сайта http://jslib.mozdev.org. Ее
файлы написаны в кодировке ASCII с
Следует иметь в виду, что эти библиотеки обновляются нерегулярно. При их загрузке нужно обращать внимание на заголовки, чтобы понять, как давно они обновлялись.
Чтобы пользоваться библиотекой, необходимо включить файл верхнего
уровня jslib.js, предоставляющий полезные процедуры и константы. Самая
важная из этих процедур - функция include(), которая выполняет те же
функции, что и require в Perl (или #include в io (для
работы с файлами) и (для работы с
(рис 5.2) Зависимости между файлами в JSLib и RDFLibСкрипты, из которых выходят стрелки, требуют, чтобы скрипты, к
которым эти стрелки направлены, были загружены первыми. Файлы без
зависимостей не показаны. Очевидно, что библиотека разделена на две
основные части, которые в этой книге будут называться RDFLib и JSLib.
Существует две независимые реализации доступа к
Библиотека задействует метод window.Components., чтобы
создавать конструкторы объектов для XPCOM-компонентов (описание того,
как это делается, дано в разделе "Другие варианты создания объектов").
Кроме того, она реализует конструкторы объектов JavaScript с помощью
системы прототипов JavaScript. Конструктор на основе прототипов
используется для создания нового объекта JavaScript. Во время процесса
создания конструкторы XPCOM-объектов используются для задания свойств
нового объекта JavaScript. Каждое такое свойство хранит один XPCOM-
объект. Поэтому окончательный объект JavaScript содержит (или
использует) один или несколько XPCOM-объектов и является точкой
доступа к этим объектам или их "фасадом". Это очень простая и широко
распространенная методика.
Такие новые объекты JavaScript содержат код для проверки ошибок,
| Конструктор | Файл с исходным кодом | Использует объекты | Цель |
|---|---|---|---|
include() method |
jslib.js | Включать другие файлы .js | |
Dir(filePath) |
io/dir.js | FileSystem() |
Работать с папкой на локальном диске |
DirUtils() |
io/dirUtils | Находить каталоги, специфичные для установки | |
File(filePath) |
io/file.js | FileSystem() |
Работать с файлом на локальном диске |
FileSystem(filePath) |
io/filesystem.js | Работать с любым элементом файловой системы на локальном диске | |
|
io/ |
Записать или прочитать один |
|
RDFFile(file_url, |
io/ |
|
Записать или прочитать один |
Socket() |
network/socket.js | Создать и читать из сокета или писать в него | |
SocketListener() |
network/socket.js | Обеспечить |
|
|
RDFBase() |
Читать записи из |
|
RDFBase( |
Читать записи из |
||
RDFContainer(type ownerURI, subjectURI, |
RDFResource() |
Упорядочивать или создавать <Seq>, < или <alt>, используя записи |
|
RDFResource(type, ownerURI, subjectURI, |
RDFBase() |
Изменить или создать |
|
|
Проиграть звук, находящийся по этому адресу |
В таблице 5.5 аргумент - объект nsIRDFDataSource, не URL
Zip, определенный в файле zip/zip.js, выполняет
только часть работы, необходимой для извлечения файла из архива.
Почти все файлы из библиотеки JSLib снабжены хорошей документацией в виде предварительных комментариев. Эти комментарии демонстрируют, как пользоваться предоставляемыми объектами.
В JavaScript также доступны интерфейсы, предоставляемые XBL-определениями.
Эти определения включаются в XML-файл со свойством
стиля CSS 2 -moz-. Такая связь добавляет определение тегу или
тегам. Хотя XBL-определения могут включать какое-то содержимое, можно
создавать и определения, содержащие только функциональность -
интерфейс. Возможность добавлять любую функциональность к любому тегу
- очень мощная функция.
Если для получения объекта Element, соответствующего тегу с XBL-определением,
используется стандарт DOM 1, результирующий объект
JavaScript будет включать свойства и методы, подразумеваемые этим
определением. Этими свойствами и методами затем можно пользоваться как
обычно для объектов базового ПО. Платформа автоматически выполнит все
необходимые действия.
Таким образом, XBL-определения расширяют функциональность некоторых DOM-объектов. Самими XBL-определениями можно пользоваться и как XPCOM-компонентами, поэтому они доступны через XPConnect. Единственная причина делать это - если XBL-компонент должен быть доступен из кода на C/C++.
В лекции 15, "XBL-связки", XBL рассматривается более подробно.
В Mozilla существует три изолированных интерфейса, доступных из скриптов. У этих интерфейсов есть собственные интерпретаторы JavaScript и собственные глобальные объекты. Так как они изолированы от остальной части Mozilla, у них имеется собственный AOM.
Система настроек Mozilla - первый из этих интерфейсов. Ее AOM
состоит из единственного объекта PrefConfig, который играет роль
глобального объекта. У него есть всего два свойства, два метода: pref() и user_pref().
@mozilla.org/preferences-service;1 @mozilla.org/preferences;1
Когда к этим компонентам происходит обращение из обычной JavaScript-среды Mozilla, настройки можно менять, но файл prefs.js не может быть изменен напрямую. Изменения этих настроек записываются в файлы prefs.js во время завершения работы платформы.
Второй такой интерфейс - система сетевой установки компонентов Mozilla. В лекции 17, "Система распространения и установки - XPInstall", полностью описывается эта среда и ее AOM. Эта система также предоставляет XPCOM-компоненты для доступа из обычной среды, но эти интерфейсы низкоуровневые и мало полезны для решения обычных задач. Они позволяют уведомлять систему XPInstall о появлении содержимого откуда-нибудь еще (например, из Internet) и принимать это содержимое для обработки. Эти ограниченные интерфейсы свидетельствуют, что XPInstall стоит использовать только для процесса установки.
Третий изолированный интерфейс Mozilla - xpcshell. Это отдельная от
платформы Mozilla программа, недоступная в обычных стабильных сборках.
Она доступна в нестабильных сборках или если вы собираете Mozilla
самостоятельно (требует параметра --enable-debug ). xpcshell -
самостоятельный интерпретатор JavaScript, использующийся для
тестирования скриптов, интерпретатора JavaScript и системы
XPConnect/XPCOM. Его AOM состоит из единственного глобального объекта
и объектов, связанных с XPCOM. Самые полезные свойства глобального
объекта перечислены в таблице 5.6.
| Свойство | Описание |
|---|---|
Components |
Массив компонентов XPConnect/XPCOM, предоставляет доступ к большей части компонентов платформы Mozilla |
|
Преобразует аргумент в строку Unicode и отправляет ее необработанной на |
load(arg1,arg2,...) |
Пытается загрузить и интерпретировать указанные файлы (не URL) |
print(arg1,args2,...) |
Преобразует аргументы в строки и отправляет их, слегка отформатировав, на printf("%s") |
quit() |
Завершает работу xpcshell |
Массив Components делает xpcshell отличным местом для тестирования
скриптов, которые используют большое число компонентов Mozilla. Так
как xpcshell пользуется обычными stdin и stdout,
эту программу легко запускать в пакетном режиме автоматическими
тестовыми системами. xpcshell - это JavaScript-эквивалент
интерпретатора Perl, только он немногим больше программы
тестирования.
Теоретически скрипт, запущенный в xpcshell, может создавать и
использовать достаточно компонентов для "постройки" целого браузера.
Практически же исполняемый файл Mozilla выполняет во время
инициализации действия на низком уровне, которые xpcshell не
использует. Следовательно, xpcshell лучше оставить для более простых
задач.
В этом разделе мы добавим в окно редактирования ( Edit ) NoteTaker
несколько скриптов. Эти скрипты будут менять способ работы XUL-
документа после его отображения платформой. Во время работы мы будем
манипулировать несколькими типами объектов, описанных в этой
лекции.
Первое изменение NoteTaker будет касаться содержимого окна с
ключевыми словами. Это окно на самом деле поддерживает две отдельные
панели. Каждая панель представляется одним из тегов <toolbarbutton>: "Edit" и ". Мы бы хотели отображать окно редактирования так,
чтобы видимой была только одна из этих панелей. Так как содержимое
панели с ключевыми словами еще не определено, мы используем
"заполнитель".
Чтобы добавить поддержку двухпанельной системы, воспользуемся тегом <. Содержимое панели "Edit" будет появляться как одна карта
колоды, а содержимое панели "Keyword" будет другой картой. Ранее
содержимое, скрывающееся под двумя тегами <toolbarbutton>, было для
кнопки "Edit". Теперь мы хотим поместить это содержимое на одну карту,
а для другой используем что-нибудь вроде:
<hbox flex="1"> <description>Добавим потом.</description> </hbox>
На рисунке 5.3. показана структура XUL-документа до и после изменения.
(рис 5.3) Добавление <deck> в NoteTakerВсе, что мы сделали - добавили в нужные места < и новое
содержимое. Это можно было сделать и в лекции 2, "Проектирование с
XUL", однако переключаться между двумя картами < без скрипта
невозможно. Теперь у нас есть JavaScript, и мы можем реализовать
переключение. Слегка забегая вперед (в лекцию 6, "События"), мы
отметим, что теги <toolbarbutton> поддерживают обработчик событий onclick. Мы добавим скрипт так, чтобы с ним было интересно
повозиться.
Существует множество способов решить нашу задачу, но все они
сводятся к добавлению тегам атрибутов id ; написанию функции, которую
мы назовем action(), и добавлению обработчика onclick. Обработчики onclick выглядят примерно так:
<toolbarbutton label="Edit" onclick="action('edit')"/>
<toolbarbutton label="Edit" onclick="action('keywords')"/>
Новые идентификаторы будут такие:
<deck flex="1" id="dialog.deck"> <hbox flex="1" id="dialog.edit"> <hbox flex="1" id="dialog.keywords">
Функция action() ; приведена в листинге 5.9.
function action(task) {
var card = document.getElementById("dialog." + task);
if (!card || ( task != "edit" task != "keywords") )
throw("Unknown Edit Task");
var deck = card.parentNode;
var index = 0;
if ( task == "edit" ) index = 0;
if ( task == "keywords") index = 1;
deck.setAttribute("selectedIndex",index);
}
Мы хотим научиться передавать функциям аргументы, похожие на
команды, потому что это общая практика для приложения Mozilla. Если мы
включим этот код в наш XUL-файл, с тегом <script>, мы получим
непонятные ошибки, описанные в разделе "Отладка" этой лекции, если
только не будем осторожны и не воспользуемся содержимым <![CDATA[]]>.
Лучше всего поместить наш скрипт в отдельный файл с самого начала и
включить его примерно так:
<script src="editDialog.js"/>
Функция action() вызывает метод window.document.getElementById().
Этот метод - самая распространенная входная точка для доступа к DOM из
скриптов. Ей передается значение HTML- или XUL-атрибута id, и она
возвращает объект для тега с этим идентификатором. Далее мы можем
работать с этим объектом через его свойства и методы.
Оставшаяся часть функции проходит через тег < с помощью
свойства DOM parentNode и устанавливает там значение атрибута selectedIndex с помощью свойства DOM setAttribute(). Так как это
свойство имеет смысл для тега <, система отображения XUL
отреагирует на изменения автоматически, и отобразится нужная
карта.
Если мы не будем пользоваться аргументами, похожими на команды,
можно достигнуть того же эффекта быстрее, но не очень аккуратно.
Эквивалентные обработчики onclick, которые по-прежнему используют
интерфейсы DOM, выглядят так:
document.getElementById("dialog.deck").setAttribute("selectedIndex",0);
document.getElementById("dialog.deck").setAttribute("selectedIndex",1);
Независимо от того, какой из двух подходов выбрать, для решения
большей части задачи потребуются всего лишь три интерфейса стандарта
DOM 1 Document, Element и Node.
Использование < - лишь один из многих способов "оживить"
XUL-документ скриптами. Другой, не менее правильный способ - менять CSS-стили после загрузки документа.
Начнем с примера <, удалив его открывающие и <hbox>, можно
интерпретировать как HTML-тег <div>. В листинге 5.10 показана другая
версия метода action().
function action(task) {
var card = document.getElementById("dialog." + task);
if (!card || ( task != "edit" task != "keywords") )
throw("Unknown Edit Task");
var oldcard; // the content to remove
if (task == "edit")
oldcard = document.getElementById("dialog.keywords");
else
oldcard = document.getElementById("dialog.edit");
oldcard.setAttribute("style","visibility:collapse");
card.removeAttribute("style");
}
Здесь используются те же интерфейсы DOM, что и в листинге 5.9, но
меняются атрибуты в DOM-иерархии окна. При изменении правил стиля
содержимого система отображения внутри платформы автоматически
обновляет отображаемый документ. Использовать атрибут style не очень
удобно. В XUL есть более удобный атрибут , который можно
менять, не трогая при этом никакие другие атрибуты или встроенные
стили, которые может добавлять скрипт. Две последние инструкции
скрипта нужно заменить следующими строчками:
oldcard.setAttribute("collapsed","true");
card.removeAttribute("collapsed");
Атрибут XUL hidden не должен обновляться автоматически, поскольку
он может нанести вред XBL-связям (см. лекцию 15, "XBL-связки").
Кроме использования < и стилей, можно использовать
DOM-интерфейсы и для физического удаления/вставки частей в иерархию DOM
документа. Это третий подход, использующийся для скрытия и отображения
содержимого, но он слишком сложен для столь простой задачи.
При преобразовании XUL-документа в иерархию DOM предоставляется больше информации, чем предусмотрено стандартными интерфейсами DOM. Существует еще и набор интерфейсов XBL-системы Mozilla. Эти дополнительные интерфейсы добавляют свойства и методы специфичным для XUL DOM-объектам. Эти свойства и методы чрезвычайно удобны и упрощают написание скриптов. Иногда использование только стандартов DOM делает код менее простым и понятным.
Давайте посмотрим, можно ли с помощью одного из этих интерфейсов
улучшить наше решение с использованием тега <. Вероятнее всего,
тег, с которым мы свяжем наш скрипт, будет <. Для начала заглянем
в файл xul.css. Этот файл хранится в архиве
deck {
display: -moz-deck;
-moz-binding: url("chrome://global/content/bindings/
general.xml#deck");
}
Строка -moz- сообщает нам о том, что для этого тега есть
связь, так что нам есть на что посмотреть. Связь называется " и
описана в файле general.xml. Откроем этот файл и поищем такую
строку:
<binding id="deck">
Этого достаточно, связь найдена. Вот одна из частей ее определения:
<binding id="deck"> <implementation> <property name="selectedIndex" ...
Это все, что нам нужно. Имена свойства в XBL и имена атрибутов в
XUL обычно совпадают, более того, они обычно совпадают с именами HTML-
объектов. Свойство selectedIndex совпадает с атрибутом selectedIndex
тега <. Его мы и используем. В методе action() заменим строку
deck.setAttribute("selectedIndex",index);
на
deck.selectedIndex = index;
Это тривиальное изменение, но наш код становится достаточно
коротким, чтобы мы могли выкинуть переменную index и избавиться еще от
пары строк. Если бы в определении связки для < был бы, например,
метод setCard(), мы бы могли использовать его вместо написания action(). Последние строки нашей функции можно сократить так:
var deck = card.parentNode; if ( task == "edit" ) deck.selectedIndex = 0; if ( task == "keywords") deck.selectedIndex = 1;
Второе изменение, которое мы сделаем в NoteTaker - внесем внешние данные в отображаемое окно извне XUL-документа. То есть мы загрузим содержимое, которое будет появляться внутри прямоугольных областей. В дальнейшем мы еще изменим способ загрузки, сохранения и отображения информации несколько раз.
Чтобы получить доступ вовне, нам не нужны XPCOM-компоненты. Мы могли бы задействовать URL, указанный в лекции 7, "Формы и меню". Но здесь мы воспользуемся наборами строк.
Мы можем манипулировать наборами строк (файлами properties ) из XUL
или из JavaScript. Если мы выберем первый путь, будет то же, что и при
написании скрипта для <: мы добавим несколько тегов и поищем
XBL-определение, которое предоставляет полезные интерфейсы. Но сейчас мы
будем работать с наборами строк напрямую из JavaScript. Так как для
этого нужен XPCOM, нам придется хранить файлы внутри
Нам нужен объект, который умел бы работать с файлами properties.
Другими словами, нам требуется некий полезный XPCOM-интерфейс и
компонент, реализующий этот интерфейс. Если мы будем искать его в
XPIDL-файлах (или по индексу этой книги), легко заметить интерфейсы в
файле nsIStringBundle.nsIStringBundleService и nsIStringBundle. Так как в имени первого
встретилось слово "Service" (служба), это, вероятно, XPCOM-служба; для
нас это отправная точка. Вспомним, что службы создают объект с помощью getService() ; в остальных случаях используется createObject().
Мы также обратим внимание на то, что метод createBundle() этого
интерфейса выглядит так:
nsIStringBundle createBundle(in string aURLSpec);
Итак, этот метод создает объект с интерфейсом nsIStringBundle из
URL. У интерфейса nsIStringBundle есть метод getStringFromName(),
извлекающий строку из файла с набором строк. Нам не так важно, что в
XPIDL используются собственные типы wstring и string ; как нам
известно, XPConnect преобразует их во что-то, что JavaScript может
понять - обычное строковое значение, которое появится как объект String.
В файле интерфейса также в самом верху заявляется, что связанный с ним идентификатор контракта таков (теперь у нас получается пара из интерфейса и XPCOM-компонента):
@mozilla.org/intl/stringbundle;1 nsIStringBundleService
Нам не нужен идентификатор контракта для интерфейса nsIStringBundle, так как служба создаст объекты с этим интерфейсом для
нас, когда мы вызовем createBundle(). Но нам нужно сначала получить
объект службы. Так как мы нашли пару целиком, это легко:
var Cc = Components.classes; var Ci = Components.interfaces; var cls = Cc["@mozilla.org/intl/stringbundle;1"]; var svc = cls.getService(Ci.nsIStringBundleService);
Если в Консоли JavaScript никаких ошибок не появится (а их быть не
должно), тогда теперь переменная
var Cc = Components.classes;
var Ci = Components.interfaces;
var cls = Cc["@mozilla.org/intl/stringbundle;1"];
var svc = cls.getService(Ci.nsIStringBundleService);
var URL = "chrome://notetaker/locale/dialog.properties";
var sbundle = svc.createBundle(URL);
function load_data() {
var names = ["summary", "details", "chop-query", "home-page", "width",
"height", "top", "left"];
var box, desc, value;
for (var i = names.length-1; i>0; i--) {
value = sbundle.GetStringFromName("dialog."+ names[i]);
desc = document.createElement("description");
desc.setAttribute("value",value);
box = document.getElementById("dialog." + names[i]);
box.appendChild(desc);
}
}
Переменная sbundle содержит XPCOM-объект, указанный нашим URL. Файл
по выбранному нами URL должен соответствовать правилам файла
properties. Функция load_data() считывает строки из этого файла и
задействует DOM так, что тег <description value="строка"> добавляется
как содержимое в конец всякого блока-заглушки <box>. Обратите
внимание, что объект <description> строится и добавляется к блоку в
самом конце. Это более эффективно, чем пошаговое добавление новых
данных в существующую структуру DOM.
Этот код также подразумевает, что в XUL-документе в ожидаемых местах есть идентификаторы. Нам нужно добавить их туда вручную. Для этого требуется заменить каждую строку
<box class="temporary"/>
на что-то вроде
<box class="temporary" id="dialog.summary"/>
Наконец, мы вызовем функцию load_data() из другого обработчика
событий, который подсмотрели в лекции 6, "События": атрибут onload тега <window>. Таким образом, мы будем уверены, что документ уже будет
существовать к тому времени, когда мы начнем манипулировать им:
<window xmlns= "http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul" onload="load_data()>
Так как файл хранится в части
dialog.summary=My Summary dialog.details=My Details dialog.chop-query=true dialog.home-page=false dialog.width=100 dialog.height=90 dialog.top=80 dialog.left=70
Путь к этому файлу относительно корневого каталога
notetaker/locale/en-US/dialog.properties
Теперь диалог NoteTaker должен походить на снимок на риcунке 5.4.
(рис 5.4) Диалог NoteTaker cо строками, загружаемыми скриптомНам удалось успешно использовать XPCOM для взаимодействия с
ресурсами вне загруженного документа. Это важный шаг, даже если все,
что мы сделали -
Подведем итог проделанной работы: мы работали с DOM-, AOM-, XBL- и
XPCOM-объектами. JavaScript предоставляет полный доступ к загруженному
содержимому и, если наше приложение установлено в
JavaScript - некомпилируемый язык; следовательно, на долю программиста приходится довольно много работы по выявлению ошибок. Так как переменные, свойства и методы определяются во время выполнения, многие ошибки могут оставаться незамеченными до самой последней стадии разработки. Важно, чтобы у процесса тестирования и отладки была определенная структура и чтобы он не сводился к догадкам. В данном разделе описывается, какие инструменты можно для этого использовать.
Лучшая защита от ошибок - хороший стиль написания кода. Держите
свои скрипты отдельно от документов. Не забывайте о завершающих точках
с запятой и отступах, пользуйтесь содержательными именами переменных.
Всегда проверяйте аргументы, передаваемые функции, и возвращайте
какое-нибудь осмысленное значение. Всегда пользуйтесь блоками try,
если используемые интерфейсы Mozilla могут генерировать исключения.
Обратите внимание на советы о настройках в первой лекции, "Основные
концепции".
Метод объекта окна - очень полезное средство. При его
использовании можно запустить Mozilla с ключом -console, тогда
вспомогательная информация будет выводиться в отдельное окно и не
повлияет на собственные окна платформы. Этот вывод также можно
сохранить в файл журнала. В UNIX запуск с этим ключом возможен только
из командной строки. Если скрипты используют выполнение через
промежутки времени, либо создаваемые либо получаемые, такой вывод -
иногда единственный способ узнать, какой же на самом деле была
последовательность событий во время обработки.
Mozilla также поддерживает URL javascript:. Этот URL может
пригодиться для быстрой проверки состояния документа. Если документ
загружен в окно Навигатора (XUL-документы могут быть загружены так
наравне с web-страницами), такие URL можно использовать для проверки
содержимого документа. Это наиболее эффективно, когда пользователь
вводит в документ большое
javascript:var x=window.state.formProgress; alert(x);
Здесь alert() - небольшое окно, отображающее одну или несколько
строк текста. Эту инструкцию можно поместить куда угодно в скрипт,
связанный с окном Mozilla, таким образом вы получите базовую
информацию о состоянии среды скрипта и ее содержимого. alert() также
приостанавливает интерпретатор JavaScript; это неплохо при простой
обработке данных, но неудачно, если предполагается, что информация
должна обрабатываться скриптом за очень быстрое время (например, при
использовании потокового аудио/видео). alert() - простейший инструмент
отладки.
Контрольные точки - еще одно простое, но очень полезное средство
отладки. У каждого созданного объекта JavaScript есть метод watch().
Этот метод используется для добавления к свойствам объекта одной
скрытой функции. Синтаксис этой скрытой функции напоминает синтаксис " в Mozilla. Пример приводится в листинге 5.13.
function report(prop,oldval,newval) {
dump(prop + "old: " + oldval + "; new: " + newval);
return newval; // гарантирует, что отслеживаемый код еще выполняется
};
var obj = { test:"before" };
obj.watch("test",report);
obj.test = "after"; // вызов report()
obj.unwatch("test");
Аргументы функции предоставляются функцией watch(). При
отслеживании свойства test каждое его изменение в качестве побочного
эффекта приводит к вызову функции .
При снятии отслеживания этот побочный эффект исчезает. Это тоже тактика отслеживания изменений, когда они происходят.
Наконец, в любую часть скрипта можно вставить ключевое слово . При этом откроется Отладчик JavaScript, о командах которого
можно узнать, набрав /help в командной строке в нижней части окна.
Отладчик основывается на этой XPCOM-паре:
@mozilla.org/js/jsd/debugger-service;1 jsdIDebuggerService
Если не хочется использовать Отладчик JavaScript, можно реализовать
что-нибудь поверх этого компонента и его интерфейса jsdIDebuggerService для себя.
При возникновении непонятных проблем следует полностью закрыть
Mozilla, запустить ее заново и провести проверку снова. Нужно всегда
держать открытой Консоль JavaScript ( Tools | Web Development ) и всегда
очищать ее журнал перед загрузкой нового документа, чтобы ошибки были
заметны сразу. Время от времени необходимо проверять, не появились ли
"умершие" процессы Mozilla без окон, к которым их можно отнести.
Язык JavaScript невелик и прост в освоении. Он во многом похож на многие другие языки из семьи языков с C-подобным синтаксисом, за исключением уникальной работы с объектами. Цепочки областей видимости и прототипов - любопытные и сложные особенности языка, и на искусное создание кода, работающего с ними, можно потратить часы. В JavaScript 2.0 цепочки прототипов будут заменены классами.
По сравнению с основной частью языка, объем интерфейсов, используемых в JavaScript, очень велик. Интерфейсы варьируются от простых и удобных до совершенно непонятных и окружают Java, модули и поддержку ранних браузеров.
Опытные web-разработчики заметят, что поддержка DOM-интерфейсов в Mozilla проста в использовании, знакома и функциональна. Без дополнительных усилий она применима и к XUL-приложениям. Хорошая поддержка стандартов - настоящий праздник после многих лет проверок на совместимость с различными браузерами. Начинающим XML-программистам рекомендуется внимательно изучить первую часть стандарта DOM 1, пока понятия узла, элемента, документа и фабричного метода не уложатся в голове.
Инфраструктура, состоящая из XPConnect и XPCOM - новый мир Mozilla. Имея более тысячи компонентов (и интерфейсов), нельзя получить все сразу. Возможно, некоторые интерфейсы вам не придется задействовать никогда; использование других будет во многом зависеть от того, как сильно требуется изменить платформу и насколько амбициозно ваше приложение.
XPCOM предоставляет в распоряжение пользователя целый мир объектов и позволяет сделать программирование на JavaScript таким же развитым, как и с применением языков Java, C или C++. У всех XPCOM-интерфейсов есть XPIDL-определение; отыскав нужное, вы прочитаете его без труда. Обзор этих интерфейсов может дать некоторое представление обо всех возможностях Mozilla.
Но вместо того, чтобы погрузиться в XPCOM, мы сначала вернемся к миру интерфейсов и поговорим о пользовательском вводе.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.