Cover_javascript_new.qxd 22.02.
2008 12:43 Page 1
Создание активных вебстраниц
5Включ
е ает
из Aja
да x и D
JavaScript. Подробное руководство ПЯТОЕ ИЗДАНИЕ
ни OM
е
JavaScript – это мощный объектноориентированный язык сценариев, который
JavaScript
может использоваться непосредственно внутри вебстраниц HTML. В сочетании
с объектной моделью документа (Document Object Model, DOM), определенной
вебброузером, и DHTML JavaScript позволяет создавать динамическое содер
жимое и интерактивные клиентские вебприложения. Синтаксис JavaScript базируется на
популярных языках программирования C, C++ и Java, что делает его понятным и легким
для изучения опытными программистами. В то же время JavaScript – это интерпретируе
мый язык сценариев, предоставляющий гибкую среду, которую могут изучать новички.
«JavaScript. Подробное руководство» содержит полное описание базового языка JavaScript,
а также традиционной и стандартизованной объектных моделей документа, реализован
ных в вебброузерах. Изощренные примеры, включенные в книгу, можно использовать для
решения распространенных задач, таких как проверка данных формы, работа с cookies и
создание переносимой анимации DHTML. Части IV и V представляют собой справочники
по базовому API JavaScript, традиционному клиентскому API и стандартизованному API
W3C DOM, в которых описываются все объекты, методы, свойства, конструкторы, кон
станты, функции и обработчики событий этих API.
Четвертое издание бестселлера по JavaScript было тщательно дополнено с учетом воз
можностей JavaScript 1.5 (ECMAScript v3). В книге также описан стандарт W3C DOM (Level 1
и Level 2), при этом для обратной совместимости сохранен материал по традиционному
DOM Level 0.
Это не только наиболее полное руководство программиста, но и справочник по JavaScript.
Он особенно полезен для тех, кто работает с последними, соответствующими стандартам
вебброузерами, такими как Internet Explorer 6, Netscape 6 и Mozilla. Вебмастеры узнают,
JavaScript
как применять JavaScript для построения динамических вебстраниц. Опытные разработ
чики смогут быстро приступить к написанию сложных программ. Эта книга необходима
каждому, кто пишет на JavaScript, независимо от его опыта.
«Настольная книга экспертов по JavaScript… Хорошо систематизирована и подробна».
– Брендан Эйх (Brendan Eich), создатель JavaScript
руководство
Подробное
Подробное руководство
˚ ¯ˆ˛ — ¨ :ˇ — ˛ ˆ — ¨—˛´ ˝¨¯ Издательство «СимволПлюс»
— ˛ ´ ¯ ˝ ˇ ˛˜ˆ˛ ˛ ´ ˚¨ ¨ ¯¸¯ : (812) 3245353, (095) 9458100
ISBN13: 9785932861035
Флэнаган
ISBN10: 5932861037
www.symbol.ru
9 785932 861035 Дэвид Флэнаган
The Definitive Guide
Fifth Edition
David Flanagan
Подробное руководство
Пятое издание
Дэвид Флэнаган
СанктПетербург–Москва
2008
Дэвид Флэнаган
JavaScript. Подробное руководство,
5
е издание
Перевод А. Киселева
Главный редактор А. Галунов
Зав. редакцией Н. Макарова
Научный редактор О. Цилюрик
Редактор А. Жданов
Корректор С. Минин
Верстка Д. Орлова
Флэнаган Д.
JavaScript. Подробное руководство. – Пер. с англ. – СПб: СимволПлюс,
2008. – 992 с., ил.
ISBN10: 5932861037
ISBN13: 9785932861035
Пятое издание бестселлера «JavaScript. Подробное руководство» полностью
обновлено. Рассматриваются взаимодействие с протоколом HTTP и примене
ние технологии Ajax, обработка XMLдокументов, создание графики на сторо
не клиента с помощью тега <canvas>, пространства имен в JavaScript, необхо
димые для разработки сложных программ, классы, замыкания, Flash и встра
ивание сценариев JavaScript в Javaприложения.
Часть I знакомит с основами JavaScript. В части II описывается среда разра
ботки сценариев, предоставляемая вебброузерами. Многочисленные примеры
демонстрируют, как генерировать оглавление HTMLдокумента, отображать
анимированные изображения DHTML, автоматизировать проверку правиль
ности заполнения форм, создавать всплывающие подсказки с использованием
Ajax, как применять XPath и XSLT для обработки XMLдокументов, загру
женных с помощью Ajax. Часть III – обширный справочник по базовому Java
Script (классы, объекты, конструкторы, методы, функции, свойства и кон
станты, определенные в JavaScript 1.5 и ECMAScript v3). Часть IV – справоч
ник по клиентскому JavaScript (API вебброузеров, стандарт DOM API Level 2
и недавно появившиеся стандарты: объект XMLHttpRequest и тег <canvas>).
ISBN10: 5932861037
ISBN13: 9785932861035
ISBN 0596101996 (англ)
© Издательство СимволПлюс, 2008
Authorized translation of the English edition © 2006 O’Reilly Media, Inc. This trans
lation is published and sold by permission of O’Reilly Media, Inc., the owner of all
rights to publish and sell the same.
Все права на данное издание защищены Законодательством РФ, включая право на полное или час
тичное воспроизведение в любой форме. Все товарные знаки или зарегистрированные товарные зна
ки, упоминаемые в настоящем издании, являются собственностью соответствующих фирм.
Издательство «СимволПлюс». 199034, СанктПетербург, 16 линия, 7,
тел. (812) 3245353, www.symbol.ru. Лицензия ЛП N 000054 от 25.12.98.
Налоговая льгота – общероссийский классификатор продукции
ОК 00593, том 2; 953000 – книги и брошюры.
Подписано в печать 14.02.2008. Формат 70×1001/16 . Печать офсетная.
Объем 62 печ. л. Тираж 2000 экз. Заказ N
Отпечатано с готовых диапозитивов в ГУП «Типография «Наука»
199034, СанктПетербург, 9 линия, 12.
Эта книга посвящается всем,
кто учит жить мирно и противостоит насилию.
Оглавление
Предисловие . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1. Введение в JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1. Что такое JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2. Версии JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3. Клиентский JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.4. Другие области использования JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.5. Изучение JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Часть I. Основы JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2. Лексическая структура . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.1. Набор символов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2. Чувствительность к регистру . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.3. Символыразделители и переводы строк . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.4. Необязательные точки с запятой . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.5. Комментарии. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.6. Литералы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.7. Идентификаторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.8. Зарезервированные слова . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3. Типы данных и значения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.1. Числа . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.2. Строки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.3. Логические значения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.4. Функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.5. Объекты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.6. Массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.7. Значение null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.8. Значение undefined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.9. Объект Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.10. Регулярные выражения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.11. Объекты Error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.12. Преобразование типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.13. Объектыобертки для элементарных типов данных. . . . . . . . . . . . . . . . . 58
8 Оглавление
3.14. Преобразование объектов в значения элементарных типов . . . . . . . . . . 60
3.15. По значению или по ссылке . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4. Переменные . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.1. Типизация переменных. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.2. Объявление переменных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.3. Область видимости переменной . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.4. Элементарные и ссылочные типы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5. Сборка мусора . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.6. Переменные как свойства . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.7. Еще об области видимости переменных . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5. Выражения и операторы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.1. Выражения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2. Обзор операторов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.3. Арифметические операторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.4. Операторы равенства . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.5. Операторы отношения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.6. Строковые операторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.7. Логические операторы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.8. Поразрядные операторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.9. Операторы присваивания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.10. Прочие операторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6. Инструкции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.1. Инструкциивыражения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2. Составные инструкции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.3. Инструкция if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.4. Инструкция else if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.5. Инструкция switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.6. Инструкция while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.7. Цикл do/while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
6.8. Инструкция for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
6.9. Инструкция for/in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.10. Метки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.11. Инструкция break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.12. Инструкция continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
6.13. Инструкция var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.14. Инструкция function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
6.15. Инструкция return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.16. Инструкция throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
6.17. Инструкция try/catch/finally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.18. Инструкция with . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
6.19. Пустая инструкция . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
Оглавление 9
6.20. Итоговая таблица JavaScriptинструкций . . . . . . . . . . . . . . . . . . . . . . . . 119
7. Объекты и массивы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.1. Создание объектов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.2. Свойства объектов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
7.3. Объекты как ассоциативные массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.4. Свойства и методы универсального класса Object . . . . . . . . . . . . . . . . . . 127
7.5. Массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.6. Чтение и запись элементов массива . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.7. Методы массивов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
7.8. Объекты, подобные массивам. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8. Функции. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
8.1. Определение и вызов функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
8.2. Аргументы функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.3. Функции как данные . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
8.4. Функции как методы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8.5. Функцияконструктор . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.6. Свойства и методы функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.7. Практические примеры функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
8.8. Область видимости функций и замыкания . . . . . . . . . . . . . . . . . . . . . . . . 156
8.9. Конструктор Function() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
9. Классы, конструкторы и прототипы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
9.1. Конструкторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
9.2. Прототипы и наследование . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
9.3. Объектноориентированный язык JavaScript . . . . . . . . . . . . . . . . . . . . . . 172
9.4. Общие методы класса Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
9.5. Надклассы и подклассы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.6. Расширение без наследования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.7. Определение типа объекта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
9.8. Пример: вспомогательный метод defineClass() . . . . . . . . . . . . . . . . . . . . . 194
10. Модули и пространства имен. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
10.1. Создание модулей и пространств имен . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
10.2. Импорт символов из пространств имен . . . . . . . . . . . . . . . . . . . . . . . . . . 204
10.3. Модуль со вспомогательными функциями . . . . . . . . . . . . . . . . . . . . . . . 208
11. Шаблоны и регулярные выражения. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
11.1. Определение регулярных выражений . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
11.2. Методы класса String для поиска по шаблону . . . . . . . . . . . . . . . . . . . . 223
11.3. Объект RegExp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
10 Оглавление
12. Разработка сценариев для Javaприложений . . . . . . . . . . . . . . . . . . . . . 229
12.1. Встраивание JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
12.2. Взаимодействие с Javaкодом . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Часть II. Клиентский JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
13. JavaScript в вебброузерах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
13.1. Среда вебброузера . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
13.2. Встраивание JavaScriptкода в HTMLдокументы . . . . . . . . . . . . . . . . . 258
13.3. Обработчики событий в HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
13.4. JavaScript в URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
13.5. Исполнение JavaScriptпрограмм . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
13.6. Совместимость на стороне клиента . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
13.7. Доступность . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
13.8. Безопасность в JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
13.9. Другие реализации JavaScript во Всемирной паутине . . . . . . . . . . . . . 285
14. Работа с окнами броузера. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
14.1. Таймеры . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
14.2. Объекты Location и History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
14.3. Объекты Window, Screen и Navigator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
14.4. Методы управления окнами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
14.5. Простые диалоговые окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
14.6. Строка состояния . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
14.7. Обработка ошибок . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
14.8. Работа с несколькими окнами и фреймами . . . . . . . . . . . . . . . . . . . . . . . 306
14.9. Пример: панель навигации во фрейме . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
15. Работа с документами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
15.1. Динамическое содержимое документа . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
15.2. Свойства объекта Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
15.3. Ранняя упрощенная модель DOM: коллекции
объектов документа . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
15.4. Обзор объектной модели W3C DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
15.5. Обход документа . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
15.6. Поиск элементов в документе . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
15.7. Модификация документа . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
15.8. Добавление содержимого в документ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
15.9. Пример: динамическое создание оглавления . . . . . . . . . . . . . . . . . . . . . 351
15.10. Получение выделенного текста . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
15.11. IE 4 DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
Оглавление 11
16. CSS и DHTML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
16.1. Обзор CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
16.2. CSS для DHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
16.3. Использование стилей в сценариях . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
16.4. Вычисляемые стили . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
16.5. CSSклассы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
16.6. Таблицы стилей . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
17. События и обработка событий. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
17.1. Базовая обработка событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
17.2. Развитые средства обработки событий в модели DOM Level 2 . . . . . . . 414
17.3. Модель обработки событий Internet Explorer . . . . . . . . . . . . . . . . . . . . . 425
17.4. События мыши . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
17.5. События клавиатуры . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
17.6. Событие onload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
17.7. Искусственные события . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
18. Формы и элементы форм . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
18.1. Объект Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
18.2. Определение элементов формы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
18.3. Сценарии и элементы формы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
18.4. Пример верификации формы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
19. Cookies и механизм сохранения данных на стороне клиента . . . . . . . 472
19.1. Обзор cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
19.2. Сохранение cookie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
19.3. Чтение cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
19.4. Пример работы с cookie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
19.5. Альтернативы cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
19.6. Хранимые данные и безопасность . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
20. Работа с протоколом HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
20.1. Использование объекта XMLHttpRequest . . . . . . . . . . . . . . . . . . . . . . . . 495
20.2. Примеры и утилиты с объектом XMLHttpRequest . . . . . . . . . . . . . . . . . 502
20.3. Ajax и динамические сценарии . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
20.4. Взаимодействие с протоколом HTTP с помощью тега <script> . . . . . . 516
21. JavaScript и XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
21.1. Получение XMLдокументов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
21.2. Манипулирование XMLданными cредствами DOM API . . . . . . . . . . . 524
21.3. Преобразование XMLдокумента с помощью XSLT . . . . . . . . . . . . . . . . 528
21.4. Выполнение запросов к XMLдокументу с помощью
XPathвыражений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
12 Оглавление
21.5. Сериализация XMLдокумента . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
21.6. Разворачивание HTMLшаблонов с использованием XMLданных. . . 537
21.7. XML и вебслужбы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
21.8. E4X: EcmaScript для XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
22. Работа с графикой на стороне клиента. . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
22.1. Работа с готовыми изображениями . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
22.2. Графика и CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
22.3. SVG – масштабируемая векторная графика . . . . . . . . . . . . . . . . . . . . . . 562
22.4. VML – векторный язык разметки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
22.5. Создание графики с помощью тега <canvas> . . . . . . . . . . . . . . . . . . . . . 572
22.6. Создание графики средствами Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
22.7. Создание графики с помощью Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
23. Сценарии с Javaапплетами и Flashроликами . . . . . . . . . . . . . . . . . . . . 588
23.1. Работа с апплетами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
23.2. Работа с подключаемым Javaмодулем . . . . . . . . . . . . . . . . . . . . . . . . . . 592
23.3. Взаимодействие с JavaScriptсценариями из Java . . . . . . . . . . . . . . . . . 593
23.4. Взаимодействие с Flashроликами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
23.5. Сценарии во Flash 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
Часть III. Справочник по базовому JavaScript . . . . . . . . . . . . . . . . . . . . . 607
Часть IV. Справочник по клиентскому JavaScript . . . . . . . . . . . . . . . . . . 721
Алфавитный указатель . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
Предисловие
После выхода из печати четвертого издания книги «JavaScript. Подробное руко
водство» объектная модель документов (Document Object Model, DOM), представ
ляющая собой основу прикладного программного интерфейса (Application Pro
gramming Interface, API) для сценариев на языке JavaScript™, исполняющихся
на стороне клиента, была реализована достаточно полно, если не полностью,
в вебброузерах. Это означает, что разработчики вебприложений получили в свое
распоряжение универсальный прикладной программный интерфейс для работы
с содержимым вебстраниц на стороне клиента и зрелый язык (JavaScript 1.5),
остававшийся стабильным на протяжении последующих лет.
Сейчас интерес к JavaScript опять начинает расти. Теперь разработчики исполь
зуют JavaScript для создания сценариев, работающих по протоколу HTTP,
управляющих XMLданными и даже динамически создающих графические изо
бражения в вебброузере. Многие программисты с помощью JavaScript создают
большие программы и применяют достаточно сложные технологии программи
рования, такие как замыкания и пространства имен. Пятое издание полностью
пересмотрено с позиций вновь появившихся технологий Ajax и Web 2.0.
Что нового в пятом издании
В первой части книги, «Основы JavaScript», была расширена глава 8, описыва
ющая функции; в нее включен материал, охватывающий замыкания и вложен
ные функции. Информация о порядке создания собственных классов была до
полнена и выделена в отдельную главу 9. Глава 10 – это еще одна новая глава,
которая содержит сведения о пространствах имен, являющихся основой для раз
работки модульного программного кода многократного использования. Наконец,
глава 12 демонстрирует, как применять JavaScript при разработке сценариев на
языке Java. Здесь показано, как встраивать интерпретатор JavaScript в прило
жения на Java 6, как использовать JavaScript для создания Javaобъектов и как
вызывать методы этих объектов.
Во второй части книги, «Клиентский язык JavaScript», описываются прежняя
(уровня 0) объектная модель документа и стандарт DOM консорциума W3C. По
скольку в настоящее время модель DOM имеет универсальные реализации, отпа
ла необходимость в двух отдельных главах, где в предыдущем издании описыва
лись приемы работы с документами. Вторая часть книги подверглась самым су
щественным изменениям; в нее включен следующий новый материал:
• Глава 19 «Cookies и механизм сохранения данных на стороне клиента» до
полнена новой информацией о cookies и сведениями о методиках программи
рования, применяемых на стороне клиента.
14 Предисловие
• Глава 20 «Работа с протоколом HTTP» описывает, как выполнять HTTPза
просы с помощью такого мощного инструмента, как объект XMLHttpRequest,
который делает возможным создание Ajaxподобных вебприложений.
• Глава 21 «JavaScript и XML» демонстрирует, как средствами JavaScript орга
низовать создание, загрузку, синтаксический разбор, преобразование, вы
борку, сериализацию и извлечение данных из XMLдокументов. Кроме того,
рассматривается расширение языка JavaScript, получившее название E4X.
• Глава 22 «Работа с графикой на стороне клиента» описывает графические воз
можности языка JavaScript. Здесь рассматриваются как простейшие способы
создания анимированных изображений, так и достаточно сложные приемы
работы с графикой с использованием ультрасовременного тега <canvas>. Кро
ме того, здесь говорится о создании графики на стороне клиента средствами
подключаемых SVG, VML, Flash и Javaмодулей.
• Глава 23 «Сценарии с Javaапплетами и Flashроликами» рассказывает о под
ключаемых Flash и Javaмодулях. В этой главе объясняется, как создавать
Flashролики и Javaапплеты.
Часть III книги представляет собой справочник по прикладному интерфейсу ба
зового языка JavaScript. Изменения в этой части по сравнению с предыдущим
изданием весьма незначительные, что обусловлено стабильностью API. Если вы
читали 4е издание, вы найдете эту часть книги удивительно знакомой.
Существенные изменения коснулись организации справочного материала, опи
сывающего прикладной интерфейс объектной модели документа (DOM API), ко
торый ранее был выделен в самостоятельную часть отдельно от описания кли
ентского языка JavaScript. Теперь же оставлена единственная часть со справоч
ной информацией, относящейся к клиентскому языку JavaScript. Благодаря
этому отпала необходимость читать описание объекта Document в одной части,
а затем искать описание объекта HTMLDocument в другой. Справочный материал об
интерфейсах модели DOM, которые так и не были достаточно полно реализова
ны в броузерах, попросту убран. Так, интерфейс NodeIterator не поддерживается
в броузерах, поэтому его описание из этой книги исключено. Кроме того, акцент
смещен от сложных формальных определений DOMинтерфейсов к JavaScript
объектам, которые являются фактической реализацией этих интерфейсов. На
пример, метод getComputedStyle() теперь описывается не как метод интерфейса
AbstractView, а как метод объекта Window, что логичнее. Для JavaScriptпрограм
мистов, создающих клиентские сценарии, нет серьезных оснований вникать
в особенности интерфейса AbstractView, поэтому его описание было убрано из
справочника. Все эти изменения сделали справочную часть книги, посвящен
ную клиентскому языку JavaScript, более простой и удобной.
Порядок работы с книгой
Глава 1 представляет собой введение в язык JavaScript. Остальная часть книги
делится на четыре части. Первая часть, которая непосредственно следует за гла
вой 1, описывает основы языка JavaScript. Главы со 2 по 6 содержат достаточно
скучный материал, тем не менее прочитать его совершенно необходимо, т. к. он
охватывает самые основы, без знания которых невозможно начать изучение но
вого языка программирования:
Предисловие 15
• Глава 2 «Лексическая структура» описывает основные языковые конструкции.
• Глава 3 «Типы данных и значения» рассказывает о типах данных, поддержи
ваемых языком JavaScript.
• Глава 4 «Переменные» охватывает темы переменных, областей видимости
переменных и всего, что с этим связано.
• Глава 5 «Выражения и операторы» описывает выражения языка JavaScript
и документирует каждый оператор, поддерживаемый этим языком програм
мирования. Поскольку синтаксис JavaScript основан на синтаксисе языка
Java, который, в свою очередь, очень многое заимствовал из языков C и C++,
программисты, имеющие опыт работы с этими языками, могут лишь вкратце
ознакомиться с содержимым этой главы.
• Глава 6 «Инструкции» описывает синтаксис и порядок использования каж
дой JavaScriptинструкции. Программисты, имеющие опыт работы с языка
ми C, C++ и Java, могут пропустить не все, но некоторые разделы этой главы.
Последующие шесть глав первой части содержат куда более интересные сведе
ния. Они также описывают основы языка JavaScript, но охватывают те его час
ти, которые едва ли вам знакомы, даже если вам приходилось писать на языке C
или Java. Если вам требуется настоящее понимание JavaScript, к изучению ма
териала этих глав следует подходить с особой тщательностью.
• Глава 7 «Объекты и массивы» описывает объекты и массивы языка JavaScript.
• Глава 8 «Функции» рассказывает о том, как определяются функции, как они
вызываются, каковы их отличительные особенности в языке JavaScript.
• Глава 9 «Классы, конструкторы и прототипы» касается вопросов объектно
ориентированного программирования на языке JavaScript. Рассказывается
о том, как определяются функцииконструкторы для новых классов объек
тов и как работает механизм наследования на основе прототипов. Кроме того,
продемонстрирована возможность эмулирования традиционных идиом объ
ектноориентированного программирования на языке JavaScript.
• Глава 10 «Модули и пространства имен» показывает, как определяются про
странства имен в JavaScriptобъектах, и описывает некоторые практические
приемы, позволяющие избежать конфликтов имен в модулях.
• Глава 11 «Шаблоны и регулярные выражения» рассказывает о том, как ис
пользовать регулярные выражения в языке JavaScript для выполнения опе
раций поиска и замены по шаблону.
• Глава 12 «Разработка сценариев для Javaприложений» демонстрирует воз
можность встраивания интерпретатора JavaScript в Javaприложения и рас
сказывает, как JavaScriptпрограммы, работающие внутри Javaприложе
ний, могут обращаться к Javaобъектам. Эта глава представляет интерес
только для тех, кто программирует на языке Java.
Часть II книги описывает реализацию JavaScript в вебброузерах. Первые шесть
глав рассказывают об основных характеристиках клиентского JavaScript:
• Глава 13 «JavaScript в вебброузерах» рассказывает об интеграции JavaScript
в вебброузеры. Здесь броузеры рассматриваются как среда программирова
ния и описываются различные варианты встраивания программного Java
Scriptкода в вебстраницы для исполнения его на стороне клиента.
16 Предисловие
• Глава 14 «Работа с окнами броузера» описывает центральный элемент кли
ентского языка JavaScript – объект Window и рассказывает, как использовать
этот объект для управления окнами броузера.
• Глава 15 «Работа с документами» описывает объект Document и рассказывает,
как из JavaScript управлять содержимым, отображаемым в окне броузера.
Эта глава является наиболее важной во второй части.
• Глава 16 «CSS и DHTML» рассказывает о порядке взаимодействия между Java
Scriptкодом и таблицами CSSстилей. Здесь показано, как средствами Java
Script изменять стили, вид и положение элементов HTMLдокумента, созда
вая визуальные эффекты, известные как DHTML.
• Глава 17 «События и обработка событий» описывает события и порядок их
обработки, что является немаловажным для программ, ориентированных на
взаимодействие с пользователем.
• Глава 18 «Формы и элементы форм» посвящена тому, как работать с HTML
формами и отдельными элементами форм. Данная глава является логиче
ским продолжением главы 15, но обсуждаемая тема настолько важна, что
была выделена в самостоятельную главу.
Вслед за этими шестью главами следуют пять глав, содержащих более узкоспе
циализированный материал:
• Глава 19 «Cookies и механизм сохранения данных на стороне клиента» охва
тывает вопросы хранения данных на стороне клиента для последующего ис
пользования. В этой главе показано, как средствами HTTP манипулировать
cookies и как сохранять их с помощью соответствующих инструментов Inter
net Explorer и подключаемого Flashмодуля.
• Глава 20 «Работа с протоколом HTTP» демонстрирует, как управлять прото
колом HTTP из JavaScriptсценариев, как с помощью объекта XMLHttpRequest
отправлять запросы вебсерверам и получать от них ответы. Данная возмож
ность является краеугольным камнем архитектуры вебприложений, извест
ной под названием Ajax.
• Глава 21 «JavaScript и XML» описывает, как средствами JavaScript созда
вать, загружать, анализировать, преобразовывать и сериализовать XMLдо
кументы, а также как извлекать из них данные.
• Глава 22 «Работа с графикой на стороне клиента» рассказывает о средствах
JavaScript, ориентированных на работу с графикой. Здесь рассматриваются
как простейшие способы создания анимированных изображений, так и дос
таточно сложные приемы работы с графикой с использованием форматов
SVG (Scalable Vector Graphics – масштабируемая векторная графика) и VML
(Vector Markup Language – векторный язык разметки), тега <canvas> и под
ключаемых Flash и Javaмодулей.
• Глава 23 «Сценарии с Javaапплетами и Flashроликами» показывает, как
организовать взаимодействие JavaScriptкода с Javaапплетами и Flashро
ликами. Кроме того, в ней рассказывается, как обращаться к JavaScriptкоду
из Javaапплетов и Flashроликов.
Третья и четвертая части содержат справочный материал соответственно по ба
зовому и клиентскому языкам JavaScript. Здесь приводятся описания объектов,
методов и свойств в алфавитном порядке.
Предисловие 17
Типографские соглашения
В этой книге приняты следующие соглашения:
Курсив
Обозначает первое появление термина. Курсив также применяется для выде
ления адресов электронной почты, вебсайтов, FTPсайтов, имен файлов и ка
талогов, групп новостей.
Моноширинный шрифт
Применяется для форматирования программного кода на языке JavaScript,
HTMLлистингов и вообще всего, что непосредственно набирается на клавиа
туре при программировании.
Моноширинный жирный
Используется для выделения текста командной строки, который должен
быть введен пользователем.
Моноширинный курсив
Обозначает аргументы функций и элементы, которые в программе необходи
мо заменить реальными значениями.
Клавиши и элементы пользовательского интерфейса, такие как кнопка Назад
или меню Сервис, выделены шрифтом OfficinaSansC.
Использование программного кода примеров
Данная книга призвана оказать помощь в решении ваших задач. Вы можете сво
бодно использовать примеры программного кода из этой книги в своих приложе
ниях и в документации. Вам не нужно обращаться в издательство за разрешени
ем, если вы не собираетесь воспроизводить существенные части программного
кода. Например, если вы разрабатываете программу и задействуете в ней не
сколько отрывков программного кода из книги, вам не нужно обращаться за
разрешением. Однако в случае продажи или распространения компактдисков
с примерами из этой книги вам необходимо получить разрешение от издательст
ва O’Reilly. При цитировании данной книги или примеров из нее и при ответе на
вопросы получение разрешения не требуется. При включении существенных
объемов программного кода примеров из этой книги в вашу документацию вам
необходимо получить разрешение издательства.
Мы приветствуем, но не требуем добавлять ссылку на первоисточник при цити
ровании. Под ссылкой на первоисточник мы подразумеваем указание авторов,
издательства и ISBN. Например: «JavaScript: The Definitive Guide, by David Fla
nagan. Copyright 2006 O’Reilly Media, Inc., 9780596101992».
За получением разрешения на использование значительных объемов программ
ного кода примеров из этой книги обращайтесь по адресу
[email protected].
Отзывы и предложения
С вопросами и предложениями, касающимися этой книги, обращайтесь в изда
тельство:
18 Предисловие
O’Reilly Media
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 9989938 (в Соединенных Штатах Америки или в Канаде)
(707) 8290515 (международный)
(707) 8290104 (факс)
Список опечаток, файлы с примерами и другую дополнительную информацию
вы найдете на сайте книги по адресу:
http://www.oreilly.com/catalog/jscript5
Кроме того, все примеры, описываемые в книге, можно загрузить с сайта авторов:
http://www.davidflanagan.com/javascript5
Свои пожелания и вопросы технического характера отправляйте по адресу:
[email protected]
Дополнительную информацию о книгах, обсуждения, центр ресурсов издатель
ства O’Reilly вы найдете на сайте:
http://www.oreilly.com
Safari® Enabled
Если на обложке технической книги есть пиктограмма «Safari® En
abled», это означает, что книга доступна в Сети через O’Reilly Net
work Safari Bookshelf.
Safari предлагает намного лучшее решение, чем электронные книги. Это вирту
альная библиотека, позволяющая без труда находить тысячи лучших техниче
ских книг, вырезать и вставлять примеры кода, загружать главы и находить бы
стрые ответы, когда требуется наиболее верная и свежая информация. Она сво
бодно доступна по адресу http://safari.oreilly.com.
Благодарности
Брендан Эйх (Brendan Eich) из Mozilla – один из создателей и главный новатор
JavaScript. Я и другие JavaScriptпрограммисты в неоплатном долгу перед ним
за разработку JavaScript и за то, что в его сумасшедшем графике нашлось время
для ответов на наши вопросы, причем он даже требовал еще вопросов. Брендан
не только терпеливо отвечал на мои многочисленные вопросы, но и прочитал
первое и третье издания этой книги и дал очень полезные комментарии к ним.
Это руководство получило благословение первоклассных технических рецензен
тов, комментарии которых весьма способствовали улучшению этой книги. Ари
стотель Пагальциc (Aristotle Pagatzis) (http://plasmasturm.org) рецензировал
новый материал о функциях, а также главы, в которых в этом издании приво
дится описание классов и пространств имен. Он с особым тщанием просмотрел
программный код примеров и дал весьма ценные комментарии. Дуглас Крок
форд (Douglas Crockford) (http://www.crockford.com) рецензировал новый мате
риал о функциях и классах. Норрис Бойд (Norris Boyd), создатель интерпретато
ра Rhino для JavaScript, рецензировал главу, в которой описывается механизм
Предисловие 19
встраивания JavaScriptкода в Javaприложения. ПитерПауль Кох (PeterPaul
Koch) (http://www.quirksmode.org), Кристиан Хейльманн (Christian Heilmann)
(http://www.wait'till'i.com) и Кен Купер (Ken Cooper) рецензировали главы этой
книги, связанные с технологией Ajax. Тодд Дихендорф (Todd Ditchendorf) (http://
www.ditchnet.org) и Джефф Штернс (Geoff Stearns) (http://blog.deconcept.com) ре
цензировали главу, описывающую приемы работы с графикой на стороне клиен
та. Тодд был настолько любезен, что занялся поиском и исправлением ошибок
в программном коде примеров, а Джефф помог разобраться в технологиях Flash
и ActionScript. Наконец, Сандерс Клейнфельд (Sanders Kleinfeld) рецензировал
всю книгу, уделяя особое внимание деталям. Его предложения и исправления
сделали эту книгу более точной и понятной. Приношу слова искренней призна
тельности каждому, кто занимался рецензированием книги. Любые ошибки, ко
торые вам встретятся в книге, лежат на моей совести.
Я очень признателен рецензентам четвертого издания книги. Валдемар Хорват
(Waldermar Horwat) из Netscape рецензировал новый материал по JavaScript 1.5.
Материал по W3C DOM проверен Филиппом Ле Хегаре (Philippe Le Hegaret) из
W3C, ПитеромПаулем Кохом (PeterPaul Koch), Диланом Шиманом (Dylan
Schiemann) и Джеффом Ятсом (Jeff Yates). Джозеф Кесселман (Joseph Kesselman)
из IBM Research ничего не рецензировал, но очень помог мне, отвечая на вопросы
по W3C DOM.
Третье издание книги рецензировалось Бренданом Эйхом, Валдемаром Хорватом
и Вайдуром Аппарао (Vidur Apparao) из Netscape, Германом Вентером (Herman
Venter) из Microsoft, двумя независимыми JavaScriptразработчиками – Джеем
Ходжесом (Jay Hodges) и Анжело Сиригосом (Angelo Sirigos). Дэн Шейфер (Dan
Shafer) из CNET Builder.com выполнил некоторую предварительную работу по
третьему изданию. Его материал не нашел применения в этом издании, но при
надлежащие ему идеи и общие принципы принесли большую пользу. Норрис
Бойд (Norris Boyd) и Скотт Фурман (Scott Furman) из Netscape, а также Скотт
Айзекс (Scott Issacs) из Microsoft нашли время, чтобы поговорить со мной о буду
щем стандарте DOM. И наконец, доктор Танкред Хиршманн (Dr. Tankred Hirsch
mann) показал глубокое понимание хитросплетений JavaScript 1.2.
Второе издание много выиграло от помощи и комментариев Ника Томпсона
(Nick Thompson) и Ричарда Якера (Richard Yaker) из Netscape, доктора Шона
Каценбергера (Dr. Shon Katzenberger), Ларри Салливана (Larry Sullivan) и Дэй
ва С. Митчелла (Dave C. Mitchell) из Microsoft, Линн Роллинс (Lynn Rollins) из
R&B Communications. Первое издание рецензировалось Нилом Беркманом (Neil
Berkman) из Bay Networks, Эндрю Шульманом (Andrew Schulman) и Терри Ал
леном (Terry Allen) из O’Reilly & Associates.
Эта книга стала лучше еще и благодаря многочисленным редакторам, которые
над ней работали. Деб Камерон (Deb Cameron) – редактор этого издания, он особое
внимание уделял обновлению и удалению устаревшего материала. Паула Фергю
сон (Paula Ferguson) – редактор третьего и четвертого изданий. Френк Уиллисон
(Frank Willison) редактировал второе издание, а Эндрю Шульман – первое.
И наконец, мои благодарности Кристи – как всегда и за очень многое.
Дэвид Флэнаган
http://www.davidflanagan.com
апрель 2006
Введение в JavaScript
JavaScript – это интерпретируемый язык программирования с объектноориен
тированными возможностями. С точки зрения синтаксиса базовый язык Java
Script напоминает C, C++ и Java такими программными конструкциями, как ин
струкция if, цикл while и оператор &&. Однако это подобие ограничивается син
таксической схожестью. JavaScript – это нетипизированный язык, т. е. в нем не
требуется определять типы переменных. Объекты в JavaScript отображают име
на свойств на произвольные значения. Этим они больше напоминают ассоциа
тивные массивы Perl, чем структуры C или объекты C++ или Java. Механизм
объектноориентированного наследования JavaScript скорее похож на механизм
прототипов в таких малоизвестных языках, как Self, и сильно отличается от ме
ханизма наследования в C++ и Java. Как и Perl, JavaScript – это интерпретируе
мый язык, и некоторые его инструменты, например регулярные выражения
и средства работы с массивами, реализованы по образу и подобию языка Perl.
Ядро языка JavaScript поддерживает работу с такими простыми типами дан
ных, как числа, строки и булевы значения. Помимо этого он обладает встроен
ной поддержкой массивов, дат и объектов регулярных выражений.
Обычно JavaScript применяется в вебброузерах, а расширение его возможно
стей за счет введения объектов позволяет организовать взаимодействие с пользо
вателем, управлять вебброузером и изменять содержимое документа, отобража
емое в пределах окна вебброузера. Эта встроенная версия JavaScript запускает
сценарии, внедренные в HTMLкод вебстраниц. Как правило, эта версия назы
вается клиентским языком JavaScript, чтобы подчеркнуть, что сценарий ис
полняется на клиентском компьютере, а не на вебсервере.
В основе языка JavaScript и поддерживаемых им типов данных лежат междуна
родные стандарты, благодаря чему обеспечивается прекрасная совместимость
между реализациями. Некоторые части клиентского JavaScript формально стан
дартизированы, другие части стали стандартом дефакто, но есть части, которые
являются специфическими расширениями конкретной версии броузера. Совмес
тимость реализаций JavaScript в разных броузерах зачастую приносит немало
беспокойств программистам, использующим клиентский язык JavaScript.
1.1. Что такое JavaScript 21
В этой главе приводится краткий обзор JavaScript и дается вводная информа
ция, перед тем как перейти к фактическому изучению возможностей языка.
Кроме того, в данной главе на нескольких фрагментах кода на клиентском язы
ке JavaScript демонстрируется практическое вебпрограммирование.
1.1. Что такое JavaScript
Вокруг JavaScript довольно много дезинформации и путаницы. Прежде чем дви
гаться дальше в изучении JavaScript, важно развенчать некоторые распростра
ненные мифы, связанные с этим языком.
1.1.1. JavaScript – это не Java
Одно из наиболее распространенных заблуждений о JavaScript состоит в том, что
этот язык представляет собой упрощенную версию Java, языка программирова
ния, разработанного в компании Sun Microsystems. Кроме некоторой синтакси
ческой схожести и способности предоставлять исполняемое содержимое для веб
броузеров, эти два языка между собой ничто не связывает. Схожесть имен – не
более чем уловка маркетологов (первоначальное название языка – LiveScript –
было изменено на JavaScript в последнюю минуту). Однако JavaScript и Java мо
гут взаимодействовать друг с другом (подробнее об этом см. в главах 12 и 23).
1.1.2. JavaScript не простой язык
Поскольку JavaScript является интерпретируемым языком, очень часто он по
зиционируется как язык сценариев, а не как язык программирования, при этом
подразумевается, что языки сценариев проще и в большей степени ориентирова
ны не на программистов, а на обычных пользователей. В самом деле, при отсутст
вии контроля типов JavaScript прощает многие ошибки, которые допускают не
опытные программисты. Благодаря этому многие вебдизайнеры могут использо
вать JavaScript для решения ограниченного круга задач, выполняемых по точ
ным рецептам.
Однако за внешней простотой JavaScript скрывается полноценный язык про
граммирования, столь же сложный, как любой другой, и даже более сложный,
чем некоторые. Программисты, пытающиеся решать с помощью JavaScript не
тривиальные задачи, часто разочаровываются в процессе разработки изза того,
что недостаточно понимают возможности этого языка. Данная книга содержит
всеобъемлющее описание JavaScript, позволяющее вам стать искушенным зна
током. Если прежде вы пользовались справочниками по JavaScript, содержащи
ми готовые рецепты, вас наверняка удивит глубина и подробность изложения
материала в последующих главах.
1.2. Версии JavaScript
Подобно любой другой новой технологии программирования, развитие JavaScript
в самом начале шло быстрыми темпами. В предыдущих изданиях книги расска
зывалось о развитии языка версия за версией и попутно говорилось, в какой вер
сии какие новшества были введены. Однако к настоящему моменту язык стабили
22 Глава 1. Введение в JavaScript
зировался и был стандартизован ассоциацией европейских производителей ком
пьютеров (European Computer Manufacturer’s Association, ECMA).1 Реализации
этого стандарта охватывают интерпретатор JavaScript 1.5 компаний Netscape
и Mozilla Foundation, а также интерпретатор Jscript 5.5 корпорации Microsoft.
Любые вебброузеры, выпущенные после Netscape 4.5 или Internet Explorer 4,
поддерживают последнюю версию языка. На практике вам едва ли придется
столкнуться с интерпретаторами, не совместимыми с этими реализациями.
Обратите внимание, что в соответствии со стандартом ECMA262 язык офици
ально называется ECMAScript. Но это несколько неудобное название использу
ется только в случае, если необходимо явно сослаться на стандарт. Чисто техни
чески название «JavaScript» относится только к реализации, выполненной Net
scape и Mozilla Foundation. Однако на практике все предпочитают использовать
это название для обозначения любой реализации JavaScript.
После длительного периода стабильного существования JavaScript появились
некоторые признаки изменений. Вебброузер Firefox 1.5, выпущенный Mozilla
Foundation, включает в себя обновленный интерпретатор JavaScript версии 1.6.
Данная версия включает новые (нестандартные) методы работы с массивами, ко
торые описываются в разделе 7.7.10, а также обладает поддержкой расширения
E4X, которое описывается ниже.
В дополнение к спецификациям ECMA262, которые стандартизуют ядро языка
JavaScript, ассоциация ECMA разработала еще один стандарт, имеющий отно
шение к JavaScript, – ECMA357. В этой спецификации было стандартизовано
расширение JavaScript, известное под названием E4X, или ECMAScript for
XML. С помощью этого расширения в язык была добавлена поддержка нового
типа данных – XML – вместе с операторами и инструкциями, позволяющими
манипулировать XMLдокументами. К моменту написания этих строк расшире
ние E4X было реализовано только в JavaScript 1.6 и Firefox 1.5. В данной книге
нет формального описания E4X, однако в главе 21 дается расширенное введение
в форме практических примеров.
Несколько лет тому назад были внесены предложения к четвертой редакции
стандарта ECMA262, где предполагалось стандартизировать JavaScript 2.0. Эти
предложения предусматривают полную перестройку языка, включая введение
строгого контроля типов и механизма истинного наследования на основе клас
сов. До настоящего времени наблюдалось некоторое движение по направлению
к стандартизации JavaScript 2.0. Однако реализации, выполненные на основе
этих предложений, должны включать в себя поддержку языка Microsoft
JScript.NET, а также языков ActionScript 2.0 и ActionScript 3.0, используемых
в проигрывателе Adobe (ранее Macromedia) Flash. На текущий момент наблюдают
ся некоторые признаки, свидетельствующие о возобновлении движения к Java
Script 2.0, например выпуск JavaScript 1.6 можно расценивать как один из ша
гов в этом направлении. Предполагается, что любая новая версия языка будет
обратно совместима с версией, описываемой в этой книге. Но даже когда язык
JavaScript 2.0 будет стандартизирован, потребуется несколько лет, чтобы его
реализации появились во всех вебброузерах.
1 Стандарт ECMA262, версия 3 (доступен по адресу http://www.ecma'internatio'
nal.org/publications/files/ecma'st/ECMA'262.pdf).
1.3. Клиентский JavaScript 23
1.3. Клиентский JavaScript
Когда интерпретатор JavaScript встраивается в вебброузер, результатом явля
ется клиентский JavaScript. Это, безусловно, наиболее распространенный вари
ант JavaScript, и большинство людей, упоминая JavaScript, обычно подразуме
вают именно клиентский JavaScript. В этой книге клиентский язык JavaScript
описывается вместе с базовымJavaScript, который представляет собой подмно
жество клиентского JavaScript.
Клиентский JavaScript включает в себя интерпретатор JavaScript и объектную
модель документа (Document Object Model, DOM), определяемую вебброузером.
Документы могут содержать JavaScriptсценарии, которые в свою очередь могут
использовать модель DOM для модификации документа или управления способом
его отображения. Другими словами, можно сказать, что клиентский JavaScript
позволяет определить поведение статического содержимого вебстраниц. Клиент
ский JavaScript является основой таких технологий разработки вебприложений,
как DHTML (глава 16), и таких архитектур, как Ajax (глава 20). Введение
к главе 13 включает обзор большинства возможностей клиентского JavaScript.
Спецификация ECMA262 определила стандартную версию базового языка Java
Script, а организация World Wide Web Consortium (W3C) опубликовала специфи
кацию DOM, стандартизующую возможности, которые броузер должен поддер
живать в своей объектной модели. (В главах 15, 16 и 17 содержится более под
робное обсуждение этого стандарта.) Основные положения стандарта W3C DOM
достаточно полно поддерживаются наиболее распространенными броузерами за
одним важным исключением – Microsoft Internet Explorer; в этом броузере от
сутствует поддержка механизма обработки событий.
1.3.1. Примеры использования клиентского JavaScript
Вебброузер, оснащенный интерпретатором JavaScript, позволяет распространять
через Интернет исполняемое содержимое в виде JavaScriptсценариев. В приме
ре 1.1 показана простая программа на языке JavaScript, которая и представляет
собой сценарий, встроенный в вебстраницу.
Пример 1.1. Простая программа на языке JavaScript
<html>
<head><title>Факториалы</title></head>
<body>
<h2>Таблица факториалов</h2>
<script>
var fact = 1;
for(i = 1; i < 10; i++) {
fact = fact*i;
document.write(i + "! = " + fact + "<br>");
}
</script>
</body>
</html>
После загрузки в броузер, поддерживающий JavaScript, этот сценарий выдаст
результат, показанный на рис. 1.1.
24 Глава 1. Введение в JavaScript
Рис. 1.1. Веб'страница, сгенерированная с помощью JavaScript
Как видно из этого примера, для встраивания JavaScriptкода в HTMLфайл были
использованы теги <script> и </script>. О теге <script> подробнее рассказывается
в главе 13. Главное, что демонстрируется в данном примере, – это использование
метода document.write().1 Этот метод позволяет динамически выводить HTML
текст внутри HTMLдокумента по мере его загрузки вебброузером.
JavaScript обеспечивает возможность управления не только содержимым HTML
документов, но и их поведением. Другими словами, JavaScriptпрограмма мо
жет реагировать на действия пользователя: ввод значения в текстовое поле или
щелчок мышью в области изображения в документе. Это достигается путем опре
деления обработчиков событий для документа – фрагментов JavaScriptкода,
исполняемых при возникновении определенного события, например щелчка на
кнопке. В примере 1.2 показан простой фрагмент HTMLкода, который включа
ет в себя обработчик события, вызываемый в ответ на такой щелчок.
Пример 1.2. HTML'кнопка с обработчиком события на языке JavaScript
<button onclick="alert('Был зафиксирован щелчок на кнопке');">
Щелкни здесь
</button>
На рис. 1.2 показан результат щелчка на кнопке.
Атрибут onclick из примера 1.2 – это строка JavaScriptкода, исполняемого, ко
гда пользователь щелкает на кнопке. В данном случае обработчик события on
click вызывает функцию alert(). Как видно из рис. 1.2, функция alert() выво
дит диалоговое окно с указанным сообщением.
Примеры 1.1 и 1.2 демонстрируют лишь простейшие возможности клиентского
JavaScript. Реальная его мощь состоит в том, что сценарии имеют доступ к со
1 Метод – это объектноориентированный термин, обозначающий функцию или
процедуру.
1.3. Клиентский JavaScript 25
Рис. 1.2. Отклик JavaScript на событие
держимому HTMLдокументов. В примере 1.3 приводится листинг полноценной
нетривиальной JavaScriptпрограммы. Программа вычисляет месячный платеж
по закладной на дом или другой ссуде исходя из размера ссуды, процентной
ставки и периода выплаты. Программа считывает данные, введенные пользова
телем в поля HTMLформы, выполняет расчет на основании введенных данных,
после чего отображает полученные результаты.
На рис. 1.3 показана HTMLформа в окне вебброузера. Как видно из рисунка,
HTMLдокумент содержит саму форму и некоторый дополнительный текст. Од
нако рисунок – это лишь статический снимок окна программы. Благодаря Java
Scriptкоду она становится динамической: как только пользователь изменяет
сумму ссуды, процентную ставку или количество платежей, JavaScriptкод за
ново вычисляет месячный платеж, общую сумму платежей и общий процент,
уплаченный за все время ссуды.
Первая половина примера – это HTMLформа, аккуратно отформатированная
с помощью HTMLтаблицы. Обратите внимание, что обработчики событий
Рис. 1.3. Калькулятор платежей по ссуде на языке JavaScript
26 Глава 1. Введение в JavaScript
onchange и onclick определены лишь для нескольких элементов формы. Веббро
узер запускает эти обработчики в тот момент, когда пользователь изменяет вход
ные данные или щелкает на кнопке Рассчитать, отображаемой на форме. Во всех
этих случаях значением атрибута обработчика события является строка Java
Scriptкода calculate(). Вызываемый обработчик события исполняет этот код,
приводящий к вызову функции calculate().
Функция calculate() определена во второй половине примера внутри тега <script>.
Функция читает введенные пользователем данные из формы, выполняет мате
матические действия, требуемые для вычисления платежей по ссуде, и отобра
жает результаты этих действий внутри тегов <span>, каждый из которых имеет
уникальный идентификатор, определяемый атрибутом id.
Пример 1.3 прост, но на его внимательное рассмотрение стоит потратить время.
Вам не обязательно сейчас понимать весь JavaScriptкод, однако комментарии
в HTML, CSS и JavaScriptкоде, а также внимательное изучение этого примера
должны дать вам хорошее представление о том, как выглядят программы на
клиентском языке JavaScript.1
Пример 1.3. Вычисление платежей по ссуде с помощью JavaScript
<html>
<head>
<title>калькулятор выплат по ссуде на JavaScript</title>
<style>
/* Это каскадная таблица стилей: с ее помощью определяется внешний вид документа */
.result { fontweight: bold; } /*стиль отображения элементов с class="result"*/
#payment { textdecoration: underline; } /* для элемента с id="payment" */
</style>
</head>
<body>
<!—
Это HTMLформа, дающая пользователю возможность вводить
данные и с помощью JavaScript показывать ему результат вычислений.
Элементы формы для улучшения их внешнего вида помещены в таблицу.
Сама форма имеет имя "loandata", а поля в форме – такие имена,
как "interest" и "years". Эти имена полей используются
в JavaScriptкоде, следующем за кодом формы.
Обратите внимание: для некоторых элементов формы
определены обработчики событий "onchange" и "onclick".
В них заданы строки JavaScriptкода, выполняемого
при вводе данных или щелчке на кнопке.
>
<form name="loandata">
<table>
<tr><td><b>Введите данные о ссуде:</b></td></tr>
<tr>
1 Если интуиция подсказывает вам, что смешивать HTML, CSS и JavaScriptкод,
как в данном примере, не очень хорошо, знайте, что вы не одиноки. В настоящее
время в кругах, связанных с вебдизайном, наблюдается тенденция выделения
содержимого, представления и поведения в отдельные файлы. Как это сделать,
рассказывается в разделе 13.1.5 главы 13.
1.3. Клиентский JavaScript 27
<td>1) Размер ссуды (в любой валюте):</td>
<td><input type="text" name="principal" onchange="calculate();"></td>
</tr>
<tr>
<td>2) Годовой процент:</td>
<td><input type="text" name="interest" onchange="calculate( );"></td>
</tr>
<tr>
<td>3) Срок ссуды в годах:</td>
<td><input type="text" name="years" onchange="calculate( );"></td>
</tr>
<tr><td></td>
<td><input type="button" value="Рассчитать"
onclick="calculate( );"></td>
</tr>
<tr><td><b>Сведения о платежах:</b></td></tr>
<tr>
<td>4) Ежемесячный платеж:</td>
<td>$<span class="result" id="payment"></span></td>
</tr>
<tr>
<td>5) Общая сумма платежей:</td>
<td>$<span class="result" id="total"></span></td>
</tr>
<tr>
<td>6) Общая сумма платежей по процентам:</td>
<td>$<span class="result" id="totalinterest"></span></td>
</tr>
</table>
</form>
<script language="JavaScript">
/*
* Это JavaScriptфункция, которая заставляет пример работать.
* Обратите внимание: в этом сценарии определяется функция calculate(),
* вызываемая обработчиками событий в форме. Функция извлекает значения
* из полей <input> формы, используя имена, определенные в коде, который
* приведен ранее. Результаты выводятся в именованные элементы <span>
*/
function calculate( ) {
// Получаем пользовательские данные из формы. Предполагаем, что данные
// являются корректными. Преобразуем процентную ставку из процентов
// в десятичное значение. Преобразуем период платежа
// в годах в количество месячных платежей.
var principal = document.loandata.principal.value;
var interest = document.loandata.interest.value / 100 / 12;
var payments = document.loandata.years.value * 12;
// Теперь вычисляется сумма ежемесячного платежа.
var x = Math.pow(1 + interest, payments);
var monthly = (principal*x*interest)/(x1);
// Получить ссылки на именованные элементы <span> формы.
var payment = document.getElementById("payment");
var total = document.getElementById("total");
28 Глава 1. Введение в JavaScript
var totalinterest = document.getElementById("totalinterest");
// Убедиться, что результат является конечным числом. Если это так –
// отобразить результаты, определив содержимое каждого элемента <span>.
if (isFinite(monthly)) {
payment.innerHTML = monthly.toFixed(2);
total.innerHTML = (monthly * payments).toFixed(2);
totalinterest.innerHTML = ((monthly*payments)principal).toFixed(2);
}
// В противном случае данные, введенные пользователем, повидимому
// были некорректны, поэтому ничего не выводится.
else {
payment.innerHTML = "";
total.innerHTML = "";
totalinterest.innerHTML = "";
}
}
</script>
</body>
</html>
1.4. Другие области использования JavaScript
JavaScript – это язык программирования общего назначения, и его использова
ние не ограничено вебброузерами. Изначально JavaScript разрабатывался
с прицелом на встраивание в любые приложения и предоставление возможности
исполнять сценарии. С самых первых дней вебсерверы компании Netscape
включали в себя интерпретатор JavaScript, что позволяло исполнять JavaScript
сценарии на стороне сервера. Аналогичным образом в дополнение к Internet Ex
plorer корпорация Microsoft использует интерпретатор JScript в своем вебсер
вере IIS и в продукте Windows Scripting Host. Компания Adobe задействует про
изводный от JavaScript язык для управления своим проигрывателем Flashфай
лов. Компания Sun также встроила интерпретатор JavaScript в дистрибутив
Java 6.0, что существенно облегчает возможность встраивания сценариев в лю
бое Javaприложение (о том, как это делается, рассказывается в главе 12).
И Netscape, и Microsoft сделали доступными свои реализации интерпретаторов
JavaScript для компаний и программистов, желающих включить их в свои при
ложения. Интерпретатор, созданный в компании Netscape, был выпущен как
свободно распространяемое ПО с открытыми исходными текстами и ныне досту
пен через организацию Mozilla (http://www.mozilla.org/js/). Mozilla фактически
распространяет две разные версии интерпретатора JavaScript 1.5: один написан
на языке C и называется SpiderMonkey, другой написан на языке Java и, что
весьма лестно для автора книги, называется Rhino (носорог).
Если вам придется писать сценарии для приложений, включающих интерпрета
тор JavaScript, первая половина книги, где описываются основы этого языка,
будет для вас особенно полезна. Однако информация из глав, в которых описы
ваются особенности конкретных вебброузеров, скорее всего, будет непримени
ма для ваших сценариев.
1.5. Изучение JavaScript 29
1.5. Изучение JavaScript
Реальное изучение нового языка программирования невозможно без написания
программ. Рекомендую вам при чтении этой книги опробовать возможности Java
Script в процессе их изучения. Вот несколько приемов, призванных облегчить
эти эксперименты.
Наиболее очевидный подход к изучению JavaScript – это написание простых
сценариев. Одно из достоинств клиентского JavaScript состоит в том, что любой,
кто имеет вебброузер и простейший текстовый редактор, имеет и полноценную
среду разработки. Для того чтобы начать писать программы на JavaScript, нет
необходимости в покупке или загрузке специального ПО.
Например, чтобы вместо факториалов вывести последовательность чисел Фибо
наччи, пример 1.1 можно переписать следующим образом:
<script>
document.write("<h2>Числа Фибоначчи </h2>");
for (i=0, j=1, k=0, fib =0; i<50; i++, fib=j+k, j=k, k=fib){
document.write("Fibonacci (" + i + ") = " + fib);
document.write("<br>");
}
</script>
Этот отрывок может показаться запутанным (и не волнуйтесь, если вы пока не
понимаете его), но для того чтобы поэкспериментировать с подобными коротки
ми программами, достаточно набрать код и запустить его в вебброузере в каче
стве файла с локальным URLадресом. Обратите внимание, что для вывода ре
зультатов вычислений используется метод document.write(). Это полезный прием
при экспериментах с JavaScript. В качестве альтернативы для отображения тек
стового результата в диалоговом окне можно применять метод alert():
alert("Fibonacci (" + i + ") = " + fib);
Отметьте, что в подобных простых экспериментах с JavaScript можно опускать
теги <html>, <head> и <body> в HTMLфайле.
Для еще большего упрощения экспериментов с JavaScript можно использовать
URLадрес со спецификатором псевдопротокола javascript: для вычисления зна
чения JavaScriptвыражения и получения результата. Такой URLадрес состоит
из спецификатора псевдопротокола (javascript:), за которым указывается произ
вольный JavaScriptкод (инструкции отделяются одна от другой точками с запя
той). Загружая URLадрес с псевдопротоколом, броузер просто исполняет Java
Scriptкод. Значение последнего выражения в таком URLадресе преобразуется
в строку, и эта строка выводится вебброузером в качестве нового документа. На
пример, для того чтобы проверить свое понимание некоторых операторов и инст
рукций языка JavaScript, можно набрать следующие URLадреса в адресном по
ле вебброузера:
javascript:5%2
javascript:x = 3; (x < 5)? "значение x меньше": "значение x больше"
javascript:d = new Date(); typeof d;
javascript:for(i=0,j=1,k=0,fib=1; i<5; i++,fib=j+k,k=j,j=fib) alert(fib);
javascript:s=""; for(i in navigator) s+=i+":"+navigator[i]+"\n"; alert(s);
30 Глава 1. Введение в JavaScript
В вебброузере Firefox однострочные сценарии вводятся в JavaScriptконсоли,
доступ к которой можно получить из меню Инструменты. Просто введите выраже
ние или инструкцию, которую требуется проверить. При использовании Java
Scriptконсоли спецификатор псевдопротокола (javascript:) можно опустить.
Не любой код, написанный вами при изучении JavaScript, будет работать так,
как ожидается, и вам захочется его отладить. Базовая методика отладки Java
Scriptкода совпадает с методикой для многих других языков: вставка в код ин
струкций, которые будут выводить значения нужных переменных так, чтобы
можно было понять, что же на самом деле происходит. Как мы уже видели, ино
гда для этих целей можно использовать метод document.write() или alert(). (Бо
лее сложный способ отладки, основанный на выводе отладочных сообщений
в файл, приводится в примере 15.9.)
В отладке также может быть полезен цикл for/in (описанный в главе 6). Напри
мер, его можно применять вместе с методом alert() для написания функции,
отображающей имена и значения всех свойств объекта. Такая функция может
быть удобна при изучении языка или при отладке кода.
Если вам постоянно приходится сталкиваться с ошибками в JavaScriptсценари
ях, вероятно, вас заинтересует настоящий отладчик JavaScript. В Internet Explo
rer можно воспользоваться отладчиком Microsoft Script Debugger, в Firefox – мо
дулем расширения, известным под названием Venkman. Описание этих инстру
ментов выходит далеко за рамки темы этой книги, но вы без труда найдете его
в Интернете, воспользовавшись какойнибудь поисковой системой. Еще один ин
струмент, который, строго говоря, не является отладчиком, – это jslint; он спосо
бен отыскивать распространенные ошибки в JavaScriptкоде программ (http://
jslint.com).
Основы JavaScript
Данная часть книги включает главы со 2 по 12 и описывает базовый язык Java
Script. Этот материал задуман как справочный, и прочитав главы этой части
один раз, вы, возможно, будете неоднократно возвращаться к ним, чтобы осве
жить в памяти некоторые особенности языка.
• Глава 2 «Лексическая структура»
• Глава 3 «Типы данных и значения»
• Глава 4 «Переменные»
• Глава 5 «Выражения и операторы»
• Глава 6 «Инструкции»
• Глава 7 «Объекты и массивы»
• Глава 8 «Функции»
• Глава 9 «Классы, конструкторы и прототипы»
• Глава 10 «Модули и пространства имен»
• Глава 11 «Шаблоны и регулярные выражения»
• Глава 12 «Разработка сценариев для Javaприложений»
Лексическая структура
Лексическая структура языка программирования – это набор элементарных
правил, определяющих, как пишутся программы на этом языке. Это низкоуров
невый синтаксис языка; он задает вид имен переменных, символы, используе
мые для комментариев, и то, как одна инструкция отделяется от другой. Эта ко
роткая глава документирует лексическую структуру JavaScript.
2.1. Набор символов
При написании программ на JavaScript используется набор символов Unicode.
В отличие от 7разрядной кодировки ASCII, подходящей только для английско
го языка, и 8разрядной кодировки ISO Latin1, подходящей только для англий
ского и основных западноевропейских языков, 16разрядная кодировка Unicode
обеспечивает представление практически любого письменного языка. Эта воз
можность важна для интернационализации и особенно для программистов, не
говорящих на английском языке.
Американские и другие англоговорящие программисты обычно пишут програм
мы с помощью текстового редактора, поддерживающего только кодировки ASCII
или Latin1, и потому у них нет простого доступа к полному набору символов
Unicode. Однако никаких трудностей это не порождает, поскольку кодировки
ASCII и Latin1 представляют собой подмножества Unicode, и любая JavaScript
программа, написанная с помощью этих наборов символов, абсолютно коррект
на. Программисты, привыкшие рассматривать символы как 8разрядные значе
ния, могут быть сбиты с толку, узнав, что JavaScript представляет каждый сим
вол с помощью двух байтов, однако на самом деле для программиста это обстоя
тельство остается незаметным и может просто игнорироваться.
Стандарт ECMAScript v3 допускает наличие Unicodeсимволов в любом месте
JavaScriptпрограммы. Однако версии 1 и 2 стандарта допускают использование
Unicodeсимволов только в комментариях и строковых литералах, заключен
ных в кавычки, все остальные составляющие программы ограничены набором
34 Глава 2. Лексическая структура
ASCIIсимволов.1 Версии JavaScript, предшествующие стандарту ECMAScript,
обычно вообще не поддерживают Unicode.
2.2. Чувствительность к регистру
JavaScript – это язык, чувствительный к регистру. Это значит, что ключевые
слова, переменные, имена функций и любые другие идентификаторы языка
должны всегда содержать одинаковые наборы прописных и строчных букв. На
пример, ключевое слово while должно набираться как «while», а не «While» или
«WHILE». Аналогично online, Online, OnLine и ONLINE – это имена четырех разных
переменных.
Заметим, однако, что язык HTML, в отличие от JavaScript, не чувствителен к ре
гистру. По причине близкой связи HTML и клиентского JavaScript это различие
может привести к путанице. Многие JavaScriptобъекты и их свойства имеют те
же имена, что и теги и атрибуты языка HTML, которые они обозначают. Если
в HTML эти теги и атрибуты могут набираться в любом регистре, то в JavaScript
они обычно должны набираться строчными буквами. Например, атрибут обра
ботчика события onclick чаще всего задается в HTML как onClick, однако в Java
Scriptкоде (или в XHTMLдокументе) он должен быть обозначен как onclick.
2.3. Символыразделители и переводы строк
JavaScript игнорирует пробелы, табуляции и переводы строк, присутствующие
между лексемами в программе. Поэтому символы пробела, табуляции и перевода
строки могут без ограничений использоваться в исходных текстах программ для
форматирования и придания им удобочитаемого внешнего вида. Однако имеется
небольшое ограничение, которое касается символов перевода строк и о котором
рассказывается в следующем разделе.
2.4. Необязательные точки с запятой
Простые JavaScriptинструкции обычно завершаются символами точки с запя
той (;), как в C, C++ и Java. Точка с запятой служит для отделения инструкций
друг от друга. Однако в JavaScript точку с запятой можно не ставить, если каж
дая инструкция помещается в отдельной строке. Например, следующий фраг
мент может быть записан без точек с запятой:
a = 3;
b = 4;
1 Для русскоязычных программистов это означает, что а) русскоязычный текст мо
жет появляться только в комментариях и в строковых литералах, предназначен
ных непосредственно для вывода; б) такие тексты представляются в кодировке
UTF16 (Unicode – это единая система связывания символов любого языка с одно
значным численным кодом, а для кодирования этого численного кода могут при
меняться различные кодировки, например UTF8, UTF16 и др.); в) все остальные
лексемы программы – операторы, имена переменных и т. д. – должны состоять из
латинских литер; это достаточно обычная и привычная практика и для других
языков программирования. – Примеч. науч. ред.
2.5. Комментарии 35
Однако если обе инструкции расположены в одной строке, то первая точка с за
пятой должна присутствовать обязательно:
a = 3; b = 4;
Пропуск точек с запятой нельзя признать правильной практикой программиро
вания, и поэтому желательно выработать привычку их использовать.
Теоретически JavaScript допускает переводы строк между любыми двумя лексе
мами, но привычка синтаксического анализатора JavaScript автоматически
вставлять точки с запятой за программиста приводит к некоторым исключени
ям из этого правила. Если в результате разделения строки программного кода та
ее часть, которая предшествует символу перевода, оказывается законченной ин
струкцией, синтаксический анализатор JavaScript может решить, что точка
с запятой пропущена случайно, и вставить ее, изменив смысл программы. К по
добным требующим внимания ситуациям относятся, среди прочих, инструкции
return, break и continue (описанные в главе 6). Рассмотрим, например, следую
щий фрагмент:
return
true;
Синтаксический анализатор JavaScript предполагает, что программист имеет
в виду следующее:
return;
true;
Хотя на самом деле программист, видимо, хотел написать
return true;
Вот случай, когда следует быть внимательным, – данный код не вызовет синтак
сической ошибки, но приведет к неочевидному сбою. Похожая неприятность
возникает, если написать:
break
outerloop;
JavaScript вставляет точку с запятой после ключевого слова break, что вызывает
синтаксическую ошибку при попытке интерпретировать следующую строку. По
аналогичным причинам постфиксные операторы ++ и (см. главу 5) должны
располагаться в той же строке, что и выражения, к которым они относятся.
2.5. Комментарии
JavaScript, как и Java, поддерживает комментарии и в стиле C++, и в стиле C.
Любой текст, присутствующий между символами // и концом строки, рассмат
ривается как комментарий и игнорируется JavaScript. Любой текст между сим
волами /* и */ также рассматривается как комментарий. Эти комментарии в сти
ле C могут состоять из нескольких строк и не могут быть вложенными. Следую
щие строки кода представляют собой корректные JavaScriptкомментарии:
// Это однострочный комментарий.
/* Это тоже комментарий */ // а это другой комментарий.
/*
36 Глава 2. Лексическая структура
* Это еще один комментарий.
* Он располагается в нескольких строках.
*/
2.6. Литералы
Литерал – это значение, указанное непосредственно в тексте программы. Ниже
приведены примеры литералов:
12 // Число двенадцать
1.2 // Число одна целая две десятых
"hello world" // Строка текста
'Hi' // Другая строка
true // Логическое значение
false // Другое логическое значение
/javascript/gi // Регулярное выражение (для поиска по шаблону)
null // Отсутствие объекта
В ECMAScript v3 также поддерживаются выражения, которые могут служить
в качестве массивовлитералов и объектовлитералов. Например:
{ x:1, y:2 } // Инициализатор объекта
[1,2,3,4,5] // Инициализатор массива
Литералы – важная часть любого языка программирования, поскольку напи
сать программу без них невозможно. Различные литералы JavaScript описаны
в главе 3.
2.7. Идентификаторы
Идентификатор – это просто имя. В JavaScript идентификаторы выступают
в качестве названий переменных и функций, а также меток некоторых циклов.
Правила формирования допустимых идентификаторов совпадают с правилами
Java и многих других языков программирования. Первым символом должна быть
буква, символ подчеркивания (_) или знак доллара ($).1 Последующие символы
могут быть любой буквой, цифрой, символом подчеркивания или знаком долла
ра. (Цифра не может быть первым символом, т. к. тогда интерпретатору труднее
отличать идентификаторы от чисел.) Примеры допустимых идентификаторов:
i
my_variable_name
v13
_dummy
$str
В ECMAScript v3 идентификаторы могут содержать буквы и цифры из полного
набора символов Unicode. До этой версии стандарта JavaScriptидентификаторы
были ограничены набором ASCII. ECMAScript v3 также допускает наличие
1 Знак $ недопустим в идентификаторах для более ранних версий, чем Java
Script 1.1. Этот знак предназначен только для средств генерации кода, поэтому
следует избегать его использования в идентификаторах.
2.8. Зарезервированные слова 37
в идентификаторах escapeпоследовательностей Unicode – символов \u, за кото
рыми расположены 4 шестнадцатеричные цифры, обозначающие 16разрядный
код символа. Например, идентификатор π можно записать как \u03c0. Этот син
таксис неудобен, но обеспечивает возможность транслитерации JavaScriptпро
грамм с Unicodeсимволами в форму, допускающую работу с ними в текстовых
редакторах и других средствах, не поддерживающих полный набор Unicode.
Наконец, идентификаторы не могут совпадать ни с одним из ключевых слов,
предназначенных в JavaScript для других целей. В следующем разделе перечис
лены ключевые слова, зарезервированные для специальных нужд JavaScript.
2.8. Зарезервированные слова
В JavaScript имеется несколько зарезервированных слов. Они не могут быть
идентификаторами (именами переменных, функций и меток циклов) в Java
Scriptпрограммах. В табл. 2.1 перечислены ключевые слова, стандартизован
ные в ECMAScript v3. Для интерпретатора JavaScript они имеют специальное
значение, т. к. являются частью синтаксиса языка.
Таблица 2.1. Зарезервированные ключевые слова JavaScript
break do if switch typeof
case else in this var
catch false instanceof throw void
continue finally new true while
default for null try with
delete function return
В табл. 2.2 перечислены другие ключевые слова. В настоящее время они в Java
Script не используются, но зарезервированы ECMAScript v3 в качестве возмож
ных будущих расширений языка.
Таблица 2.2. Слова, зарезервированные для расширений ECMA
abstract double goto native static
Boolean enum implements package super
byte export import private synchronized
char extends int protected throws
class final interface public transient
const float long short volatile
debugger
Помимо нескольких только что перечисленных формально зарезервированных
слов текущие проекты стандарта ECMAScript v4 рассматривают применение
ключевых слов as, is, namespace и use. Хотя текущие интерпретаторы JavaScript
не запрещают использование этих четырех слов в качестве идентификаторов,
однако все равно следует этого избегать.
38 Глава 2. Лексическая структура
Кроме того, следует избегать использования идентификаторов глобальных пере
менных и функций, предопределенных в языке JavaScript. Если попытаться
создать переменную или функцию с таким идентификатором, то это будет при
водить либо к ошибке (если свойство определено как доступное только для чте
ния), либо к переопределению глобальной переменной или функции, чего точно
не стоит делать, если вы не стремитесь к этому преднамеренно. В табл. 2.3 пере
числены имена глобальных переменных и функций, определяемых стандартом
ECMAScript v 3. Конкретные реализации могут содержать свои предопределен
ные элементы с глобальной областью видимости, кроме того, каждая конкрет
ная платформа JavaScript (клиентская, серверная и прочие) может еще больше
расширять этот список.1
Таблица 2.3. Другие идентификаторы, которых стоит избегать
arguments encodeURI Infinity Object String
Array Error isFinite parseFloat SyntaxError
Boolean escape isNaN parseInt TypeError
Date eval Math RangeError undefined
decodeURI EvalError NaN ReferenceError unescape
decodeURIcomponent Function Number RegExp URIError
1 При описании объекта Window в четвертой части книги приведен список глобаль
ных переменных и функций, определенных в клиентском JavaScript.
Типы данных и значения
Компьютерные программы работают, манипулируя значениями (values), таки
ми как число 3,14 или текст «Hello World». Типы значений, которые могут быть
представлены и обработаны в языке программирования, известны как типы
данных (data types), и одной из наиболее фундаментальных характеристик язы
ка программирования является поддерживаемый им набор типов данных. Java
Script позволяет работать с тремя элементарными типами данных: числами,
строками текста (или просто строками) и значениями логической истинности
(или просто логическими значениями). В JavaScript также определяются два
тривиальных типа данных, null и undefined, каждый из которых определяет
только одно значение.
В дополнение к этим элементарным типам данных JavaScript поддерживает со
ставной тип данных, известный как объект (object). Объект (т. е. член объектно
го типа данных) представляет собой коллекцию значений (либо элементарных,
таких как числа и строки, либо сложных, например других объектов). Объекты
в JavaScript имеют двойственную природу: объект может быть представлен как
неупорядоченная коллекция именованных значений или как упорядоченная
коллекция пронумерованных значений. В последнем случае объект называется
массивом (array). Хотя в JavaScript объекты и массивы в основе являются од
ним типом данных, они ведут себя совершенно поразному, и в этой книге рас
сматриваются как отдельные типы.
В JavaScript определен еще один специальный тип объекта, известный как функ'
ция (function). Функция – это объект, с которым связан исполняемый код. Функ
ция может вызываться (invoked) для выполнения определенной операции. По
добно массивам, функции ведут себя не так, как другие виды объектов, и в Java
Script определен специальный синтаксис для работы с ними. Поэтому мы будем
рассматривать функции независимо от объектов и массивов.
Помимо функций и массивов в базовом языке JavaScript определено еще не
сколько специальных видов объектов. Эти объекты представляют собой не новые
типы данных, а лишь новые классы (classes) объектов. Класс Date определяет объ
екты, представляющие даты, класс RegExp – объекты, представляющие регуляр
40 Глава 3. Типы данных и значения
ные выражения (мощное средство поиска по шаблону, описываемое в главе 11),
и класс Error – объекты, представляющие синтаксические ошибки и ошибки
времени выполнения, которые могут возникать в JavaScriptпрограмме.
В оставшейся части этой главы подробно описан каждый из элементарных типов
данных. В ней также приведены начальные сведения об объектах, массивах
и функциях, которые более подробно рассмотрены в главах 7 и 8. И наконец, в ней
приведен обзор классов Date, RegExp и Error, подробно документируемых в III час
ти книги. Глава содержит некоторые узкоспециализированные подробности, ко
торые можно пропустить при первом прочтении.
3.1. Числа
Числа – это основной тип данных, не требующий особых пояснений. JavaScript
отличается от таких языков программирования, как C и Java, тем, что не делает
различия между целыми и вещественными значениями. Все числа в JavaScript
представляются 64разрядными вещественными значениями (с плавающей точ
кой), формат которых определяется стандартом IEEE 754.1 Этот формат спосо
бен представлять числа от ±1,7976931348623157 × 10308 до ±5 × 10324.
Число, находящееся непосредственно в коде JavaScriptпрограммы, называется
числовым литералом. JavaScript поддерживает числовые литералы нескольких
форматов, описанных в последующих разделах. Обратите внимание: любому чи
словому литералу может предшествовать знак «минус» (), делающий числа от
рицательными. Однако фактически минус представляет собой унарный опера
тор смены знака (см. главу 5), не являющийся частью синтаксиса числовых ли
тералов.
3.1.1. Целые литералы
В JavaScript целые десятичные числа записываются как последовательность
цифр. Например:
0
3
10000000
Числовой формат JavaScript позволяет точно представлять все целые числа в диа
пазоне от –9007199254740992 (–253) до 9007199254740992 (253) включительно.
Для целых значений вне этого диапазона может теряться точность в младших раз
рядах. Следует отметить, что некоторые целые операции в JavaScript (в особенно
сти битовые операторы, описанные в главе 5) выполняются с 32разрядными це
лыми, принимающими значения от –2147483648 (–231) до 2147483647 (231–1).
3.1.2. Шестнадцатеричные и восьмеричные литералы
Помимо десятичных целых литералов JavaScript распознает шестнадцатерич
ные значения (по основанию 16). Шестнадцатеричные литералы начинаются
с последовательности символов «0x» или «0X», за которой следует строка шест
1 Этот формат должен быть знаком Javaпрограммистам как формат типа double.
Это также формат double почти во всех современных реализациях C и C++.
3.1. Числа 41
надцатеричных цифр. Шестнадцатеричная цифра – это одна из цифр от 0 до 9
или букв от a (или A) до f (или F), представляющих значения от 10 до 15. Ниже
приводятся примеры шестнадцатеричных целых литералов:
0xff // 15*16 + 15 = 255 (по основанию 10)
0xCAFE911
Хотя стандарт ECMAScript не поддерживает представление целых литералов
в восьмеричном формате (по основанию 8), некоторые реализации JavaScript до
пускают подобную возможность. Восьмеричный литерал начинается с цифры 0,
за ней следуют цифры, каждая из которых может быть от 0 до 7. Например:
0377 // 3*64 + 7*8 + 7 = 255 (по основанию 10)
Поскольку некоторые реализации поддерживают восьмеричные литералы, а не
которые нет, никогда не следует писать целый литерал с ведущим нулем, ибо
нельзя сказать наверняка, как он будет интерпретирован данной реализацией –
как восьмеричное число или как десятичное.
3.1.3. Литералы вещественных чисел
Литералы вещественных чисел должны иметь десятичную точку; в них использу
ется традиционный синтаксис вещественных чисел. Вещественное значение
представлено как целая часть числа, за которой следуют десятичная точка и дроб
ная часть числа.
Литералы вещественных чисел могут также представляться в экспоненциаль
ной нотации: вещественное число, за которым следует буква e (или E), а затем
необязательный знак плюс или минус и целая экспонента. Эта нотация обозна
чает вещественное число, умноженное на 10 в степени, определяемой значением
экспоненты.
Более лаконичное определение синтаксиса таково:
[цифры][.цифры][(E|e)[(+|)]цифры]
Например:
3.14
2345.789
.333333333333333333
6.02e23 // 6.02 X 1023
1.4738223E32 // 1.4738223 X 1032
Обратите внимание: вещественных чисел существует бесконечно много, но фор
мат представления вещественных чисел в JavaScript позволяет точно выразить
лишь ограниченное их количество (точнее 18437736874454810627). Это значит,
что при работе с вещественными числами в JavaScript представление числа час
то будет округлением реального числа. Точность округления, как правило, до
статочна и на практике редко приводит к ошибкам.
3.1.4. Работа с числами
Для работы с числами в JavaScriptпрограммах используются поддерживаемые
языком арифметические операторы, к которым относятся операторы сложения
42 Глава 3. Типы данных и значения
(+), вычитания (), умножения (*) и деления (/). Подробное описание этих и дру
гих арифметических операторов имеется в главе 5.
Помимо перечисленных основных арифметических операторов JavaScript под
держивает выполнение более сложных математических операций с помощью
большого количества математических функций, относящихся к базовой части
языка. Для удобства эти функции хранятся в виде свойств одного объекта Math,
и для доступа к ним всегда используется литеральное имя Math. Например, синус
числового значения переменной x можно вычислить следующим образом:
sine_of_x = Math.sin(x);
А так вычисляется квадратный корень числового выражения:
hypot = Math.sqrt(x*x + y*y);
Подробные сведения обо всех математических функциях, поддерживаемых Java
Script, приведены в описании объекта Math и соответствующих листингах треть
ей части книги.
3.1.5. Преобразования чисел
В языке JavaScript имеется возможность представлять числа в виде строк и пре
образовывать строки в числа. Порядок этих преобразований описывается в раз
деле 3.2.
3.1.6. Специальные числовые значения
В JavaScript определено несколько специальных числовых значений. Когда ве
щественное число превышает самое большое представимое конечное значение,
результату присваивается специальное значение бесконечности, которое в Java
Script обозначается как Infinity. А когда отрицательное число становится мень
ше наименьшего представимого отрицательного числа, результатом является
отрицательная бесконечность, обозначаемая как Infinity.
Еще одно специальное числовое значение возвращается JavaScript, когда мате
матическая операция (например, деление нуля на ноль) приводит к неопреде
ленному результату или ошибке. В этом случае результатом является специаль
ное значение «нечисло», обозначаемое как NaN. «Нечисло» (NotaNumber) ведет
себя необычно: оно не равно ни одному другому числу, в том числе и самому себе!
По данной причине для проверки на это значение имеется специальная функция
isNaN(). Похожая функция, isFinite(), позволяет проверить число на неравенст
во NaN или положительной/отрицательной бесконечности.
В табл. 3.1 приведено несколько констант, определенных в JavaScript для обо
значения специальных числовых значений.
Таблица 3.1. Специальные числовые константы
Константа Значение
Infinity Специальное значение, обозначающее бесконечность
NaN Специальное значение – «нечисло»
Number.MAX_VALUE Максимальное представимое значение
3.2. Строки 43
Константа Значение
Number.MIN_VALUE Наименьшее (ближайшее к нулю) представимое значение
Number.NaN Специальное значение – «нечисло»
Number.POSITIVE_INFINITY Специальное значение, обозначающее плюс бесконечность
Number.NEGATIVE_INFINITY Специальное значение, обозначающее минус бесконечность
Константы Infinity и NaN, определенные в ECMAScript v1, не были реализованы
вплоть до JavaScript 1.3. Однако различные константы Number реализованы на
чиная с JavaScript 1.1.
3.2. Строки
Строка представляет собой последовательность букв, цифр, знаков пунктуации
и прочих Unicodeсимволов и является типом данных JavaScript для представ
ления текста. Как вы скоро увидите, строковые литералы можно использовать
в своих программах, заключая их в согласованные пары одинарных или двой
ных кавычек. Обратите внимание: в JavaScript нет символьного типа данных,
такого как char в C, C++ и Java. Одиночный символ представлен строкой единич
ной длины.
3.2.1. Строковые литералы
Строковый литерал – это последовательность из нуля или более Unicodeсимво
лов, заключенная в одинарные или двойные кавычки (' или "). Сами символы
двойных кавычек могут содержаться в строках, ограниченных символами оди
нарных кавычек, а символы одинарных кавычек – в строках, ограниченных
символами двойных кавычек. Строковые литералы должны записываться в од
ной строке программы и не могут разбиваться на две строки. Чтобы включить
в строковый литерал символ перевода строки, следует использовать последова
тельность символов \n, описание которой приведено в следующем разделе. При
меры строковых литералов:
"" // Это пустая строка: в ней ноль символов
'testing'
"3.14"
'name="myform"'
"Вы предпочитаете книги издательства O'Reilly, не правда ли?"
"В этом строковом литерале\nдве строки"
"π это отношение длины окружности круга к его диаметру"
Как иллюстрирует последний пример строки, стандарт ECMAScript v1 допуска
ет Unicodeсимволы в строковых литералах. Однако в реализациях, более ран
них, чем JavaScript 1.3, в строках обычно поддерживаются только символы из
набора ASCII или Latin1. Как мы увидим в следующем разделе, Unicodeсимво
лы также можно включать в строковые литералы с помощью специальных
управляющих последовательностей. Это может потребоваться, если в тексто
вом редакторе отсутствует полноценная поддержка Unicode.
Обратите внимание: ограничивая строку одинарными кавычками, необходимо
проявлять осторожность в обращении с апострофами, употребляемыми в анг
44 Глава 3. Типы данных и значения
лийском языке для обозначения притяжательного падежа и в сокращениях,
как, например, в словах «can't» и «O'Reilly's». Поскольку апостроф и одиночная
кавычка – это одно и то же, необходимо при помощи символа обратного слэша
(\) экранировать апострофы, расположенные внутри одиночных кавычек (под
робнее об этом – в следующем разделе).
Программы на клиентском JavaScript часто содержат строки HTMLкода, а HTML
код, в свою очередь, часто содержит строки JavaScriptкода. Как и в JavaScript,
в HTML для ограничения строк применяются либо одинарные, либо двойные ка
вычки. Поэтому при объединении JavaScript и HTMLкода есть смысл придер
живаться одного «стиля» кавычек для JavaScript, а другого – для HTML. В сле
дующем примере строка «Спасибо» в JavaScriptвыражении заключена в оди
нарные кавычки, а само выражение, в свою очередь, заключено в двойные ка
вычки как значение HTMLатрибута обработчика событий:
<a href="" onclick="alert('Спасибо')">Щелкни на мне</a>
3.2.2. Управляющие последовательности
в строковых литералах
Символ обратного слэша (\) имеет специальное назначение в JavaScriptстроках.
Вместе с символами, следующими за ним, он обозначает символ, не представи
мый внутри строки другими способами. Например, \n – это управляющая после'
довательность (escape sequence), обозначающая символ перевода строки.1
Другой пример, упомянутый в предыдущем разделе, – это последовательность
\', обозначающая символ одинарной кавычки. Эта управляющая последова
тельность необходима для включения символа одинарной кавычки в строковый
литерал, заключенный в одинарные кавычки. Теперь становится понятно, поче
му мы называем эти последовательности управляющими – здесь символ обрат
ного слэша позволяет управлять интерпретацией символа одинарной кавычки.
Вместо того чтобы отмечать ею конец строки, мы используем ее как апостроф:
'You\'re right, it can\'t be a quote'
В табл. 3.2 перечислены управляющие последовательности и обозначаемые ими
символы. Две управляющие последовательности являются обобщенными; они
могут применяться для представления любого символа путем указания кода
символа из набора Latin1 или Unicode в виде шестнадцатеричного числа. Напри
мер, последовательность \xA9 обозначает символ копирайта, который в кодиров
ке Latin1 имеет шестнадцатеричный код A9. Аналогично управляющая последо
вательность, начинающаяся с символов \u, обозначает произвольный Unicode
символ, заданный четырьмя шестнадцатеричными цифрами. Например, \u03c0
обозначает символ π. Следует отметить, что управляющие последовательности
для обозначения Unicodeсимволов требуются по стандарту ECMAScript v1, но
обычно не поддерживаются в реализациях, вышедших ранее чем JavaScript 1.3.
Некоторые реализации JavaScript также допускают задание символа Latin1
тремя восьмеричными символами, указанными после символа обратного слэша,
1 Тем, кто программирует на C, C++ и Java, эта и другие управляющие последова
тельности JavaScript уже знакомы.
3.2. Строки 45
но такие управляющие последовательности не поддерживаются в стандарте
ECMAScript v3 и не должны использоваться.
Таблица 3.2. Управляющие последовательности JavaScript
Константа Значение
\0 Символ NUL (\u0000)
\b «Забой» (\u0008)
\t Горизонтальная табуляция (\u0009)
\n Перевод строки (\u000A)
\v Вертикальная табуляция (\u000B)
\f Перевод страницы (\u000C)
\r Возврат каретки (\u000D)
\" Двойная кавычка (\u0022)
\' Одинарная кавычка (\u0027)
\\ Обратный слэш (\u005C)
\xXX Символ Latin1, заданный двумя шестнадцатеричными цифрами XX
\uxXXXX Unicodeсимвол, заданный четырьмя шестнадцатеричными цифрами XXXX
\XXX Символ из набора Latin1, заданный тремя восьмеричными цифрами
XXX, с кодом в диапазоне от 1 до 377. Не поддерживается ECMAScript v3;
такой способ записи не должен использоваться
И наконец, следует заметить, что символ обратного слэша не может предшество
вать символу перевода строки для продолжения строки (или другой JavaScript
лексемы) на следующей строке или включения буквального перевода строки
в строковый литерал. Если символ «\» предшествует любому символу, отлично
му от приведенных в табл. 3.2, обратный слэш просто игнорируется (хотя буду
щие версии могут, конечно, определять новые управляющие последовательно
сти). Например, \# – это то же самое, что и #.
3.2.3. Работа со строками
Одной из встроенных возможностей JavaScript является способность конкатени
ровать строки. Если оператор + применяется к числам, они складываются, а ес
ли к строкам, они объединяются, при этом вторая строка добавляется в конец
первой. Например:
msg = "Hello, " + "world"; // Получается строка "Hello, world"
greeting = "Добро пожаловать на мою домашнюю страницу," + " " + name;
Для определения длины строки – количества содержащихся в ней символов –
используется свойство length. Так, если переменная s содержит строку, то длину
последней можно получить следующим образом:
s.length
Для работы со строками существует несколько методов. Так можно получить по
следний символ в строке s:
46 Глава 3. Типы данных и значения
last_char = s.charAt(s.length – 1)
Чтобы извлечь второй, третий и четвертый символы из строки s, применяется
инструкция:
sub = s.substring(1,4);
Определить позицию первого символа «a» в строке s можно следующим образом:
i = s.indexOf('a');
Есть и еще ряд методов, которые можно использовать при работе со строками.
Полностью эти методы документированы в описании объекта String и в листин
гах третьей части книги.
Из предыдущих примеров можно понять, что JavaScriptстроки (и, как мы уви
дим позднее, массивы JavaScript) индексируются, начиная с 0. Другими слова
ми, порядковый номер первого символа строки равен нулю. Программистам, ра
ботавшим с C, С++ и Java, должно быть удобно это соглашение, однако програм
мистам, привыкшим к языкам, в которых нумерация строк и массивов начина
ется с единицы, придется какоето время привыкать к этому.
В некоторых реализациях JavaScript отдельные символы могут извлекаться из
строк (но не записываться в строки) при обращении к строкам как к массивам,
в результате приведенный ранее вызов метода charAt() может быть записан сле
дующим образом:
last_char = s[s.length – 1];
Однако этот синтаксис не стандартизован в ECMAScript v3, не является перено
симым и его следует избегать.
Когда мы будем обсуждать объектный тип данных, вы увидите, что свойства
и методы объектов используются так же, как в предыдущих примерах свойства
и методы строк. Это не значит, что строки – это тип объектов. На самом деле
строки – это отдельный тип данных JavaScript. Для доступа к их свойствам и ме
тодам используется объектный синтаксис, но сами они объектами не являются.
Почему это так, мы узнаем в конце данной главы.
3.2.4. Преобразование чисел в строки
Преобразование чисел в строки производится автоматически, по мере необходи
мости. Например, если число используется в операции конкатенации строк, оно
будет преобразовано в строку:
var n = 100;
var s = n + " бутылок пива на стене.";
Такая способность JavaScript к автоматическому преобразованию числа в стро
ку реализует идиому программирования, которую часто можно встретить на
практике: чтобы преобразовать число в строку, достаточно просто сложить его
с пустой строкой:
var n_as_string = n + "";
Для явного преобразования числа в строку используется функция String():
var string_value = String(number);
3.2. Строки 47
Еще один способ преобразования числа в строку заключается в вызове метода
toString():
string_value = number.toString( );
Метод toString() объекта Number (примитивы чисел автоматически преобразуют
ся в объекты типа Number, благодаря чему можно воспользоваться этим методом)
может принимать один необязательный аргумент, который определяет базу,
или основание, системы счисления для преобразования. Если основание систе
мы счисления не указывается, по умолчанию она предполагается равной 10. Од
нако существует возможность выполнять преобразования и в других системах
счисления (от 2 до 36)1, например:
var n = 17;
binary_string = n.toString(2); // Вернет "10001"
octal_string = "0" + n.toString(8); // Вернет "021"
hex_string = "0x" + n.toString(16); // Вернет "0x11"
Одним из недостатков реализаций JavaScript, существовавших до версии Java
Script 1.5, было отсутствие встроенной возможности определить число десятич
ных знаков, которые должны получиться в результате, или задать результат
в экспоненциальном представлении. В связи с этим могут возникать определен
ные сложности с представлением чисел в традиционных форматах, таких как
денежные суммы.
В стандарте ECMAScript v3 и JavaScript 1.5 эта проблема была решена за счет до
бавления нового метода преобразования числа в строку в классе Number. Метод to
Fixed() преобразует число в строку и отображает определенное число знаков по
сле десятичной точки. Однако данный метод не выполняет преобразование числа
в экспоненциальную форму. Эту задачу решает метод toExponential(), который
преобразует число в экспоненциальное представление с одним знаком перед точ
кой и с заданным числом десятичных знаков после точки. Для отображения опре
деленного числа значащих разрядов числа используется метод toPrecision(). Он
возвращает строку с экспоненциальным представлением числа, если заданного
количества значащих разрядов недостаточно для точного отображения целой
части числа. Обратите внимание: все три метода корректно выполняют округле
ние результата. Ниже приводятся примеры обращения к этим методам:
var n = 123456.789;
n.toFixed(0); // "123457"
n.toFixed(2); // "123456.79"
n.toExponential(1); // "1.2e+5"
n.toExponential(3); // "1.235e+5"
n.toPrecision(4); // "1.235e+5"
n.toPrecision(7); // "123456.8"
1 Спецификациями ECMAScript предусматривается возможность определения ос
нования системы счисления в методе toString(), но при этом допускается возвра
щать из метода строку в представлении, зависящем от конкретной реализации,
если основание не равно 10. Таким образом, согласно стандарту метод может про
сто игнорировать значение аргумента и всегда возвращать число в десятичном
представлении. Однако на практике большинство реализаций возвращают кор
ректный результат с учетом заданного основания системы счисления.
48 Глава 3. Типы данных и значения
3.2.5. Преобразование строк в числа
Когда строка используется в числовом контексте, она автоматически преобразу
ется в число. Например, следующее выражение является вполне допустимым:
var product = "21" * "2"; // в результате получится число 42.
Это обстоятельство можно взять на вооружение при необходимости преобразо
вать строку в число; для этого достаточно просто вычесть из строки значение 0:
var number = string_value 0;
(Будьте внимательны: операция сложения в данной ситуации будет интерпрети
рована как операция конкатенации строк.)
Менее изощренный и более прямолинейный способ преобразования строки в чис
ло заключается в обращении к конструктору Number() как к обычной функции:
var number = Number(string_value);
Недостаток такого способа преобразования строки в число заключается в его
чрезмерной строгости. Этот способ может использоваться только для преобразо
вания десятичных чисел, и хотя он допускает наличие ведущих и оконечных
символов пробела, появление других нецифровых символов после числа в строке
недопустимо.
Более гибкий способ преобразования обеспечивается функциями parseInt() и par
seFloat(). Эти функции преобразуют и возвращают произвольные числа, стоя
щие в начале строки, игнорируя любые нецифровые символы, расположенные
вслед за числом. Функция parseInt() выполняет только целочисленное преобра
зование, тогда как parseFloat() может преобразовывать как целые, так и вещест
венные числа. Если строка начинается с символов «0x» или «0X», функция par
seInt() интерпретирует строку как шестнадцатеричное число.1 Например:
parseInt("3 слепых мышки"); // Вернет 3
parseFloat("3.14 метров"); // Вернет 3.14
parseInt("12.34"); // Вернет 12
parseInt("0xFF"); // Вернет 255
В качестве второго аргумента функция parseInt() может принимать основание
системы счисления. Корректными значениями являются числа в диапазоне от 2
до 36, например:
parseInt("11", 2); // Вернет 3 (1*2 + 1)
parseInt("ff", 16); // Вернет 255 (15*16 + 15)
parseInt("zz", 36); // Вернет 1295 (35*36 + 35)
parseInt("077", 8); // Вернет 63 (7*8 + 7)
parseInt("077", 10); // Вернет 77 (7*10 + 7)
1 Стандарт ECMAScript утверждает, что если строка начинается с символа «0» (но
не «0x» или «0X»), функция parseInt() может интерпретировать строку как число
и в восьмеричном, и в десятичном представлении. Поскольку поведение функции
четко не определено, следует избегать использования функции parseInt() для ин
терпретации строк, начинающихся с «0», или явно указывать основание системы
счисления.
3.3. Логические значения 49
Если методы parseInt() и parseFloat() оказываются не в состоянии выполнить
преобразование, они возвращают значение NaN:
parseInt("eleven"); // Вернет NaN
parseFloat("$72.47"); // Вернет NaN
3.3. Логические значения
Числовые и строковые типы данных имеют большое или бесконечное количест
во возможных значений. Логический тип данных, напротив, имеет только два
допустимых логических значения, представленных литералами true и false. Ло
гическое значение говорит об истинности чегото, т. е. о том, является это чтото
истинным или нет.
Логические значения обычно представляют собой результат сравнений, выпол
няемых в JavaScriptпрограммах. Например:
a == 4
Это выражение проверяет, равно ли значение переменной a числу 4. Если да, ре
зультатом этого сравнения будет логическое значение true. Если переменная a не
равна 4, результатом сравнения будет false.
Логические значения обычно используются в управляющих конструкциях
JavaScript. Например, инструкция if/else в JavaScript выполняет одно дейст
вие, если логическое значение равно true, и другое действие, если false. Обычно
сравнение, создающее логическое значение, непосредственно объединяется с ин
струкцией, в которой оно используется. Результат выглядит так:
if (a == 4)
b = b + 1;
else
a = a + 1;
Здесь выполняется проверка, равна ли переменная a числу 4. Если да, к значе
нию переменной b добавляется 1; в противном случае число 1 добавляется к зна
чению переменной a.
Вместо того чтобы интерпретировать два возможных логических значения как
true и false, иногда удобно рассматривать их как «включено» (true) и «выключе
но» (false) или «да» (true) и «нет» (false).
3.3.1. Преобразование логических значений
Логические значения легко преобразуются в значения других типов, причем не
редко такое преобразование выполняется автоматически.1 Если логическое значе
1 Тем, кто программировал на C, следует обратить внимание, что в JavaScript име
ется отдельный логический тип данных, в отличие от языка C, в котором для ими
тации логических значений служат целые числа. Javaпрограммистам следует
иметь в виду, что хотя в JavaScript есть логический тип, он не настолько «чист»,
как тип данных boolean в Java – в JavaScript логические значения легко преоб
разуются в другие типы данных и обратно, поэтому на практике в том, что касает
ся работы с логическими значениями, JavaScript больше напоминает C, чем Java.
50 Глава 3. Типы данных и значения
ние используется в числовом контексте, тогда значение true преобразуется в чис
ло 1, а false – в 0. Если логическое значение используется в строковом контек
сте, тогда значение true преобразуется в строку "true", а false – в строку "false".
Когда в качестве логического значения используется число, оно преобразуется
в значение true, если оно не равно значениям 0 или NaN, которые преобразуются
в логическое значение false. Когда в качестве логического значения используется
строка, она преобразуется в значение true, если это не пустая строка, в против
ном случае в результате преобразования получается значение false. Специаль
ные значения null и undefined преобразуются в false, а любые функция, объект
или массив, значения которых отличны от null, преобразуются в true.
Если вы предпочитаете выполнять преобразование явно, можно воспользовать
ся функцией Boolean():
var x_as_boolean = Boolean(x);
Другой способ явного преобразования заключается в использовании двойного
оператора логического отрицания:
var x_as_boolean = !!x;
3.4. Функции
Функция – это фрагмент исполняемого кода, который определен в JavaScriptпро
грамме или заранее предопределен в реализации JavaScript. Хотя функция опре
деляется только один раз, JavaScriptпрограмма может исполнять или вызывать
ее сколько угодно. Функции могут передаваться аргументы, или параметры, оп
ределяющие значение или значения, для которых она должна выполнять вычис
ления; также функция может возвращать значение, представляющее собой ре
зультат этих вычислений. Реализации JavaScript предоставляют много предопре
деленных функций, таких как функция Math.sin(), возвращающая синус угла.
JavaScriptпрограммы могут также определять собственные функции, содержа
щие, например, такой код:
function square(x) // Функция называется square. Она принимает один аргумент, x.
{ // Здесь начинается тело функции.
return x*x; // Функция возводит свой аргумент в квадрат и возвращает
// полученное значение.
} // Здесь функция заканчивается.
Определив функцию, можно вызывать ее, указав имя, за которым следует за
ключенный в скобки список необязательных аргументов, разделенных запяты
ми. Следующие строки представляют собой вызовы функций:
y = Math.sin(x);
y = square(x);
d = compute_distance(x1, y1, z1, x2, y2, z2);
move();
Важной чертой JavaScript является то, что функции представляют собой значе
ния, которыми можно манипулировать в JavaScriptкоде. Во многих языках,
в том числе в Java, функции – это всего лишь синтаксические элементы языка, но
не тип данных: их можно определять и вызывать. То обстоятельство, что функции
3.5. Объекты 51
в JavaScript представляют собой настоящие значения, придает языку большую
гибкость. Это означает, что функции могут храниться в переменных, массивах
и объектах, а также передаваться в качестве аргументов другим функциям. Очень
часто это бывает очень удобно. Более подробно об определении и вызове функций,
а также об использовании их в качестве значений рассказывается в главе 8.
Поскольку функции представляют собой значения, такие же, как числа и стро
ки, они могут присваиваться свойствам объектов. Когда функция присваивает
ся свойству объекта (объектный тип данных и свойства объекта описаны в разде
ле 3.5), она часто называется методом этого объекта. Методы – важная часть
объектноориентированного программирования. Им посвящена глава 7.
3.4.1. Функциональные литералы
В предыдущем разделе мы видели определение функции square(). С помощью
этого синтаксиса обычно описывается большинство функций в JavaScriptпро
граммах. Однако стандарт ECMAScript v3 предоставляет синтаксис (реализован
ный в JavaScript 1.2 и более поздних версиях) для определения функциональных
литералов. Функциональный литерал задается с помощью ключевого слова func
tion, за которым следуют необязательное имя функции, список аргументов
функции, заключенный в круглые скобки, и тело функции в фигурных скобках.
Другими словами, функциональный литерал выглядит так же, как определение
функции, правда, у него может не быть имени. Самое большое различие состоит
в том, что функциональные литералы могут входить в другие JavaScriptвыра
жения. То есть функцию square() не обязательно задавать в виде определения:
function square(x) { return x*x; }
Ее можно задать с помощью функционального литерала:
var square = function(x) { return x*x; }
Функции, определенные таким образом, иногда называют лямбдафункциями.
Это дань уважения языку программирования LISP, который одним из первых
допускал вставку неименованных функций в виде литералов внутрь программы.
Хотя в данный момент польза от функциональных литералов может быть неоче
видной, позднее, в сложных сценариях мы увидим, что они бывают довольно
удобными и полезными.
Имеется еще один способ определения функции: можно передать список аргу
ментов и тело функции в виде строк в конструктор Function(). Например:
var square = new Function("x", "return x*x;");
Такое определение функций используется редко. Обычно неудобно задавать тело
функции в виде строки, и во многих реализациях JavaScript функции, опреде
ленные подобным образом, менее эффективны, чем функции, определенные лю
бым из двух других способов.
3.5. Объекты
Объект – это коллекция именованных значений, которые обычно называют свой'
ствами (properties) объекта. (Иногда они называются полями объекта, но упо
52 Глава 3. Типы данных и значения
требление этого термина может сбить с толку.) Чтобы сослаться на свойство объ
екта, надо указать имя объекта, затем точку и имя свойства. Например, если объ
ект под названием image имеет свойства width и height, мы можем сослаться на эти
свойства следующим образом:
image.width
image.height
Свойства объектов во многом похожи на JavaScriptпеременные – они могут со
держать любой тип данных, включая массивы, функции и другие объекты. По
этому можно встретить вот такой JavaScriptкод:
document.myform.button
Этот фрагмент ссылается на свойство button объекта, который, в свой очередь,
хранится в свойстве myform объекта с именем document.
Как упоминалось раньше, функция, хранящаяся в свойстве объекта, часто на
зывается методом, а имя свойства становится именем метода. При вызове метода
объекта сначала используется оператор «точка» для указания функции, а затем
() для вызова этой функции. Например, метод write() объекта с именем document
можно вызвать так:
document.write("это проверка");
Объекты в JavaScript могут выступать в качестве ассоциативных массивов, т. е.
могут ассоциировать произвольные значения с произвольными строками. При
такой работе с объектом обычно требуется другой синтаксис для доступа к его
свойствам: строка, содержащая имя требуемого свойства, заключается в квад
ратные скобки. Тогда к свойствам объекта image, упомянутого ранее, можно об
ратиться посредством следующего кода:
image["width"]
image["height"]
Ассоциативные массивы – это мощный тип данных; они полезны при реализа
ции ряда технологий программирования. Об объектах, их традиционном приме
нении и применении в качестве ассоциативных массивов рассказано в главе 7.
3.5.1. Создание объектов
Как мы увидим в главе 7, объекты создаются путем вызова специальных функ
цийконструкторов. Все следующие строки создают новые объекты:
var o = new Object();
var now = new Date();
var pattern = new RegExp("\\sjava\\s", "i");
Создав собственный объект, можно его как угодно использовать и устанавливать
его свойства:
var point = new Object();
point.x = 2.3;
point.y = 1.2;
3.6. Массивы 53
3.5.2. Объектные литералы
В JavaScript определяется синтаксис объектных литералов, позволяющий соз
давать объекты и указывать их свойства. Объектный литерал (также называе
мый инициализатором объекта) представляет собой список разделенных запя
тыми пар «свойство/значение», заключенный в фигурные скобки. Внутри пар
роль разделителя играет двоеточие. Таким образом, объект point из предыдуще
го примера также может быть создан и инициализирован следующей строкой:
var point = { x:2.3, y:1.2 };
Объектные литералы могут быть вложенными. Например:
var rectangle = { upperLeft: { x: 2, y: 2 },
lowerRight: { x: 4, y: 4 }
};
Наконец, значениями свойств в объектных литералах не обязательно должны
быть константы – это могут быть произвольные JavaScriptвыражения. Кроме
того, в качестве имен свойств в объектных литералах допускается использовать
строковые значения:
var square = { "upperLeft": { x:point.x, y:point.y },
'lowerRight': { x:(point.x + side), y:(point.y+side) }};
3.5.3. Преобразование объектов
Когда непустой объект используется в логическом контексте, результатом преоб
разования является значение true. Когда объект используется в строковом кон
тексте, преобразование выполняется методом toString() объекта и в дальнейших
вычислениях участвует строка, возвращаемая этим методом. Когда объект ис
пользуется в числовом контексте, сначала вызывается метод объекта valueOf().
Если этот метод возвращает числовое значение примитивного типа, в дальней
ших вычислениях участвует это значение. Однако в большинстве случаев метод
valueOf() возвращает сам объект. В такой ситуации объект сначала преобразует
ся в строку вызовом метода toString(), а затем выполняется попытка преобразо
вать строку в число.
Проблема преобразования объектов в значения примитивных типов имеет свои
тонкости, и мы еще вернемся к ней в конце главы.
3.6. Массивы
Массив (array), как и объект, представляет собой коллекцию значений. Если ка
ждое значение, содержащееся в объекте, имеет имя, то в массиве каждое значе
ние имеет номер, или индекс. В JavaScript можно извлекать значения из масси
ва, указав после имени массива индекс, заключенный в квадратные скобки. На
пример, если a – это имя массива, а i – неотрицательное целое число, то a[i] яв
ляется элементом массива. Индексы массива начинаются с нуля, т. е. a[2]
ссылается на третий элемент массива a.
Массивы могут содержать любой тип данных JavaScript, в том числе ссылки на
другие массивы или на объекты или функции. Например:
54 Глава 3. Типы данных и значения
document.images[1].width
Этот код ссылается на свойство width объекта, хранящегося во втором элементе
массива, в свою очередь хранящегося в свойстве images объекта document.
Обратите внимание: описываемые здесь массивы отличаются от ассоциативных
массивов (см. раздел 3.5). Здесь обсуждаются «настоящие» массивы, которые ин
дексируются неотрицательными целыми числами. Ассоциативные массивы ин
дексируются строками. Следует также отметить, что в JavaScript не поддержи
ваются многомерные массивы (хотя допускается существование массивов из мас
сивов). И наконец, поскольку JavaScript является нетипизированным языком,
элементы массива не обязательно должны иметь одинаковый тип, как в типизи
рованных языках, подобных Java. Подробнее о массивах мы поговорим в главе 7.
3.6.1. Создание массивов
Массив может быть создан с помощью функцииконструктора Array(). Созданному
массиву допустимо присваивать любое количество индексированных элементов:
var a = new Array();
a[0] = 1.2;
a[1] = "JavaScript";
a[2] = true;
a[3] = { x:1, y:3 };
Массивы могут также быть инициализированы путем передачи элементов мас
сива конструктору Array(). Таким образом, предыдущий пример создания и ини
циализации массива можно записать так:
var a = new Array(1.2, "JavaScript", true, { x:1, y:3 });
Если передать конструктору Array() только одно число, оно определит длину
массива. Таким образом, следующее выражение создает новый массив с 10 неоп
ределенными элементами:
var a = new Array(10);
3.6.2. Литералы массивов
В JavaScript определяется синтаксис литералов для создания и инициализации
массивов. Литерал, или инициализатор, массива – это список разделенных за
пятыми значений, заключенных в квадратные скобки. Значения в скобках по
следовательно присваиваются элементам массива с индексами, начиная с нуля.
Например, программный код, создающий и инициализирующий массив из пре
дыдущего раздела, можно записать следующим образом:
var a = [1.2, "JavaScript", true, { x:1, y:3 }];
Как и объектные литералы, литералы массивов могут быть вложенными:
var matrix = [[1,2,3], [4,5,6], [7,8,9]];
Как и в объектных литералах, элементы в литерале массива могут быть произ
вольными выражениями и не обязательно должны быть константами:
var base = 1024;
var table = [base, base+1, base+2, base+3];
3.7. Значение null 55
Для того чтобы включить в литерал массива неопределенный элемент, достаточ
но пропустить значение между запятыми. Следующий массив содержит пять
элементов, в том числе три неопределенных:
var sparseArray = [1,,,,5];
3.7. Значение null
Ключевое слово null в JavaScript имеет специальный смысл. Обычно считается,
что у значения null объектный тип и оно говорит об отсутствии объекта. Значе
ние null уникально и отличается от любых других. Если переменная равна null,
следовательно, в ней не содержится допустимого объекта, массива, числа, стро
ки или логического значения.1
Когда значение null используется в логическом контексте, оно преобразуется
в значение false, в числовом контексте оно преобразуется в значение 0, а в стро
ковом контексте — в строку "null".
3.8. Значение undefined
Еще одно специальное значение, иногда используемое в JavaScript, – undefined.
Оно возвращается при обращении либо к переменной, которая была объявлена,
но которой никогда не присваивалось значение, либо к свойству объекта, кото
рое не существует. Заметьте, что специальное значение undefined – это не то же
самое, что null.
Хотя значения null и undefined не эквивалентны друг другу, оператор эквива
лентности == считает их равными. Рассмотрим следующее выражение:
my.prop == null
Это сравнение истинно, либо если свойство my.prop не существует, либо если оно
существует, но содержит значение null. Поскольку значение null и undefined обо
значают отсутствие значения, это равенство часто оказывается тем, что нам
нужно. Однако когда действительно требуется отличить значение null от значе
ния undefined, нужен оператор идентичности === или оператор typeof (подробнее
об этом в главе 5).
В отличие от null, значение undefined не является зарезервированным словом
JavaScript. Стандарт ECMAScript v3 указывает, что всегда существует глобаль
ная переменная с именем undefined, начальным значением которой является зна
чение undefined. Следовательно, в реализации, соответствующей стандарту, un
defined можно рассматривать как ключевое слово, если только этой глобальной
переменной не присвоено другое значение.
Если нельзя с уверенностью сказать, есть ли в данной реализации переменная
undefined, можно просто объявить собственную переменную:
var undefined;
1 Программистам на C и C++ следует обратить внимание, что null в JavaScript – это
не то же самое, что 0, как в других языках. В определенных обстоятельствах null
преобразуется в 0, однако эти два значения не эквивалентны.
56 Глава 3. Типы данных и значения
Объявив, но не инициализировав переменную, вы гарантируете, что переменная
имеет значение undefined. Оператор void (см. главу 5) предоставляет еще один
способ получения значения undefined.
Когда значение undefined используется в логическом контексте, оно преобразует
ся в значение false. В числовом контексте – в значение NaN, а в строковом –
в строку "undefined".
3.9. Объект Date
В предыдущих разделах мы описали все фундаментальные типы данных, под
держиваемые JavaScript. Значения даты и времени не относятся к этим фунда
ментальным типам, однако в JavaScript имеется класс объектов, представляю
щих дату и время, и этот класс можно использовать для работы с этим типом
данных. Объект Date в JavaScript создается с помощью оператора new и конструк
тора Date() (оператор new будет введен в главе 5, а в главе 7 вы больше узнаете
о создании объектов):
var now = new Date(); // Создание объекта, в котором хранятся текущие дата и время.
// Создание объекта, в котором хранится дата Рождества.
// Обратите внимание: номера месяцев начинаются с нуля, поэтому декабрь имеет номер 11!
var xmas = new Date(2000, 11, 25);
Методы объекта Date позволяют получать и устанавливать различные значения
даты и времени и преобразовывать дату в строку с использованием либо локаль
ного времени, либо времени по Гринвичу (GMT). Например:
xmas.setFullYear(xmas.getFullYear() + 1); // Заменяем дату датой следующего Рождества.
var weekday = xmas.getDay(); // В 2007 году Рождество выпадает на вторник.
document.write("Сегодня: " + now.toLocaleString()); // Текущие дата и время.
В объекте Date также определяются функции (не методы, поскольку они не вы
зываются через объект Date) для преобразования даты, заданной в строковой или
числовой форме, во внутреннее представление в миллисекундах, полезное для
некоторых видов операций с датами.
Полное описание объекта Date и его методов вы найдете в третьей части книги.
3.10. Регулярные выражения
Регулярные выражения предоставляют богатый и мощный синтаксис описания
текстовых шаблонов. Они применяются для поиска соответствия заданному
шаблону и реализации операций поиска и замены. В JavaScript для формирова
ния регулярных выражений принят синтаксис языка Perl.
Регулярные выражения представляются в JavaScript объектом RegExp и могут
создаваться с помощью конструктора RegExp(). Как и объект Date, объект RegExp
не является одним из фундаментальных типов данных JavaScript; это лишь
стандартизованный тип объектов, предоставляемый всеми соответствующими
реализациями JavaScript.
Однако в отличие от объекта Date, объекты RegExp имеют синтаксис литералов
и могут задаваться непосредственно в коде JavaScriptпрограммы. Текст между
парой символов слэша образует литерал регулярного выражения. За вторым
3.11. Объекты Error 57
символом слэша в паре могут также следовать одна или несколько букв, изменя
ющих смысл шаблона. Например:
/^HTML/
/[19][09]*/
/\bjavascript\b/i
Грамматика регулярных выражений сложна и подробно описана в главе 11. Сей
час вам важно лишь знать, как литерал регулярного выражения выглядит в Java
Scriptкоде.
3.11. Объекты Error
В ECMAScript v3 определяется несколько классов для представления ошибок.
При возникновении ошибки времени выполнения интерпретатор JavaScript «ге
нерирует» объект одного из этих типов. (Вопросы генерации и перехвата ошибок
обсуждаются в главе 6 при описании инструкций throw и try.) Каждый объект
ошибки имеет свойство message, которое содержит зависящее от реализации со
общение об ошибке. Заранее определены следующие типы объектов ошибок –
Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError и URIError. Под
робнее об этих классах рассказывается в третьей части книги.
3.12. Преобразование типов
Поскольку все типы данных уже обсуждались в предыдущих разделах, здесь мы
рассмотрим, как значения каждого типа преобразуются в значения других ти
пов. Основное правило заключается в следующем: если значение одного типа ис
пользуется в контексте, требующем значение некоего другого типа, интерпрета
тор JavaScript автоматически пытается преобразовать это значение. Так, напри
мер, если в логическом контексте используется число, оно преобразуется в зна
чение логического типа. Если в контексте строки используется объект, он
преобразуется в строковое значение. Если в числовом контексте используется
строка, интерпретатор JavaScript пытается преобразовать ее в число. В табл. 3.3
приводится информация о том, как производится преобразование значений, ко
гда значение некоторого типа задействовано в определенном контексте.
Таблица 3.3. Автоматическое преобразование типов
Тип Контекст, в котором используется значение
значения Строковый Числовой Логический Объектный
Неопределен "undefined" NaN false Error
ное значение
null "null" 0 false Error
Непустая Как есть Числовое значение true Объект String
строка строки или NaN
Пустая строка Как есть 0 false Объект String
0 "0" Как есть false Объект Number
NaN "NaN" Как есть false Объект Number
58 Глава 3. Типы данных и значения
Таблица 3.3 (продолжение)
Тип Контекст, в котором используется значение
значения Строковый Числовой Логический Объектный
Infinity "Infinity" Как есть true Объект Number
Infinity "Infinity" Как есть true Объект Number
Любое другое Строковое пред Как есть true Объект Number
число ставление числа
true "true" 1 Как есть Объект Boolean
false "false" 0 Как есть Объект Boolean
Объект toString() valueOf(), true Как есть
toString() или NaN
3.13. Объектыобертки для элементарных
типов данных
Ранее в этой главе мы обсуждали строки, и тогда я обратил ваше внимание на
странную особенность этого типа данных: для работы со строками используется
объектная нотация.1 Например, типичная операция со строками может выгля
деть следующим образом:
var s = "These are the times that try people's souls.";
var last_word = s.substring(s.lastIndexOf(" ")+1, s.length);
Если бы вы не знали, то могли бы подумать, что s – это объект, и вы вызываете
методы и читаете значения свойств этого объекта.
Что же происходит? Являются ли строки объектами или это элементарный тип
данных? Оператор typeof (см. главу 5) убеждает нас, что строки имеют строко
вый тип данных, отличный от объектного типа. Почему же тогда для манипуля
ций со строками используется объектная нотация?
Дело в том, что для каждого из трех базовых типов данных определен соответст
вующий класс объектов. То есть помимо поддержки числовых, строковых и ло
гических типов данных JavaScript поддерживает классы Number, String и Boolean.
Эти классы представляют собой «обертки» для базовых типов данных. Обертка
(wrapper) содержит такое же значение базового типа, но кроме этого определяет
еще свойства и методы, которые могут использоваться для манипуляций с этим
значением.
JavaScript может гибко преобразовывать один тип в другой. Когда мы использу
ем строку в объектном контексте, т. е. когда пытаемся обратиться к свойству или
методу строки, JavaScript создает внутри себя объектобертку для строкового
значения. Этот объект String используется вместо базового строкового значения.
Для объекта определены свойства и методы, поэтому удается задействовать зна
1 Этот раздел содержит достаточно сложный материал, который при первом про
чтении можно пропустить.
3.13. Объекты*обертки для элементарных типов данных 59
чение базового типа в объектном контексте. То же самое, конечно, верно и для
других базовых типов и соответствующих им объектовоберток; мы просто не ра
ботаем с другими типами в объектном контексте так же часто, как со строками.
Следует отметить, что объект String, созданный при использовании строки в объ
ектном контексте, временный – он служит для того, чтобы обеспечить доступ
к свойству или методу, после чего необходимость в нем отпадает, и потому он ути
лизируется системой. Предположим, что s – это строка, и мы определяем длину
строки с помощью следующего предложения:
var len = s.length;
Здесь s остается строкой, и ее исходное значение не меняется. Создается новый
временный объект String, позволяющий обращаться к свойству length, а затем
этот объект удаляется, не меняя исходного значения переменной s. Если эта схе
ма кажется вам одновременно и элегантной, и неестественно сложной, вы пра
вы. Однако обычно реализации JavaScript выполняют внутреннее преобразова
ние очень эффективно, и вам не стоит об этом беспокоиться.
Чтобы явно использовать объект String в своей программе, надо создать постоян
ный объект, который не будет автоматически удаляться системой. Объекты String
создаются так же, как и другие объекты, – с помощью оператора new. Например:
var s = "hello world"; // Значение строкового типа
var S = new String("Hello World"); // Объект String
Что же можно делать с созданным объектом S типа String? Ничего такого, что
нельзя сделать с соответствующим значением базового типа. Если мы воспользу
емся оператором typeof, он сообщит нам, что S – это объект, а не строковое значе
ние, но кроме того, мы не увидим различий между базовым строковым значени
ем и объектом String.1 Как мы уже видели, строки автоматически преобразуются
в объекты String, когда это требуется. Оказывается, что обратное тоже верно. Ко
гда мы используем объект String там, где предполагается значение базового стро
кового типа, JavaScript автоматически преобразует объект String в строку. По
этому, если мы используем наш объект String с оператором +, для выполнения
операции конкатенации создается временное значение базового строкового типа:
msg = S + '!';
Имейте в виду, все, что говорилось в этом разделе о строковых значениях и объ
ектах String, также применимо к числовым и логическим значениям и соответ
ствующим объектам Number и Boolean. Более подробную информацию об этих
классах можно получить из соответствующих статей в третьей части книги.
Наконец, следует отметить, что любые строки, числа или логические значения
могут быть преобразованы в соответствующий объектобертку с помощью функ
ции Object():
var number_wrapper = Object(3);
1 Однако при этом метод eval() рассматривает строковые значения и объекты String
поразному, и если непреднамеренно передать ему объект String вместо значения
базового строкового типа, он поведет себя не так, как вы предполагаете.
60 Глава 3. Типы данных и значения
3.14. Преобразование объектов
в значения элементарных типов
Обычно объекты преобразуются в значения элементарных типов достаточно
прямолинейным способом, о котором рассказывалось в разделе 3.5.3. Однако на
вопросах преобразования объектов следует остановиться более подробно.1
Прежде всего следует заметить, что попытка преобразования непустых объектов
в логическое значение дает в результате значение true. Это справедливо для лю
бых объектов (включая массивы и функции), даже для объектовоберток, кото
рые представляют элементарные типы, при другом способе преобразования даю
щие значение false. Например, все нижеследующие объекты преобразуются
в значение true при использовании их в логическом контексте:
new Boolean(false) // Внутреннее значение – false, но объект преобразуется в true
new Number(0)
new String("")
new Array( )
В табл. 3.3 описывается порядок преобразования объектов в числовые значения,
когда сначала вызывается метод объекта valueOf(). Большинство объектов по
умолчанию наследуют метод valueOf() от базового объекта Object, который воз
вращает сам объект. Поскольку по умолчанию метод valueOf() не возвращает
значение элементарного типа, далее интерпретатор JavaScript пытается преоб
разовать объект в число вызовом метода toString() с последующим преобразова
нием строки в число.
В случае массивов это приводит к весьма интересным результатам. Метод
toString() у массивов преобразует элементы массива в строки и возвращает ре
зультат операции конкатенации этих строк, разделяя отдельные элементы мас
сива запятыми. Таким образом, пустой массив преобразуется в пустую строку,
что в результате дает число 0! Кроме того, если массив состоит из единственного
элемента, содержащего число n, весь массив преобразуется в строковое представ
ление числа n, которое затем будет вновь преобразовано в число n. Если массив
содержит более одного элемента или единственный элемент массива не является
числом, результатом преобразования будет значение NaN.
Тип преобразования зависит от контекста, в котором это преобразование произ
водится. Существуют такие ситуации, когда невозможно однозначно определить
контекст. Оператор + и операторы сравнения (<, <=, > и >=) могут оперировать как
числами, так и строками, таким образом, когда в одной из таких операций уча
ствует объект, возникает неоднозначность: в значение какого типа следует пре
образовать объект – в строку или в число. В большинстве случаев интерпретатор
JavaScript сначала пытается преобразовать объект с помощью метода valueOf().
Если этот метод возвращает значение элементарного типа (как правило, число),
тогда используется это значение. Однако чаще всего метод valueOf() возвращает
непреобразованный объект, и тогда интерпретатор JavaScript пытается преобра
зовать объект в строку вызовом метода toString().
1 Этот раздел содержит достаточно сложный материал, который при первом про
чтении можно пропустить.
3.15. По значению или по ссылке 61
Однако здесь есть одно исключение из правил: когда с оператором + использует
ся объект Date, преобразование сразу начинается с вызова метода toString(). Это
исключение обусловлено тем обстоятельством, что объект Date обладает собст
венными реализациями методов toString() и valueOf(). Однако когда объект Date
указывается с оператором +, чаще всего подразумевается операция конкатена
ции строк, а при выполнении операции сравнения практически всегда требуется
определить, какая из двух дат является более ранней по времени.
Большинство объектов либо вообще не имеют метода valueOf(), либо этот метод
не возвращает значение требуемого элементарного типа. Когда объект использу
ется с оператором +, обычно выполняется операция конкатенации строк вместо
сложения чисел. Аналогично, когда объект участвует в операциях сравнения,
обычно производится сравнение строковых значений, а не чисел.
Объекты, обладающие собственной реализацией метода valueOf(), могут вести
себя иначе. Если вы переопределите метод valueOf(), чтобы он возвращал число,
над объектом можно будет выполнять арифметические и другие числовые опера
ции, но операция сложения объекта со строкой может не дать желаемого резуль
тата, поскольку метод valueOf() возвратит значение элементарного типа, и метод
toString() вызван уже не будет. В результате к строке будет добавляться строко
вое представление числа, возвращаемого методом valueOf().
Наконец, следует запомнить, что метод valueOf() не вызывает метод toNumber().
Строго говоря, назначение этого метода состоит в том, чтобы преобразовать объ
ект в разумное значение элементарного типа; по этой причине в некоторых объ
ектах методы valueOf() возвращают строку.
3.15. По значению или по ссылке
В JavaScript, как и в других языках программирования, имеется возможность
манипулировать данными тремя способами.1 Первый способ – это копирование
данных. Например, значение можно присвоить новой переменной. Второй спо
соб – передать значение функции или методу. Третий – сравнить его с другим
значением, чтобы проверить, равны ли эти значения. Для понимания языка
программирования совершенно необходимо разобраться, как в нем выполняют
ся эти три действия.
Существует два фундаментальных способа манипулирования данными: по зна'
чению и по ссылке. Когда выполняется манипулирование данной величиной по
значению, это означает, что в операции участвует собственно значение данной
величины. В операции присваивания создается копия фактического значения,
после чего эта копия сохраняется в переменной, в свойстве объекта или в эле
менте массива. Копия и оригинал – это два совершенно независимых друг от
друга значения, которые хранятся раздельно. Когда некоторая величина переда
ется функции по значению, это означает, что функции передается копия. Если
функция изменит полученное значение, эти изменения коснутся только копии
и никак не затронут оригинал. Наконец, когда величина сравнивается по значе
нию с другой величиной, два различных набора данных должны содержать одно
1 Этот раздел содержит достаточно сложный материал, который при первом про
чтении можно пропустить.
62 Глава 3. Типы данных и значения
и то же значение (это обычно подразумевает, что для выявления эквивалентно
сти величин производится их побайтное сравнение).
Другой способ манипулирования значением – по ссылке. В этом случае сущест
вует только одна фактическая копия значения, а манипулирование производит
ся посредством ссылок на это значение.1 Когда действия со значением произво
дятся по ссылке, переменные хранят не само значение, а лишь ссылки на него.
Именно эта ссылочная информация копируется, передается и участвует в опера
циях сравнения. Таким образом, в операции присваивания по ссылке участвует
сама ссылка, а не копия значения и не само значение. После присваивания пере
менная будет ссылаться на то же самое значение, что и оригинальная перемен
ная. Обе ссылки считаются абсолютно равноправными и в равной степени могут
использоваться для манипулирования значением. Если значение изменяется
с помощью одной ссылки, эти изменения будут наблюдаться с помощью другой
ссылки. То же происходит, когда значение передается функции по ссылке.
В функцию попадает ссылка на значение, а функция может использовать ее для
изменения самого значения. Любые такие изменения становятся видимыми за
пределами функции. Наконец, когда выполняется операция сравнения по ссыл
ке, происходит сравнение двух ссылок, чтобы проверить, не ссылаются ли они
на одно и то же значение. Ссылки на два разных значения, даже эквивалентные
(т. е. состоящие из тех же самых байтов данных), не могут считаться равными.
Это два абсолютно разных способа манипулирования значениями, и разобраться
в них совершенно необходимо. В табл. 3.4 приводится краткое описание выше
изложенного. Данная дискуссия, посвященная манипулированию данными по
ссылке и по значению, носила достаточно общий характер, но она с небольшими
отличиями вполне применима ко всем языкам программирования. В следую
щих разделах описываются характерные отличия, свойственные языку Java
Script. Там рассказывается о том, какими типами данных манипулировать по
значению, а какими – по ссылке.
Таблица 3.4. Передача данных по ссылке и по значению
По значению По ссылке
Копирова Выполняется копирование Копируется только ссылка на значение.
ние самого значения – образуют Если значение будет изменено с помощью
ся две независимые друг от вновь созданной копии ссылки, эти изме
друга копии. нения будут наблюдаться при использо
вании оригинальной ссылки.
Передача Функции передается отдель Функции передается ссылка на значение.
ная копия значения. Измене Если внутри функции значение будет из
ние этой копии не оказывает менено с помощью полученной ссылки,
никакого влияния на значе эти изменения будут наблюдаться и за ее
ние за пределами функции. пределами.
1 Программисты на C и все те, кому знакома концепция указателей, должны пони
мать основную идею ссылок в данном контексте. Тем не менее следует отметить,
что JavaScript не поддерживает работу с указателями.
3.15. По значению или по ссылке 63
По значению По ссылке
Сравнение Сравниваются два разных Сравниваются две ссылки, чтобы опреде
значения (часто побайтно), лить, ссылаются ли они на одно и то же
чтобы определить, равны ли значение. Ссылки на разные значения
они. рассматриваются как неравные, даже ес
ли сами значения совершенно идентичны.
3.15.1. Элементарные и ссылочные типы
Главное правило JavaScript заключается в следующем: операции над элемен'
тарными типами производятся по значению, а над ссылочными типами, как
и следует из их названия, – по ссылке. Числа и логические величины – это эле
ментарные типы в JavaScript. Элементарные потому, что состоят из небольшого
и фиксированного числа байтов, операции над которыми с легкостью выполня
ются низкоуровневым интерпретатором JavaScript. Представителями ссылоч
ных типов являются объекты. Массивы и функции – это специализированные
типы объектов и потому также являются ссылочными типами. Эти типы данных
могут состоять из произвольного числа свойств или элементов, поэтому опериро
вать ими не так просто, как значениями элементарных типов, имеющими фик
сированные размеры. Поскольку размеры массивов и объектов могут быть чрез
вычайно велики, операции по значению над ними могут привести к неоправдан
ному копированию и сравнению гигантских объемов памяти.
А что можно сказать о строках? Строки могут иметь произвольную длину, поэто
му их вполне можно было бы рассматривать как ссылочный тип. Тем не менее
в JavaScript строки обычно рассматриваются как элементарный тип просто по
тому, что они не являются объектами. В действительности строки не вписыва
ются в двухполярный элементарноссылочный мир. Подробнее я остановлюсь на
строках чуть позже.
Лучший способ выяснить различия между данными, операции над которыми
производятся по ссылке и по значению, состоит в изучении практического при
мера. Тщательно разберитесь в следующем примере, обращая особое внимание
на комментарии. В примере 3.1 выполняется копирование, передача и сравне
ние чисел. Поскольку числа являются элементарными типами, данный пример
является иллюстрацией операций, выполняемых по значению.
Пример 3.1. Копирование, передача и сравнение величин по значению
// Первой рассматривается операция копирования по значению
var n = 1; // Переменная n хранит значение 1
var m = n; // Копирование по значению: переменная m хранит другое значение 1
// Данная функция используется для иллюстрации операции передачи величины по значению
// Как вы увидите, функция работает не совсем так, как хотелось бы
function add_to_total(total, x)
{
total = total + x; // Эта строка изменяет лишь внутреннюю копию total
}
// Теперь производится обращение к функции, которой передаются по значению числа,
// содержащиеся в переменных n и m. Копия значения из переменной n внутри функции
// доступна под именем total. Функция складывает копии значений переменных m и n,
64 Глава 3. Типы данных и значения
// записывая результат в копию значения переменной n. Однако это не оказывает
// никакого влияния на оригинальное значение переменной n за пределами функции.
// Таким образом, в результате вызова этой функции мы не получаем никаких изменений.
add_to_total(n, m);
// Сейчас мы проверим операцию сравнения по значению.
// В следующей строке программы литерал 1 представляет собой совершенно
// независимое числовое значение, "зашитое" в текст программы. Мы сравниваем
// его со значением, хранящимся в переменной n. В данном случае, чтобы
// убедиться в равенстве двух чисел, выполняется их побайтовое сравнение.
if (n == 1) m = 2; // n содержит то же значение, что и литерал 1;
// таким образом в переменную m будет записано значение 2
Теперь рассмотрим пример 3.2. В этом примере операции копирования, переда
чи и сравнения выполняются над объектами. Поскольку объекты относятся
к ссылочным типам, все действия над ними производятся по ссылке. В данном
примере использован объект Date, подробнее о котором можно прочитать в треть
ей части книги.
Пример 3.2. Копирование, передача и сравнение величин по ссылке
// Здесь создается объект, который соответствует дате Рождества в 2007 году
// Переменная xmas хранит ссылку на объект, а не сам объект
var xmas = new Date(2007, 11, 25);
// Затем выполняется копирование ссылки, получается вторая ссылка на оригинальный объект
var solstice = xmas; // Обе переменные ссылаются на тот же самый объект
// Здесь выполняется изменение объекта с помощью новой ссылки
solstice.setDate(21);
// Изменения можно наблюдать при использовании первой ссылки
xmas.getDate( ); // Возвращает 21, а не первоначальное значение 25
// То же происходит при передаче объектов и массивов в функции.
// Следующая функция складывает значения всех элементов массива.
// Функции передается ссылка на массив, а не копия массива.
// Благодаря этому функция может изменять содержимое массива, переданного
// по ссылке, и эти изменения будут видны после возврата из функции.
function add_to_totals(totals, x)
{
totals[0] = totals[0] + x;
totals[1] = totals[1] + x;
totals[2] = totals[2] + x;
}
// Наконец, далее выполняется операция сравнения по ссылке.
// При сравнении двух переменных, созданных ранее, обнаруживается,
// что они эквивалентны, поскольку ссылаются на один и тот же объект даже
// при том, что производилось изменение даты по одной из них:
(xmas == solstice) // Возвращает значение true
// Две переменные, созданные позднее, ссылаются на разные объекты,
// каждый из которых содержит одну и ту же дату.
var xmas = new Date(2007, 11, 25);
var solstice_plus_4 = new Date(2007, 11, 25);
// Но согласно правилу "сравнения по ссылке" ссылки на разные объекты
3.15. По значению или по ссылке 65
// не считаются эквивалентными!
(xmas != solstice_plus_4) // Возвращает значение true
Прежде чем закончить обсуждение темы выполнения операций над объектами
и массивами по ссылке, добавим немного ясности. Фраза «передача по ссылке» мо
жет иметь несколько смыслов. Для некоторых из вас эта фраза означает такой спо
соб вызова функции, который позволяет изменять эти значения внутри функции
и наблюдать эти изменения за ее пределами. Однако данный термин трактуется
в этой книге в несколько ином смысле. Здесь просто подразумевается, что функ
ции передается ссылка на массив или объект, но не сам объект. Функция же с по
мощью этой ссылки получает возможность изменять свойства объекта или элемен
ты массива, и эти изменения сохраняются по выходе из функции. Те из вас, кто
знаком с другими трактовками этого термина, могут заявить, что объекты и масси
вы передаются по значению, правда, этим значением фактически является ссылка
на объект, а не сам объект. Пример 3.3 наглядно иллюстрирует эту проблему.
Пример 3.3. Ссылки передаются по значению
// Здесь приводится другая версия функции add_to_totals(). Хотя она не работает,
// потому что вместо изменения самого массива она изменяет ссылку на этот массив.
function add_to_totals2(totals, x)
{
newtotals = new Array(3);
newtotals[0] = totals[0] + x;
newtotals[1] = totals[1] + x;
newtotals[2] = totals[2] + x;
totals = newtotals; // Эта строка не оказывает влияния
// на массив за пределами функции
}
3.15.2. Копирование и передача строк
Как упоминалось ранее, строки не вписываются в двухполярный элементарно
ссылочный мир. Поскольку строки не являются объектами, вполне естественно
предположить, что они относятся к элементарному типу. Если строки рассмат
ривать как элементарный тип данных, тогда в соответствии с описанными выше
правилами операции над ними должны производиться по значению. Но по
скольку строки могут иметь произвольную длину, это может приводить к непро
изводительному расходованию системных ресурсов на операции копирования
и побайтового сравнения. Таким образом, не менее естественно было бы предпо
ложить, что строки реализованы как ссылочный тип данных.
Вместо того чтобы строить предположения, можно попробовать написать не
большой фрагмент на языке JavaScript и решить проблему экспериментальным
путем. Если строки копируются и передаются по ссылке, должна иметься воз
можность изменять их содержимое с помощью ссылки, хранящейся в другой пе
ременной, или в результате передачи строки в функцию.
Однако при попытке написать такой фрагмент для проведения эксперимента вы
столкнетесь с серьезной проблемой: в JavaScript невозможно изменить содержи
мое строки. Существует метод charAt(), который возвращает символ из заданной
позиции в строке, но нет соответствующего ему метода setCharAt(), позволяюще
го ввести в эту позицию другой символ. Это не упущение. Строки в JavaScript
66 Глава 3. Типы данных и значения
преднамеренно неизменяемы – в JavaScript отсутствуют элементы языка, с по
мощью которых можно было бы изменять символы в строке.
Поскольку строки являются неизменяемыми, вопрос остается открытым, т. к.
нет никакого способа проверить, как передаются строки – по ссылке или по зна
чению. Можно предположить, что с целью повышения эффективности интер
претатор JavaScript реализован так, чтобы строки передавались по ссылке, но
это так и останется всего лишь предположением, поскольку нет возможности
проверить его экспериментально.
3.15.3. Сравнение строк
Несмотря на отсутствие возможности определить, как копируются строки, по
ссылке или по значению, существует возможность написать такой фрагмент на
JavaScript, с помощью которого можно выяснить, как именно выполняется опе
рация сравнения – по ссылке или по значению. В примере 3.4 приводится фраг
мент, выполняющий такую проверку.
Пример 3.4. Как сравниваются строки, по ссылке или по значению?
// Определить, как сравниваются строки, по ссылке или по значению,
// довольно просто. Здесь сравниваются совершенно разные строки, содержащие
// одинаковые последовательности символов. Если сравнение выполняется
// по значению, они должны интерпретироваться как эквивалентные, если же они
// сравниваются по ссылке, результат должен быть противоположным:
var s1 = "hello";
var s2 = "hell" + "o";
if (s1 == s2) document.write("Строки сравниваются по значению");
Данный эксперимент доказывает, что строки сравниваются по значению. Это
может оказаться сюрпризом для некоторых программистов, работающих с язы
ками C, C++ и Java, где строки относятся к ссылочным типам и сравниваются по
ссылке. При необходимости сравнить в этих языках фактическое содержимое
строк приходится использовать специальные методы или функции. Язык Java
Script относится к языкам высокого уровня и потому предполагает, что когда
выполняется сравнение строк, скорее всего, имеется в виду сравнение по значе
нию. Таким образом, несмотря на то, что с целью достижения более высокой эф
фективности строки в JavaScript (повидимому) копируются и передаются по
ссылке, тем не менее операция сравнения выполняется по значению.
3.15.4. По ссылке или по значению: подведение итогов
Таблица 3.5 кратко иллюстрирует то, как выполняются операции над различ
ными типами данных в JavaScript.
Таблица 3.5. Операции над типами данных в JavaScript
Тип Копирование Передача Сравнение
Число По значению По значению По значению
Логическое значение По значению По значению По значению
Строка Не изменяется Не изменяется По значению
Объект По ссылке По ссылке По ссылке
Переменные
Переменная – это имя, связанное со значением. Мы говорим, что значение хра
нится, или содержится, в переменной. Переменные позволяют хранить данные
в программе и работать с ними. Например, следующая строка JavaScriptкода
присваивает значение 2 переменной с именем i:
i = 2;
А следующая добавляет 3 к значению переменной i и присваивает результат но
вой переменной sum:
var sum = i + 3;
Это почти все, что надо знать о переменных. Но для полного понимания меха
низма их работы в JavaScript следует освоить и другие понятия, и пары строк
кода здесь недостаточно! В данной главе рассматриваются вопросы типизации,
объявления, области видимости, содержимого и разрешения имен переменных,
а также сборки мусора и двойственности понятия «переменная/свойство».1
4.1. Типизация переменных
Самое важное различие между JavaScript и такими языками, как Java и C, со
стоит в том, что JavaScript – это нетипизированный (untyped) язык. В частно
сти, это значит, что JavaScriptпеременная может содержать значение любого
типа, в отличие от Java или Cпеременной, в которой может содержаться только
определенный тип данных, заданный при ее объявлении. Так, в JavaScript мож
но присвоить переменной число, а затем присвоить той же переменной строку:
i = 10;
i = "десять";
1 Это сложный предмет, полное понимание которого требует хорошего знакомства
с материалом последующих глав книги. Начинающие могут прочесть первые два
раздела и перейти к главам 5, 6 и 7, а затем вернуться к этой главе.
68 Глава 4. Переменные
В Java, C, C++ и любом другом строго типизированном языке подобный код не
допустим.
Особенностью JavaScript, вытекающей из отсутствия типизации, является то,
что язык в случае необходимости легко и автоматически преобразует значения
из одного типа в другой. Например, если вы попытаетесь дописать число к стро
ке, JavaScript автоматически преобразует число в соответствующую строку, ко
торая может быть добавлена к имеющейся. Более подробно преобразования ти
пов рассматриваются в главе 3.
Нетипизированность языка JavaScript обусловливает его простоту по сравнению
с типизированными языками, такими как C++ и Java, преимущество которых
состоит в том, что они способствуют более строгой практике программирования,
облегчая написание, поддержку и повторное использование длинных и сложных
программ. В то же время многие JavaScriptпрограммы представляют собой ко
роткие сценарии, поэтому такая строгость необязательна, и программисты мо
гут воспользоваться преимуществами более простого синтаксиса.
4.2. Объявление переменных
Прежде чем использовать переменную в JavaScript, ее необходимо объявить.1
Переменные объявляются с помощью ключевого слова var следующим образом:
var i;
var sum;
Можно объявить несколько переменных:
var i, sum;
Кроме того, объявление переменной можно совмещать с ее инициализацией:
var message = "hello";
var i = 0, j = 0, k = 0;
Если начальное значение не задано в инструкции var, то переменная объявляет
ся, но ее начальное значение остается неопределенным (undefined), пока не будет
изменено программой.
Обратите внимание, что инструкция var также может включаться в циклы for
и for/in (о которых рассказывается в главе 6), что позволяет объявлять перемен
ную цикла непосредственно в самом цикле. Например:
for(var i = 0; i < 10; i++) document.write(i, "<br>");
for(var i = 0, j = 10; i < 10; i++, j) document.write(i*j, "<br>");
for(var i in o) document.write(i, "<br>");
Переменные, объявленные с помощью инструкции var, называются долговре'
менными (permanent): попытка удалить их с помощью оператора delete приве
дет к ошибке. (Оператор delete рассматривается в главе 5.)
1 Если этого не сделать, то переменная неявно будет объявлена самим интерпрета
тором JavaScript.
4.3. Область видимости переменной 69
4.2.1. Повторные и опущенные объявления
С помощью инструкции var можно объявить одну и ту же переменную несколько
раз. Если повторное объявление содержит инициализатор, то оно действует как
обычная инструкция присваивания.
Если попытаться прочитать значение необъявленной переменной, JavaScript
сгенерирует сообщение об ошибке. Если присвоить значение переменной, не
объявленной с помощью инструкции var, JavaScript неявно объявит эту пере
менную за вас. Однако переменные, объявленные таким образом, всегда созда
ются как глобальные, даже если они работают только в теле функции. Чтобы не
создавать глобальную переменную (или не использовать существующую), когда
достаточно локальной переменной для отдельной функции, всегда помещайте
инструкцию var в тело функции. Лучше всего объявлять с ключевым словом var
все переменные – и глобальные, и локальные. (Различие между локальными
и глобальными переменными подробнее рассматривается в следующем разделе.)
4.3. Область видимости переменной
Область видимости (scope) переменной – это та часть программы, для которой
эта переменная определена. Глобальная переменная имеет глобальную область
видимости – она определена для всей JavaScriptпрограммы. Переменные, объ
явленные внутри функции, определены только в ее теле. Они называются локаль'
ными и имеют локальную область видимости. Параметры функций также счита
ются локальными переменными, определенными только в теле этой функции.
Внутри тела функции локальная переменная имеет преимущество перед гло
бальной переменной с тем же именем. Если объявить локальную переменную
или параметр функции с тем же именем, что у глобальной переменной, то фак
тически глобальная переменная будет скрыта. Так, следующий код печатает
слово «локальная»:
var scope = "глобальная"; // Объявление глобальной переменной
function checkscope() {
var scope = "локальная"; // Объявление локальной переменной с тем же именем
document.write(scope); // Используется локальная переменная, а не глобальная
}
checkscope(); // Печатается слово "локальная"
Объявляя переменные с глобальной областью видимости, инструкцию var мож
но опустить, но при объявлении локальных переменных она необходима. По
смотрите, что получается, если этого не сделать:
scope = "глобальная"; // Объявление глобальной переменной, даже без var
function checkscope() {
scope = "локальная"; // Ой! Мы только что изменили глобальную переменную
document.write(scope); // Используется глобальная переменная
myscope = "локальная"; // Здесь мы неявно объявляем новую глобальную переменную
document.write(myscope); // Используется новая глобальная переменная
}
checkscope(); // Печатает "локальнаялокальная"
document.write(scope); // Печатает "локальная"
document.write(myscope); // Печатает "локальная"
70 Глава 4. Переменные
Функции, как правило, не знают, какие переменные объявлены в глобальной
области видимости или для чего они нужны. Поэтому функция, использующая
глобальную переменную вместо локальной, рискует изменить значение, необхо
димое какойлибо другой части программы. К счастью, избежать этой неприят
ности легко: объявляйте все переменные с помощью инструкции var.
Определения функций могут быть вложенными. Каждая функция имеет собст
венную локальную область видимости, поэтому может быть несколько вложен
ных уровней локальных областей видимости. Например:
var scope = "глобальная область видимости"; // Глобальная переменная
function checkscope() {
var scope = "локальная область видимости"; // Локальная переменная
function nested() {
var scope = "вложенная область видимости"; // Вложенная область видимости
// локальных переменных
document.write(scope); // Печатает "вложенная область видимости"
}
nested();
}
checkscope();
4.3.1. Отсутствие блочной области видимости
Обратите внимание: в отличие от C, C++ и Java, в JavaScript нет области види
мости на уровне блоков. Все переменные, объявленные внутри функции, незави
симо от того, где именно это сделано, определены во всей функции. В следующем
фрагменте переменные i, j и k имеют одинаковые области видимости: все три оп
ределены во всем теле функции. Это было бы не так, если бы код был написан на
C, C++ или Java:
function test(o) {
var i = 0; // i определена во всей функции
if (typeof o == "object") {
var j = 0; // j определена везде, а не только в блоке
for(var k = 0; k < 10; k++) { // k определена везде, не только в цикле
document.write(k);
}
document.write(k); // k все еще определена: печатается 10
}
document.write(j); // j определена, но может быть не инициализирована
}
Правило, согласно которому все переменные, объявленные в функции, опреде
лены во всей функции, может иметь удивительные следствия. Например:
var scope = "глобальная";
function f() {
alert(scope); // Показывает "undefined", а не "глобальная".
var scope = "локальная"; // Переменная инициализируется здесь,
// но определена она везде в функции.
alert(scope); // Показывает "локальная"
}
f();
Ктото может подумать, что в результате первого вызова alert() будет напечата
но слово «глобальная», т. к. инструкция var, объявляющая локальную перемен
4.4. Элементарные и ссылочные типы 71
ную, еще не была выполнена. Однако согласно правилу определения областей
видимости все происходит не так. Локальная переменная определена во всем те
ле функции, значит, глобальная переменная с тем же именем скрыта во всем те
ле функции. Хотя локальная переменная определена везде, до выполнения ин
струкции var она не инициализирована. Поэтому функция f в предыдущем при
мере эквивалентна следующему фрагменту:
function f() {
var scope; // Локальная переменная определяется в начале функции
alert(scope); // Здесь она существует, но имеет значение undefined
scope = "локальная"; // Здесь мы инициализируем переменную и присваиваем ей значение
alert(scope); // Здесь она уже имеет значение
}
Этот пример показывает, почему хорошая практика программирования подра
зумевает помещение всех объявлений переменных в начале функции.
4.3.2. Неопределенные и неинициализированные
переменные
Примеры предыдущего раздела демонстрируют тонкий момент программирова
ния на JavaScript: имеется два вида неопределенных переменных. Первый – пе
ременная, которая нигде не объявлена. Попытка прочитать значение необъявлен'
ной переменной приведет к ошибке времени выполнения. Необъявленные пере
менные не определены, потому что они просто не существуют. Как уже было ска
зано, присваивание значения необъявленной переменной не приводит к ошибке –
просто она при присваивании неявно объявляется как глобальная переменная.
Второй вид неопределенных переменных – переменная, которая была объявлена,
но значение ей нигде не присваивалось. Если прочитать значение одной из таких
переменных, то будет получено ее значение по умолчанию – undefined. Такие пе
ременные лучше называть неинициализированными (unassigned), чтобы отли
чить их от тех переменных, которые вообще не объявлялись и не существуют.
В следующем фрагменте иллюстрируются некоторые различия между неопреде
ленными и неинициализированными переменными:
var x; // Объявляем неинициализированную переменную. Значением ее является undefined.
alert(u); // Использование необъявленной переменной приведет к ошибке.
u = 3; // Присваивание значения необъявленной переменной создает эту переменную.
4.4. Элементарные и ссылочные типы
Следующая тема, которую мы рассмотрим, – содержимое переменных. Мы часто
говорим, что переменные содержат значения. Что же они содержат в действи
тельности? Чтобы ответить на этот, казалось бы, простой вопрос, мы должны
снова взглянуть на типы данных, поддерживаемые JavaScript. Эти типы можно
разделить на две группы: элементарные и ссылочные.
Числа, логические значения, а также значения null и undefined – это элементар
ные типы. Объекты, массивы и функции – это ссылочные типы.
Элементарный тип имеет фиксированный размер. Например, число занимает во
семь байтов, а логическое значение может быть представлено всего одним битом.
72 Глава 4. Переменные
Числовой тип – самый большой из элементарных типов. Если для каждой Java
Scriptпеременной зарезервировано в памяти восемь байтов, переменная может
непосредственно содержать значение любого элементарного типа.1
Однако ссылочные типы – это другое дело. Например, объекты могут быть любой
длины – они не имеют фиксированного размера. То же самое относится и к мас
сивам: массив может иметь любое число элементов. Аналогично функция может
содержать любой объем JavaScriptкода. Поскольку данные типы не имеют фик
сированного размера, их значения не могут непосредственно храниться в восьми
байтах памяти, связанных с каждой переменной. Поэтому в переменной хранит
ся ссылка на это значение. Обычно эта ссылка представляет собой какойлибо
указатель или адрес в памяти. Ссылка – это не само значение, но она сообщает
переменной, где это значение можно найти.
Различие между элементарными и ссылочными типами существенно, т. к. они
ведут себя поразному. Рассмотрим следующий код, оперирующий числами
(элементарный тип):
var a = 3.14; // Объявление и инициализация переменной
var b = a; // Копирование значения переменной в новую переменную
a = 4; // Модификация значения исходной переменной
alert(b) // Показывает 3.14; копия не изменилась
В этом фрагменте нет ничего необычного. Теперь посмотрим, что произойдет, ес
ли слегка изменить код, заменив числа массивами (ссылочный тип):
var a = [1,2,3]; // Инициализируем переменную ссылкой на массив
var b = a; // Копируем эту ссылку в новую переменную
a[0] = 99; // Изменяем массив, используя первоначальную ссылку
alert(b); // Показываем измененный массив [99,2,3], используя новую ссылку
Те, кого результат не удивил, уже хорошо знакомы с различием между элемен
тарными и ссылочными типами. Тем же, кого он удивил, надо посмотреть вни
мательнее на вторую строку. Обратите внимание, что в этом предложении вы
полняется присваивание ссылки на значение типа «массив», а не присваивание
самого массива. После второй строки фрагмента мы все еще имеем один объект
массива; нам только удалось получить две ссылки на него.
Если разница между элементарным и ссылочным типами вам внове, просто по
старайтесь держать в уме содержимое переменной. Переменные содержат фак
тические значения элементарных типов, но лишь ссылки на значения ссылоч
ных типов. Разное поведение базовых и ссылочных типов более подробно изуча
ется в разделе 3.15.
Вы могли заметить, что я не указал, относятся ли строки в JavaScript к базовым
или к ссылочным типам. Строки – это необычный случай. Они имеют переменную
длину и потому, очевидно, не могут храниться непосредственно в переменных фик
сированного размера. Исходя из соображений эффективности можно ожидать, что
интерпретатор JavaScript будет копировать ссылки на строки, а не их фактическое
содержимое. В то же время во многих отношениях строки ведут себя как элемен
тарные типы. Вопрос о том, к какому типу принадлежат строки, элементарному
1 Это упрощение, которое не следует рассматривать как описание фактической ре
ализации JavaScript.
4.5. Сборка мусора 73
или ссылочному, спорный, т. к. строки на самом деле неизменны: нет возможности
избирательно изменить содержимое внутри строкового значения. Это значит, что
нельзя составить пример, похожий на предыдущий, в котором массивы копирова
лись бы по ссылке. В конце концов, не имеет значения, как рассматривать строки,
как неизменный ссылочный тип, ведущий себя как элементарный, или как эле
ментарный тип, реализованный с использованием механизма ссылочного типа.
4.5. Сборка мусора
Ссылочные типы не имеют фиксированного размера; в самом деле, некоторые из
них могут быть очень большими. Мы уже говорили о том, что переменные не со
держат непосредственных значений ссылочного типа. Значения хранятся в ка
комлибо другом месте, а в переменных находится только ссылка на это местопо
ложение. А сейчас кратко остановимся на реальном хранении значений.
Поскольку строки, объекты и массивы не имеют фиксированного размера, место
для их хранения должно выделяться динамически, когда становится известен
размер. Когда JavaScriptпрограмма создает строку, массив или объект, интерпре
татор должен выделить память для хранения этой сущности. Память, выделяемая
подобным образом, должна быть впоследствии освобождена, иначе интерпретатор
JavaScript исчерпает всю доступную память, что приведет к отказу системы.
В таких языках, как C и C++, память приходится освобождать вручную. Именно
программист отвечает за отслеживание всех создаваемых объектов и, когда они
больше не требуются, – за их ликвидацию (освобождение памяти). Это бывает
довольно обременительно и часто приводит к ошибкам.1
В JavaScript, где не надо вручную освобождать память, реализована технология,
называемая сборкой мусора (garbage collection). Интерпретатор JavaScript мо
жет обнаружить, что объект никогда более не будет использоваться программой.
Определив, что объект недоступен (т. е. больше нет способа получения ссылки
на него), интерпретатор выясняет, что объект более не нужен, и занятая им па
мять может быть освобождена.2 Рассмотрим следующие строки кода:
var s = "hello"; // Выделяем память для строки
var u = s.toUpperCase(); // Создаем новую строку
s = u; // Переписываем ссылку на первоначальную строку
После работы этого кода исходная строка "hello" больше недоступна – ни в одной
из переменных программы нет ссылки на нее. Система определяет этот факт
и освобождает память.
1 Это не совсем строго: для локальных (объявленных в функции) переменных, раз
мещаемых в стеке, какой бы сложной структуры ни были переменные, автомати
чески вызывается деструктор и производится освобождение памяти. Точно так
же ведут себя контейнеры STL, или «собственные данные потока». Утверждение
автора в абсолютной степени относится только к объектам, динамически распре
деляемым операторами new и delete. – Примеч. науч. ред.
2 Описываемая схема сборки мусора, известная как «подсчет числа ссылок», может
иметь серьезные проблемы в более сложных программах при появлении объектов
с циклическими ссылками – объекты никогда не будут освобождены. Эта проблема
хорошо изучена в Perl; способы борьбы см. в описании языка. – Примеч. науч. ред.
74 Глава 4. Переменные
Сборка мусора выполняется автоматически и невидима для программиста.
О сборке мусора он должен знать ровно столько, сколько ему требуется, чтобы
доверять ее работе, – он не должен думать, куда делись все старые объекты.
4.6. Переменные как свойства
Вы уже могли заметить, что в JavaScript между переменными и свойствами объ
ектов много общего. Им одинаково присваиваются значения, они одинаково
применяются в JavaScriptвыражениях и т. д. Есть ли какаянибудь принципи
альная разница между переменной i и свойством i объекта o? Ответ: никакой.
Переменные в JavaScript принципиально не отличаются от свойств объекта.
4.6.1. Глобальный объект
Одно из первых действий, выполняемых интерпретатором JavaScript при запус
ке перед исполнением любого кода, – это создание глобального объекта. Свойст
ва этого объекта представляют собой глобальные переменные JavaScriptпро
граммы. Объявляя в JavaScript глобальную переменную, фактически вы опреде
ляете свойство глобального объекта.
Интерпретатор JavaScript инициализирует ряд свойств глобального объекта,
ссылающихся на предопределенные значения и функции. Так, свойства Infini
ty, parseInt и Math ссылаются на число «бесконечность», предопределенную
функцию parseInt() и предопределенный объект Math. Более подробно прочитать
об этих глобальных значениях можно в третьей части книги.
В коде верхнего уровня (т. е. JavaScriptкоде, который не является частью функции)
сослаться на глобальный объект можно посредством ключевого слова this. Внутри
функций ключевое слово this имеет другое применение, которое описано в главе 8.
В клиентском языке JavaScript в качестве глобального объекта для всего Java
Scriptкода, содержащегося в соответствующем ему окне броузера, служит объ
ект Window. Этот глобальный объект имеет свойство window, ссылающееся на сам
объект, которое можно использовать вместо ключевого слова this для ссылки на
глобальный объект. Объект Window определяет базовые глобальные свойства, та
кие как parseInt и Math, а также глобальные клиентские свойства, такие как
navigator и screen.
4.6.2. Локальные переменные объект вызова
Если глобальные переменные – это свойства специального глобального объекта,
так что же тогда такое локальные переменные? Они тоже представляют собой
свойства объекта. Этот объект называется объектом вызова (call object). Когда вы
полняется тело функции, аргументы и локальные переменные функции хранятся
как свойства этого объекта. Использование абсолютно отдельного объекта для ло
кальных переменных позволяет JavaScript не допускать переписывания локаль
ными переменными значений глобальных переменных с теми же именами.
4.6.3. Контексты исполнения в JavaScript
Начиная исполнять функцию, интерпретатор JavaScript создает для нее новый
контекст исполнения (execution context), т. е. контекст, в котором выполняется
4.7. Еще об области видимости переменных 75
любой фрагмент JavaScriptкода. Важная часть контекста – объект, в котором
определены переменные. Поэтому код JavaScriptпрограммы, не являющийся
частью какойлибо функции, работает в контексте исполнения, в котором для
определений переменных используется глобальный объект. А любая JavaScript
функция работает в собственном уникальном контексте исполнения с собствен
ным объектом вызова, в котором определены локальные переменные.
Интересно отметить, что реализации JavaScript могут допускать несколько гло
бальных контекстов исполнения с отдельным глобальным объектом каждый.1
(Хотя в этом случае каждый глобальный объект не является действительно гло
бальным.) Очевидный пример – это клиентский JavaScript, в котором каждое
отдельное окно броузера или каждый фрейм в окне определяет отдельный гло
бальный контекст исполнения. Код клиентского JavaScript в каждом фрейме
или окне работает в собственном контексте исполнения и имеет собственный
глобальный объект. Однако эти отдельные клиентские глобальные объекты име
ют свойства, связывающие их друг с другом. Другими словами, JavaScriptкод
в одном фрейме может ссылаться на другой фрейм с помощью выражения pa
rent.frames[1], а на глобальную переменную x в первом фрейме можно сослаться
из второго фрейма с помощью выражения parent.frames[0].x.
Вам не обязательно уже сейчас полностью понимать, как связываются вместе
контексты исполнения отдельных окон и фреймов в клиентском JavaScript. Эту
тему мы подробно рассмотрим при обсуждении интеграции JavaScript с веббро
узерами в главе 13. Сейчас достаточно знать, что гибкость JavaScript позволяет
одному интерпретатору JavaScript исполнять сценарии в различных глобаль
ных контекстах исполнения и что этим контекстам не нужно быть совершенно
раздельными – они могут ссылаться друг на друга.
Последнее утверждение надо рассмотреть подробнее. Если JavaScriptкод в од
ном контексте исполнения может читать и писать значения свойств и выполнять
функции, определенные в другом контексте исполнения, то становятся актуаль
ными вопросы безопасности. Возьмем в качестве примера клиентский Java
Script. Предположим, что окно броузера A запускает сценарий или содержит ин
формацию из вашей локальной сети, а окно B запускает сценарий с некоторого
произвольного сайта в Интернете. Скорее всего, мы не захотим предоставлять
коду в окне B доступ к свойствам в окне A. Ведь тогда этот код получит возмож
ность прочитать важную корпоративную информацию и, например, украсть ее.
Следовательно, безопасный запуск JavaScriptкода должен обеспечить специ
альный механизм, предотвращающий доступ из одного контекста исполнения
в другой, если такой доступ не разрешен. Мы вернемся к этой теме в разделе 13.8.
4.7. Еще об области видимости переменных
Когда мы в первый раз обсуждали понятие области видимости переменной, я оп
ределил его только на основе лексической структуры JavaScriptкода: глобаль
ные переменные имеют глобальную область видимости, а переменные, объяв
ленные в функции, – локальную. Если одно определение функции вложено
1 Это отход от темы; если он вам неинтересен, спокойно переходите к следующему
разделу.
76 Глава 4. Переменные
в другое, то переменные, объявленные в этой вложенной функции, имеют вло
женную локальную область видимости. Теперь, когда мы знаем, что глобальные
переменные представляют собой свойства глобального объекта, а локальные –
свойства особого объекта вызова, мы можем вернуться к понятию области види
мости переменной и переосмыслить его. Это даст нам хорошую возможность по
новому взглянуть на существование переменных во многих контекстах и глубже
понять, как работает JavaScript.
В JavaScript с каждым контекстом исполнения связана цепочка областей види'
мости (scope chain), представляющая собой список, или цепочку, объектов. Ко
гда JavaScriptкоду требуется найти значение переменной x (этот процесс назы
вается разрешением имени переменной), он начинает поиск в первом (наиболее
глубоком) объекте цепочки. Если в этом объекте отыскивается свойство с име
нем x, то используется значение этого свойства. Если в первом объекте не удает
ся найти свойство с именем x, то JavaScript продолжает поиск в следующем объ
екте цепочки. Если во втором объекте тоже не найдено свойство с именем x, по
иск продолжается в следующем объекте, и т. д.
В JavaScriptкоде верхнего уровня (в коде, не содержащемся ни в одном из опре
делений функций), цепочка областей видимости состоит только из глобального
объекта. Все переменные разыскиваются в этом объекте. Если переменная не су
ществует, то ее значение равно undefined. В функции (не вложенной) цепочка об
ластей видимости состоит из двух объектов. Когда функция ссылается на пере
менную, в первую очередь проверяется объект вызова (локальная область види
мости), во вторую очередь – глобальный объект (глобальная область видимости).
Вложенная функция будет иметь три или более объектов в цепочке областей ви
димости. Процесс поиска имени переменной в цепочке областей видимости
функции иллюстрирует рис. 4.1.
Рис. 4.1. Цепочка областей видимости и разрешения имени переменной
Выражения и операторы
В этой главе объясняется, как работают выражения и операторы в JavaScript.
Те, кто знаком с C, C++ или Java, заметят, что в JavaScript выражения и опера
торы очень похожи, и смогут ограничиться беглым просмотром этой главы. А те,
кто не программирует на C, C++ или Java, в этой главе узнают все, что требуется
знать о выражениях и операторах в JavaScript.
5.1. Выражения
Выражение – это фраза языка JavaScript, которая может быть вычислена интер
претатором для получения значения. Простейшие выражения – это литералы
или имена переменных, например:
1.7 // Числовой литерал
"JavaScript is fun!" // Строковый литерал
true // Логический литерал
null // Литерал значения null
/java/ // Литерал регулярного выражения
{ x:2, y:2 } // Объектный литерал
[2,3,5,7,11,13,17,19] // Литерал массива
function(x){return x*x;} // Функциональный литерал
i // Переменная i
sum // Переменная sum
Значение выражениялитерала – это просто значение самого литерала. Значение
выраженияпеременной – это значение, содержащееся в переменной, или значе
ние, на которое переменная ссылается.
Эти выражения не очень интересны. Путем объединения простых выражений
могут создаваться более сложные (и интересные) выражения. Например, мы ви
дели, что и 1.7 – это выражение, и i – это выражение. Следующий пример тоже
представляет собой выражение:
i + 1.7
78 Глава 5. Выражения и операторы
Значение этого выражения определяется путем сложения значений двух более
простых выражений. Знак + в этом примере – это оператор, объединяющий два
выражения в одно более сложное. Другим оператором является (минус), объ
единяющий выражения путем вычитания. Например:
(i + 1.7) sum
В этом выражении оператор «минус» применяется для вычитания значения пе
ременой sum из значения предыдущего выражения, i + 1.7. Как вы увидите
в следующем разделе, JavaScript поддерживает несколько других операторов,
помимо + и .
5.2. Обзор операторов
Если вы программировали на C, C++ или Java, то большинство JavaScriptопе
раторов должны быть уже вам знакомо. Они сведены в табл. 5.1, к которой мож
но обращаться как к справочнику. Обратите внимание: большинство операторов
обозначаются символами пунктуации, такими как + и =, а некоторые – ключевы
ми словами, например delete и instanceof. И ключевые слова, и знаки пунктуа
ции обозначают обычные операторы, просто в первом случае получается более
удобочитаемый и менее лаконичный синтаксис.
В этой таблице столбец, обозначенный буквой «P», содержит приоритет операто
ра, а столбец, обозначенный буквой «A», – ассоциативность оператора (либо L –
слева направо, либо R – справа налево). Те, кто пока не понимает, что это такое,
получат разъяснения в следующих подразделах, после которых приводятся опи
сания самих операторов.
Таблица 5.1. JavaScript'операторы
P A Оператор Типы операндов Выполняемая операция
15 L . Объект, идентификатор Обращение к свойству
L [] Массив, целое число Индексация массива
L () Функция, аргументы Вызов функции
R new Вызов конструктора Создание нового объекта
14 R ++ Левостороннее Префиксный или постфиксный
выражение инкремент (унарный)
R Левостороннее Префиксный или постфиксный
выражение декремент (унарный)
R Число Унарный минус (смена знака)
R + Число Унарный плюс (нет операции)
R ~ Целое число Поразрядное дополнение (унарный)
R ! Логическое значение Логическое дополнение (унарный)
R delete Левостороннее Аннулирование определения
значение свойства (унарный)
R typeof Любой Возвращает тип данных (унарный)
5.2. Обзор операторов 79
P A Оператор Типы операндов Выполняемая операция
R void Любой Возвращает неопределенное
значение (унарный)
13 L *, /, % Числа Умножение, деление, остаток
12 L +, Числа Сложение, вычитание
L + Строки Конкатенация строк
11 L << Целые числа Сдвиг влево
L >> Целые числа Сдвиг вправо с расширением
знакового разряда
L >>> Целые числа Сдвиг вправо с дополнением нулями
10 L <, <= Числа или строки Меньше чем, меньше или равно
L >, >= Числа или строки Больше чем, больше или равно
L instanceof Объект, конструктор Проверка типа объекта
L in Строка, объект Проверка наличия свойства
9 L == Любой Проверка на равенство
L != Любой Проверка на неравенство
L === Любой Проверка на идентичность
L !== Любой Проверка на неидентичность
8 L & Целые числа Поразрядная операция И
7 L ^ Целые числа Поразрядная операция
исключающего ИЛИ
6 L | Целые числа Поразрядная операция ИЛИ
5 L && Логические значения Логическое И
4 L || Логические значения Логическое ИЛИ
3 R ?: Логическое значение, Условный трехместный оператор
любое, любое
2 R = Левостороннее Присваивание
значение, любое
R *=, /=, %=, +=, Левостороннее Присваивание с операцией
=, <<=, >>=, значение, любое
>>>=, &=, ^=, |=
1 L , Любой Множественное вычисление
5.2.1. Количество операндов
Операторы могут быть разбиты на категории по количеству требуемых им опе
рандов. Большинство JavaScriptоператоров, таких как оператор +, о котором
мы уже говорили, двухместные. Такие операторы объединяют два выражения
в одно, более сложное. Таким образом, эти операторы работают с двумя операн
дами. JavaScript поддерживает также несколько унарных операторов, которые
80 Глава 5. Выражения и операторы
преобразуют одно выражение в другое, более сложное. Оператор «минус» в вы
ражении 3 представляет собой унарный оператор, выполняющий смену знака
для операнда 3. И наконец, JavaScript поддерживает один тернарный оператор,
условный оператор ?:, который объединяет в одно значение три выражения.
5.2.2. Тип операндов
Создавая JavaScriptвыражения, необходимо обращать внимание на типы дан
ных, передаваемых операторам, и на типы данных, которые они возвращают.
Различные операторы требуют, чтобы операнды возвращали значения опреде
ленного типа. Например, нельзя выполнить умножение строк, поэтому выраже
ние "a" * "b" не является допустимым в JavaScript. Однако интерпретатор Java
Script по мере возможности будет пытаться преобразовывать выражение в тре
буемый тип, поэтому выражение "3" * "5" вполне допустимо. Его значением будет
число 15, а не строка "15". Более подробно о преобразованиях типов в JavaScript
рассказывалось в разделе 3.12.
Некоторые операторы ведут себя поразному в зависимости от типа операндов.
Самый яркий пример – оператор +, который складывает числовые операнды
и выполняет конкатенацию строк. Кроме того, если ему передать одну строку
и одно число, он преобразует число в строку и выполнит конкатенацию двух по
лученных строк. Например, результатом выражения "1" + 0 будет строка "10".
Обратите внимание, что операторы присваивания, как и некоторые другие, тре
буют в качестве выражений в левой части левостороннего значения (lvalue). Ле
востороннее значение – это исторический термин, обозначающий «выражение,
которое может присутствовать в левой части оператора присваивания». В Java
Script левосторонними значениями являются переменные, свойства объектов
и элементы массивов. Спецификация ECMAScript разрешает встроенным функ
циям возвращать левосторонние значения, но не определяет никаких встроен
ных функций, ведущих себя подобным образом.
И наконец, операторы не всегда возвращают значения того же типа, к которому
принадлежат операнды. Операторы сравнения (меньше, равно, больше и т. д.)
принимают в качестве аргументов различные типы, но всегда возвращают ре
зультат логического типа, показывающий, истинно ли сравнение. Так, выраже
ние a < 3 возвращает значение true, если значение переменной a действительно
меньше, чем 3. Как мы увидим, логические значения, возвращаемые оператора
ми сравнения, используются в инструкциях if, циклах while и for, управляю
щих в JavaScript исполнением программы в зависимости от результатов вычис
ления выражений с операторами сравнения.
5.2.3. Приоритет операторов
В табл. 5.1 в столбце, помеченном буквой «P», указан приоритет каждого опе
ратора. Приоритет оператора управляет порядком, в котором выполняются опе
рации. Операторы с большим значением приоритета в столбце «P» выполняются
раньше, чем те, для которых указаны меньшие значения приоритетов.
Рассмотрим следующее выражение:
w = x + y * z;
5.3. Арифметические операторы 81
Оператор умножения * имеет больший приоритет по сравнению с оператором
сложения +, поэтому умножение выполняется раньше сложения. Кроме того,
оператор присваивания = имеет наименьший приоритет, поэтому присваивание
выполняется после завершения всех операций в правой части.
Приоритет операторов может быть переопределен с помощью скобок. Для того
чтобы сложение в предыдущем примере выполнялось раньше, надо написать:
w = (x + y)* z;
На практике, если вы не уверены в приоритетах операторов, проще всего явно
задать порядок вычислений с помощью скобок. Важно знать лишь следующие
правила: умножение и деление выполняются раньше сложения и вычитания,
а присваивание имеет очень низкий приоритет и почти всегда выполняется по
следним.
5.2.4. Ассоциативность операторов
В табл. 5.1 в столбце, помеченном буквой «A», указана ассоциативность опера
тора. Значение L задает ассоциативность слева направо, а значение R – ассоциа
тивность справа налево. Ассоциативность оператора определяет порядок выпол
нения операций с одинаковым приоритетом. Ассоциативность слева направо
означает, что операции выполняются слева направо. Например, оператор сложе
ния имеет ассоциативность слева направо, поэтому следующие два выражения
эквивалентны:
w = x + y + z;
w = ((x + y) + z);
Теперь обратите внимание на такие (практически бессмысленные) выражения:
x = ~~y;
w = x = y = z;
q = a?b:c?d:e?f:g;
Они эквивалентны следующим выражениям:
x = ~((~y));
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));
Причина в том, что унарные операторы, операторы присваивания и условные
тернарные операторы имеют ассоциативность справа налево.
5.3. Арифметические операторы
Рассказав о приоритетах, ассоциативности и других второстепенных вопросах,
мы можем начать обсуждение самих операторов. В этом разделе приведены опи
сания арифметических операторов:
Сложение (+)
Оператор «плюс» складывает числовые операнды или выполняет конкатена
цию строк. Если одним из операндов является строка, другой операнд преобра
зуется в строку и выполняется конкатенация. Операндыобъекты преобразу
82 Глава 5. Выражения и операторы
ются в числа или строки, которые могут быть сложены или конкатенированы.
Преобразование выполняется с помощью методов valueOf() и/или toString().
Вычитание ()
Когда «минус» используется в качестве двухместного оператора, он выполня
ет вычитание второго операнда из первого. Если указаны нечисловые операн
ды, то оператор пытается преобразовать их в числа.
Умножение (*)
Оператор * умножает два своих операнда. Нечисловые операнды он пытается
преобразовать в числа.
Деление (/)
Оператор / делит первый операнд на второй. Нечисловые операнды он пыта
ется преобразовать в числа. Те, кто привык к языкам программирования,
различающим целые и вещественные числа, могут ожидать получения цело
численного результата при делении одного целого на другое. Однако в Java
Script все числа вещественные, поэтому результатом любого деления являет
ся значение с плавающей точкой. Операция 5/2 дает 2.5, а не 2. Результат де
ления на ноль – плюс или минус бесконечность, а 0/0 дает NaN.
Деление по модулю (%)
Оператор % вычисляет остаток, получаемый при целочисленном делении пер
вого операнда на второй. Если заданы нечисловые операнды, то оператор пы
тается преобразовать их в числа. Знак результата совпадает со знаком перво
го операнда, например 5 % 2 дает 1. Оператор деления по модулю обычно при
меняется к целым операндам, но работает и для вещественных значений. На
пример, 4.3 % 2.1 дает результат 0.1.
Унарный минус ()
Когда минус используется в качестве унарного оператора, он указывается пе
ред одиночным операндом и выполняет унарную операцию смены знака.
Другими словами, он преобразует положительное значение в отрицательное,
и наоборот. Если операнд не является числом, этот оператор пытается преоб
разовать его в число.
Унарный плюс (+)
Для симметрии с оператором «унарный минус» в JavaScript также имеется
оператор «унарный плюс». При помощи этого оператора можно явно задать
знак числовых литералов, если вы считаете, что это сделает текст программы
более понятным:
var profit = +1000000;
В таком коде оператор «плюс» ничего не делает; результатом его работы яв
ляется значение его аргумента. Однако нечисловые аргументы он преобразу
ет в числа. Если аргумент не может быть преобразован, возвращается NaN.
Инкремент (++)
Этот оператор инкрементирует (т. е. увеличивает на единицу) свой единст
венный операнд, который должен быть переменной, элементом массива или
свойством объекта. Если значение этой переменной, элемента массива или
свойства не является числом, оператор сначала пытается преобразовать его
5.4. Операторы равенства 83
в число. Точное поведение этого оператора зависит от его положения по отно
шению к операнду. Если поставить его перед операндом (префиксный опера
тор инкремента), то к операнду прибавляется 1, а результатом является уве
лич