Программирование на JavaScript в примерах и задачах [Алексей Николаевич Васильев] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

Васильев А.Н.

ПРОГРАММИРОВАНИЕ
НА
В ПРИМЕРАХ

Java
Script

И ЗАДАЧАХ

Москва
2017

РОССИЙСКИЙ
КОМПЬЮТЕРНЫЙ
БЕСТСЕЛЛЕР

УДК 004.43
ББК 32.973.26-018.2
В19

В19

Васильев, Алексей Николаевич.
JavaScript в примерах и задачах / Алексей Васильев. – Москва :
Издательство «Э», 2017. – 720 с. – (Российский компьютерный бестселлер).
ISBN 978-5-699-95459-9
Простой и интересный самоучитель по JavaScript, наиболее популярному сегодня
языку программирования во всем мире. Полный спектр сведений о языке JavaScript с
примерами и разбором задач от автора учебников-бестселлеров по языкам программирования Алексея Васильева. С помощью этой книги освоить язык JavaScript сможет
каждый желающий – от новичка до специалиста.
УДК 004.43
ББК 32.973.26-018.2

ISBN 978-5-699-95459-9

© Васильев А.Н., 2017
© Оформление. ООО «Издательство «Э», 2017

ОГЛАВЛЕНИЕ

Вступление. Книга о JavaScript . . . . . . . . . . . . . . . . . .
Сценарии и программы . . . . . . . . . . . . . . . . . . . .
Веб-документы и язык HTML . . . . . . . . . . . . . . . .
Добавление сценария в веб-документ . . . . . . . . . . .
Концепция книги . . . . . . . . . . . . . . . . . . . . . . . .
Тестирование сценариев и программное обеспечение
Обратная связь . . . . . . . . . . . . . . . . . . . . . . . . .
Об авторе . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Благодарности . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

. 7
. 8
11
18
25
28
39
39
40

Глава 1. Знакомство с JavaScript . . . . . . . . . . . . . . . . . .
Отображение текста в рабочем документе . . . . . . . . .
Способы реализации сценария . . . . . . . . . . . . . . . .
Знакомство с переменными . . . . . . . . . . . . . . . . . .
Сценарий с одной переменной . . . . . . . . . . . . . . .
Сценарий с двумя переменными . . . . . . . . . . . . .
Присваивание переменной значений разных типов .
Вычисление выражений . . . . . . . . . . . . . . . . . . .
Основные операторы . . . . . . . . . . . . . . . . . . . . . . .
Арифметические операторы . . . . . . . . . . . . . . . .
Операторы сравнения . . . . . . . . . . . . . . . . . . . .
Логические операторы . . . . . . . . . . . . . . . . . . . .
Побитовые операторы . . . . . . . . . . . . . . . . . . . .
Сокращенные формы оператора присваивания . . .
Тернарный оператор . . . . . . . . . . . . . . . . . . . . .
Преобразование типов . . . . . . . . . . . . . . . . . . . .
Приоритет операторов . . . . . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 2. Управляющие инструкции . . . . . . . . . . . . . . . .
Условный оператор . . . . . . . . . . . . . . . . . . . . . . . .
Общий синтаксис условного оператора . . . . . . . . .
Пример с условным оператором . . . . . . . . . . . . . .
Упрощенная форма условного оператора . . . . . . .
Пример с упрощенной формой условного оператора
Вложенные условные операторы . . . . . . . . . . . . .
Оператор цикла while . . . . . . . . . . . . . . . . . . . . . . .
Синтаксис оператора . . . . . . . . . . . . . . . . . . . . .
Пример использования оператора цикла . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

42
43
46
49
51
53
54
56
58
58
62
63
67
73
74
75
77
79
80
80
80
82
87
88
90
98
98
99

ЧАСТЬ I. ОСНОВЫ JAVASCRIPT

3

Оглавление

Оператор цикла do-while . . . . . . . . . . . . . . . . . . . . .
Синтаксис оператора . . . . . . . . . . . . . . . . . . . . .
Пример с использованием оператора цикла . . . . . .
Оператор цикла for . . . . . . . . . . . . . . . . . . . . . . . .
Синтаксис оператора . . . . . . . . . . . . . . . . . . . . .
Пример использования оператора . . . . . . . . . . . .
Оператор выбора switch . . . . . . . . . . . . . . . . . . . . . .
Синтаксис оператора выбора . . . . . . . . . . . . . . . .
Примеры использования оператора выбора . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 3. Функции . . . . . . . . . . . . . . . . . . . . . . . . . . .
Знакомство с функциями . . . . . . . . . . . . . . . . . . . .
Описание функции . . . . . . . . . . . . . . . . . . . . . .
Примеры объявления функций . . . . . . . . . . . . . .
Локальные и глобальные переменные . . . . . . . . . . . .
Область видимости и локальные переменные
в функции . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обращение к глобальной переменной в функции . .
Создание глобальной переменной в функции . . . .
Глобальная переменная как свойство объекта окна .
Аргументы функции . . . . . . . . . . . . . . . . . . . . . . .
Аргумент как локальная переменная . . . . . . . . . .
Механизм передачи аргументов функции . . . . . . .
Проверка типа аргумента . . . . . . . . . . . . . . . . . .
Количество аргументов функции . . . . . . . . . . . .
Передача функции аргументом . . . . . . . . . . . . . .
Рекурсия . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутренние функции . . . . . . . . . . . . . . . . . . . . . .
Присваивание функций . . . . . . . . . . . . . . . . . . . . .
Анонимные функции . . . . . . . . . . . . . . . . . . . . . .
Функция как результат . . . . . . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

102
102
104
106
106
108
113
113
115
123
125
125
126
127
131

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

131
135
137
139
146
146
149
152
156
160
164
167
174
179
186
193

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

196
196
198
198
203
206
209
211
215
215
217

ЧАСТЬ II. JAVASCRIPT И ООП
Глава 4. Знакомство с объектами и принципы ООП .
Концепция ООП . . . . . . . . . . . . . . . . . . . . . .
Создание объектов . . . . . . . . . . . . . . . . . . . .
Литерал объекта . . . . . . . . . . . . . . . . . . . .
Объект с методом . . . . . . . . . . . . . . . . . . .
Присваивание объектов . . . . . . . . . . . . . . .
Добавление свойств и методов в объект . . . . .
Конструктор объектов . . . . . . . . . . . . . . . .
Утилиты для работы с объектами . . . . . . . . . .
Оператор with . . . . . . . . . . . . . . . . . . . . . .
Оператор for-in . . . . . . . . . . . . . . . . . . . . . .

4

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

Оглавление

Оператор in . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Оператор delete и удаление свойств и методов . . . . .
Прототипы . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Механизм доступа к свойствам
и создание объекта на основе прототипа . . . . . . . .
Получение доступа к прототипу . . . . . . . . . . . . .
Создание объектов без прототипа . . . . . . . . . . . .
Конструкторы и прототипы . . . . . . . . . . . . . . . .
Свойства и методы . . . . . . . . . . . . . . . . . . . . . . . .
Перечисляемые и неперечисляемые свойства . . . .
Свойства с режимом доступа . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 5. Знакомство с массивами . . . . . . . . . . . . . . . . .
Создание массива . . . . . . . . . . . . . . . . . . . . . . . . .
Явное указание элементов массива . . . . . . . . . . .
Добавление элементов в массив . . . . . . . . . . . . . .
Использование объекта-конструктора Array . . . . . .
Операции с массивами . . . . . . . . . . . . . . . . . . . . .
Методы для работы с массивами . . . . . . . . . . . . .
Присваивание и копирование массивов . . . . . . . .
Методы toString() и valueOf() . . . . . . . . . . . . . . . . . .
Двумерные массивы . . . . . . . . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 6. Использование объектов . . . . . . . . . . . . . . . . .
Обработка исключительных ситуаций . . . . . . . . . . .
Инструкция try-catch . . . . . . . . . . . . . . . . . . . . . .
Объект ошибки . . . . . . . . . . . . . . . . . . . . . . . . .
Генерирование ошибок . . . . . . . . . . . . . . . . . . . .
Вложенные try-catch блоки и блок finally . . . . . . . . . .
Создание ошибки пользовательского типа . . . . . .
Объекты и массивы . . . . . . . . . . . . . . . . . . . . . . . .
Объект как ассоциативный массив . . . . . . . . . . . .
Методы toString() и valueOf() для объектов . . . . . . . . .
Массивы и объекты как свойства объекта . . . . . . .
Функция как объект . . . . . . . . . . . . . . . . . . . . . . .
Количество аргументов функции . . . . . . . . . . . .
Функция с произвольным количеством аргументов
Передача контекста функции . . . . . . . . . . . . . . .
Встроенные объекты . . . . . . . . . . . . . . . . . . . . . . .
Объект Math . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Объект Number . . . . . . . . . . . . . . . . . . . . . . . . . .
Объект Boolean . . . . . . . . . . . . . . . . . . . . . . . . . .
Объект String . . . . . . . . . . . . . . . . . . . . . . . . . . .
Объект Date . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . 220
. . . . . . 223
. . . . . . 227
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

228
238
247
250
258
258
269
280
283
283
283
286
288
295
295
311
323
328
333
334
334
335
339
346
351
357
362
362
367
374
383
384
388
391
405
405
407
410
412
417
427

5

Оглавление

ЧАСТЬ III. ИСПОЛЬЗОВАНИЕ JAVASCRIPT
Глава 7. Веб-документы и сценарии . . . . . . . . . . . .
Место и роль сценария в веб-документе . . . . . .
Размещение сценария в документе . . . . . . . .
Обработка событий . . . . . . . . . . . . . . . . . .
Объект окна window . . . . . . . . . . . . . . . . . . . .
Объектные модели . . . . . . . . . . . . . . . . . .
Диалоговые окна . . . . . . . . . . . . . . . . . . . .
Открытие и закрытие окна . . . . . . . . . . . . .
Загрузка документа . . . . . . . . . . . . . . . . . .
Свойства и методы объекта window . . . . . . . . .
Таймеры . . . . . . . . . . . . . . . . . . . . . . . . .
Объект документа document . . . . . . . . . . . . . . .
Свойства и методы объекта document . . . . . . .
Настройки цвета . . . . . . . . . . . . . . . . . . . .
Методы write() и writeln() . . . . . . . . . . . . . . . .
Программное создание документа . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 8. Элементы управления и обработка событий
Элементы управления в веб-документе . . . . . . .
Кнопки и поля ввода . . . . . . . . . . . . . . . . .
Опции, переключатели и списки . . . . . . . . .
Работа с изображениями . . . . . . . . . . . . . . . .
Просмотр изображений . . . . . . . . . . . . . . .
Рисование изображения и текста . . . . . . . . .
Создание изображений в сценарии . . . . . . . .
События . . . . . . . . . . . . . . . . . . . . . . . . . . .
Объект события . . . . . . . . . . . . . . . . . . . .
Диспетчеризация событий . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 9. Различные примеры . . . . . . . . . . . . . . . .
Триадная кривая Коха . . . . . . . . . . . . . . . . . .
Калькулятор . . . . . . . . . . . . . . . . . . . . . . . .
Бегущий текст . . . . . . . . . . . . . . . . . . . . . . .
Игра «Жизнь» . . . . . . . . . . . . . . . . . . . . . . .
Динамические рисунки . . . . . . . . . . . . . . . . .
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Заключение. Немного о веб-программировании . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

430
430
430
438
440
440
442
448
455
461
481
502
502
512
515
518
529
530
530
531
553
574
575
585
595
606
606
620
630
632
632
646
667
674
696
714
715

Вступление
КНИГА О JAVASCRIPT

Видел чудеса техники, но такого…
из к/ф «Иван Васильевич меняет
профессию»

Вниманию читателя предлагается книга о JavaScript. На сегодня
JavaScript является одним из наиболее популярных языков программирования. Причем не просто программирования, а веб-программирования. Вместе с тем язык JavaScript в некоторых аспектах особенный. Поэтому, прежде чем приступить к его изучению, имеет смысл
прокомментировать общее положение дел.
Как правило, JavaScript упоминают как сценарный язык. Другими
словами, обычно на JavaScript создаются сценарии. Сценарий — это
та же программа. Различие между программой и сценарием в том,
под управлением какой среды они выполняются. Обычная программа чаще всего выполняется под управлением операционной системы.
Сценарий выполняется под управлением браузера. Браузер, в свою
очередь, представляет собой специальную программу, с помощью которой просматриваются веб-документы.
i


НА ЗАМЕТКУ
Браузеров существует достаточно много. Наиболее популярными (на момент написания книги) являются Internet Explorer, Mozilla
Firefox, Opera и Google Chrome. Среди браузеров существует достаточно сильная конкуренция. Поэтому лидеры списка время от
времени меняются, появляются новые браузеры, а уже существующие уходят со сцены. В общем, вопрос выбора браузера непростой
и достаточно динамичный в плане предпочтений пользователей.

Указанное различие между программой и сценарием во многом техническое. Тем не менее оно имеет последствия. Собственно, об этих
последствиях и поговорим далее.

7

Вступление
i


НА ЗАМЕТКУ
То, что описывается далее, имеет некоторый привкус «технических
подробностей». Эти подробности важные, но не критичные. Поэтому, даже если что-то покажется малопонятным, отчаиваться не стоит. Данный материал скорее для тех, кому интересно знать больше,
чем необходимо.

Сценарии и программы
Меня не проведешь. Приемы сыщиков
я вижу на пять футов вглубь.
из к/ф «Приключения Шерлока Холмса
и доктора Ватсона»

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


Сначала набирается программный код. Это как раз та последовательность команд, которая должна быть выполнена. Программный
код набирается в соответствии с правилами языка программирования, на котором пишется программа. Языков программирования очень много — например, С++, C#, Java или Python (этот перечень языков программирования далеко не полный). У каждого из
них свои правила составления кодов.



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



Если используется программа-компилятор, то исходный программный код компилируется и в результате получается набор

8

Книга о JavaScript

команд, готовых к выполнению операционной системой. Фактически при компиляции на основе набора инструкций, написанных на каком-то определенном языке программирования, создается набор команд машинного или квазимашинного уровня. Эти
команды и выполняются при запуске программы на выполнение.


Принципиальное отличие интерпретатора от компилятора состоит в том, что интерпретатор выполняет трансляцию исходного
кода в исполняемый код, так сказать, в процессе выполнения, инструкция за инструкцией. Если компилятором конвертируется вся
программа, а затем начинается ее выполнение, то интерпретатор
конвертирует и исполняет сначала одну инструкцию, затем вторую, третью и так далее. То есть конвертирование программного
кода происходит по мере необходимости.

Описанная последовательность действий достаточно условная, но
вместе с тем показательная. Она дает ответ на вопрос, что же нам на
самом деле нужно, чтобы написать программу на том или ином языке
программирования. Во всяком случае, становится понятно, что без
компилятора (или интерпретатора — все зависит от конкретного языка программирования) не обойтись.
В общем и целом получается так: имеется некоторый язык программирования, на котором мы собираемся писать программные коды. Язык
программирования — это в широком смысле набор правил. Поэтому
формально программный код можно написать, имея под рукой лишь
обычный текстовый редактор. Но затем нам понадобится программа,
способная не просто «понять» этот код, но и преобразовать его в форму, приемлемую для восприятия операционной системой. Здесь на
сцену выходит компилятор или интерпретатор. Компиляторы и интерпретаторы предназначены для компилирования и интерпретации
программ. Для этого они создаются фирмами-разработчиками. Это их
основная задача. Что здесь важно? Важно то, что написание кода и его
компиляция/интерпретация разнесены во времени и в пространстве.
Другими словами, создавая программный код, мы можем особо не заботиться, как затем его компилировать или интерпретировать.
Еще один важный момент связан непосредственно с компиляторами
и интерпретаторами. Это программы, которые устанавливаются на
компьютер. Проще говоря, если мы хотим писать (и запускать) программы на языке С++, то нам необходимо будет установить компилятор для данного языка (и для данной операционной системы).

9

Вступление



Д Е ТА Л И
Откровенно говоря, очень редко компилятор устанавливается отдельно. Обычно устанавливается интегрированная среда разработки
(обычно упоминается как IDE, что является сокращением от английского Integrated Development Environment), в состав которой входит не
только компилятор, но и редактор кодов, встроенный отладчик и многие другие полезные утилиты. Со средой разработки написание программы превращается в процесс комфортный и где-то даже приятный. Но в данном случае это не важно. Даже если используется среда
разработки, компилятор все равно устанавливается и используется.

Все сказанное, напомним, относится к программам. Теперь вспомним
о сценариях. Что принципиально меняется при написании сценариев? На самом деле, мало что. Как и в случае с программой, при написании сценария в соответствии с правилами языка (в данном случае
имеется в виду язык JavaScript) набирается программный код. Код
будет выполняться при выполнении сценария. Нас интересует создание сценариев на языке JavaScript. Сценарии на языке JavaScript интерпретируются. Но интерпретация выполняется не в явном виде,
а, как отмечалось, средствами браузера. Беды в том нет, но есть определенное неудобство, связанное с необходимостью «инкапсуляции»
сценария в веб-документ. Дело в том, что при интерпретации программы, с одной стороны, имеется программный код, а с другой — интерпретатор. Программный код интерпретируется интерпретатором.
А вот если речь идет о сценарии, то этот сценарий должен быть включен в веб-документ. При открытии веб-документа браузером сценарий выполняется встроенными средствами браузера.
i


НА ЗАМЕТКУ
Здесь мы имеем в виду разработку с помощью JavaScript клиентских веб-приложений. То есть речь идет о приложениях, которые
выполняются на компьютере клиента. С серверными приложениями ситуация несколько иная.

Фактически интерпретатор для сценария «спрятан» в браузере. Выполнить единственно сценарий при таком подходе проблематично.
Сценарий приходится выполнять в контексте работы с веб-документом. Само по себе это не хорошо и не плохо. Но с методологической
точки зрения при изучении языка JavaScript ситуация не самая луч-

10

Книга о JavaScript

шая, поскольку в «компании» с JavaScript-кодом сценария оказывается еще и код веб-документа. Причина в том, что основное назначение
браузеров — работа с веб-документами. Выполнение сценариев браузерами — в известном смысле дополнительная функция.
i


НА ЗАМЕТКУ
Великий немецкий философ Иммануил Кант (1724–1804) утверждал, что к человеку должно относиться только как к цели, но не как
к средству (категорический императив Канта). В рамках данной философской парадигмы, пожалуй, можно утверждать, что, тогда как
программа для интерпретатора является целью, сценарий для браузера скорее является средством.



Д Е ТА Л И
Ситуация со сценариями (в плане выполнения кода под управлением не операционной системы, а другой программы — в данном случае браузера) не является уникальной. Например, существуют макросы. Скажем, при работе с приложением Excel из пакета Microsoft
Office на языке VBA можно создавать программные коды, выполняемые под управлением приложения Excel. Это и есть макросы.

Здесь имеет смысл отметить, что при создании веб-документов используется специальный язык, который называется языком гипертекстовой разметки, или HTML (сокращение от английского
HyperText Markup Language). Язык HTML не является предметом книги, но не упомянуть его совсем не удастся. Покоряясь неизбежному,
предадимся рассуждениям о языке HTML.

Веб-документы и язык HTML
Он начинает новую жизнь, дайте ему возможность вспомнить все лучшее.
из к/ф «Покровские ворота»

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

11

Вступление

му на браузер в некотором приближении можно смотреть как на программу для просмотра текстов с гипертекстовой разметкой.



Д Е ТА Л И
Откровенно говоря, ситуация не такая простая, как может показаться на первый взгляд. Один и тот же документ с гипертекстовой
разметкой в разных браузерах может отображаться по-разному.
Более того, есть дескрипторы, которые «понимаются» одними браузерами и совершенно «не признаются» другими. Подход, базирующийся на создании HTML-документов, ориентированных на работу с одним определенным типом браузера, не проходит, поскольку
концептуально веб-документы предназначены для использования
в Интернете, что автоматически сводит на нет попытку ограничить
пользователей документа браузером одного типа. То есть даже на
уровне стандартного (не использующего сценарии) HTML-документа возникают неожиданные моменты. С другой стороны, для каждой проблемы обычно имеется более или менее удачное решение.

Создать HTML-документ достаточно просто. Из всех возможных
простых способов мы здесь рассмотрим самый простой и наименее
ресурсозатратный. Для этого понадобится простенький текстовый
редактор вроде Notepad (Блокнот) и, естественно, браузер.
i


НА ЗАМЕТКУ
Браузер в принципе подойдет любой, но мы будем ориентироваться
на группу лидеров: Internet Explorer, Mozilla Firefox, Google Chrome
и Opera.

Сначала в текстовом редакторе необходимо набрать код документа.
Под словом «код» имеется в виду HTML-код. Чтобы не быть голословными, поступим следующим образом: создадим пустой текстовый документ. Этот документ необходимо открыть и в окне текстового редактора ввести код, представленный в листинге В.1 (назначение
инструкций кода поясняется позже).

 Листинг В.1. Код гипертекстовой разметки документа




12

Книга о JavaScript


Îìàð Õàéÿì. Ðóáàè



Ðóáàè
×òîá ìóäðî æèçíü ïðîæèòü, çíàòü íàäîáíî íåìàëî,
Äâà âàæíûõ ïðàâèëà çàïîìíè äëÿ íà÷àëà:
Òû ëó÷øå ãîëîäàé, ÷åì ÷òî ïîïàëî åñòü,
È ëó÷øå áóäü îäèí, ÷åì âìåñòå ñ êåì ïîïàëî.

Îìàð Õàéÿì


Далее необходимо сохранить изменения в документе, закрыть его
и изменить расширение txt на html (или htm). Собственно, все: мы создали HTML-документ. Этот документ можно открыть с помощью
браузера. Также можно его открыть с помощью текстового редактора. В последнем случае увидим содержимое документа, то есть его
HTML-код. На рис. В.1 показан документ с HTML-кодом из листинга В.1, открытый в текстовом редакторе.

Рис. В.1. Документ с HTML-кодом открыт в текстовом редакторе

13

Вступление

Если мы откроем документ с помощью браузера, получим несколько
иной результат. На рис. В.2 показано, как будет выглядеть созданный
нами документ в окне браузера Mozilla Firefox.

Рис. В.2. Документ открыт в окне браузера Mozilla Firefox

Для сравнения на рис. В.3 этот же документ открыт браузером Internet
Explorer.

Рис. В.3. Документ открыт в окне браузера Internet Explorer

Также желающие могут взглянуть на рис. В.4 и рис. В.5, на которых
показан документ с HTML-кодом, открытый соответственно с помощью браузеров Google Chrome и Opera (разработчик — компания
Opera Software).

14

Книга о JavaScript

Рис. В.4. Документ открыт в окне браузера Google Chrome

Рис. В.5. Документ открыт в окне браузера Opera

В принципе способ отображения документа разными браузерами
идентичен. Так и должно быть. Теперь кратко обсудим назначение
инструкций, использованных в HTML-коде документа.
i


НА ЗАМЕТКУ
В книге информация относительно HTML-кодировки обычно дается
непосредственно при объяснении примеров или приводится в специальных врезках.

Сначала сделаем несколько общих замечаний относительно HTMLкодов. Во-первых, текст, не помеченный дескрипторами, отображается как обычно, то есть как текст. Во-вторых, дескрипторы бывают
парными (таких большинство) и непарными. Дескриптор — это определенное ключевое слово, заключенное в угловые скобки. Если де-

15

Вступление

скриптор парный, то второй (закрывающий) дескриптор отличается
от первого (открывающего) дескриптора наличием обратной косой
черты / перед ключевым словом в угловых скобках.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Дескриптор имеет вид . Это открывающий дескриптор.
Закрывающий дескриптор (если такой имеется) будет выглядеть
как . Например, пара дескрипторов и определяют
фрагмент текста, который в браузере будет отображаться жирным
шрифтом. Здесь речь идет о парном дескрипторе. Примеры непарных дескрипторов:
• дескриптор является инструкцией разрыва строки (в месте
размещения данного дескриптора выполняется разрыв текстовой строки и осуществляется переход к новой строке для отображения текста);
• дескриптор является инструкцией отображения горизонтальной линии (по умолчанию на ширину окна браузера).

В-третьих, у HTML-документа должна быть определенная структура, которая формируется, как несложно догадаться, с помощью дескрипторов.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Откровенно говоря, браузеры очень демократичны в отношении
правил оформления HTML-документа. Даже если взять самый
обычный текстовый файл, добавить туда некоторые дескрипторы
(например, выделить часть текста дескрипторами и ), сохранить файл с расширением html и затем открыть его в браузере, браузер отобразит документ с учетом наличия в нем дескрипторов.
Вместе с тем существуют определенные правила создания документов с гипертекстовой разметкой, и их стоит придерживаться.

Использованный нами код содержит как парные, так и непарные дескрипторы. Начинается он с дескриптора , являющегося стандартным началом HTML-документа, указывающим браузеру, с какого типа данными предстоит иметь дело. Весь фактический
HTML-код размещается между дескрипторами (открывает код
документа) и (закрывает код документа). Между дескрипторами и размещается несколько блоков кода (в данном случае блоков два). Первый блок выделен дескрипторами и .

16

Книга о JavaScript

Это заглавный блок документа. В данном блоке размещается важная
информация о документе. В нашем примере в заглавном блоке между
дескрипторами и указан текст Îìàð Õàéÿì. Ðóáàè, который станет рабочим названием документа (не путать с названием файла!).



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Рабочее название документа, указанное между дескрипторами
и , в рабочей области документа не отображается. Оно
отображается на вкладке браузера, когда в нем открыт документ.

Второй блок выделен дескрипторами и . Этот блок — основное тело документа. В данном блоке фактически размещается код
и текст, предназначенный для отображения в рабочей области документа. Что мы находим внутри блока? Внутри мы находим инструкцию Ðóáàè. В соответствии с ней текст Ðóáàè будет выделен как
заголовок третьего уровня. О том, что речь идет о заголовке именно
третьего уровня, свидетельствуют дескрипторы и .



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Несложно догадаться, что если существует заголовок третьего
уровня, то есть заголовок второго уровня (выделяется дескрипторами и ) и заголовок первого уровня (выделяется дескрипторами и ). Как выделять заголовок, определяется браузером. Обычно речь идет о применении увеличенного размера
шрифта и выделении жирным стилем.

Далее следует текст, в котором встречаются непарный дескриптор
. В месте размещения данного дескриптора выполняется перенос строки. Непарный дескриптор используется для отображения в документе горизонтальной линии. При этом переход к новой
строке после данной линии выполняется автоматически. Наконец,
дескрипторы и используются для применения к тексту жирного шрифта.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
В написании дескрипторов можно использовать как большие, так
и маленькие буквы. Например, вместо кода можем использовать
код . Данное замечание относится и к прочим дескрипторам.

17

Вступление

Добавление сценария в веб-документ
— Что за вздор. Как вам это в голову взбрело?
— Да не взбрело бы, но факты, как говорится,
упрямая вещь.
из к/ф «Чародеи»

Веб-документ может содержать не только дескрипторы, определяющие
внешний вид документа при отображении его в браузере, но и особый
программный код — сценарий. Сценарий в веб-документе помещается
в специальный блок. Блок выделяется дескрипторами и .
Блоков со сценариями в веб-документе может быть несколько, и находиться они могут в разных местах документа. Например, блок со сценарием разрешается размещать в заглавном блоке документа (выделен
дескрипторами и ). А можно блок со сценарием разместить
в блоке основного тела документа (выделяется дескрипторами
и ). Именно так мы и будем поступать в дальнейшем — код сценариев будет содержаться внутри -блока.



Д Е ТА Л И
В целом место размещения блока со сценарием имеет значение:
сценарий выполняется по мере загрузки документа. Если сценарий
находится в заглавном блоке веб-документа, то его выполнение начнется практически сразу при загрузке документа в браузер. Если
сценарий находится в блоке основного тела документа, то выполняется он по мере загрузки блока с кодом. Но нас пока что такие
детали интересовать не будут.

Прежде чем рассмотреть код веб-документа с инкапсулированным
в него сценарием (очень простым), отметим еще одно немаловажное обстоятельство. Дело в том, что при добавлении блока сценария
в веб-документ желательно указать, на каком языке программирования написан сценарий. В общем-то такая ситуация стандартна для
HTML-документа: во многих случаях дескрипторы блоков содержат
некоторые дополнительные настройки, влияющие на способ отображения того или иного элемента или фрагмента текста. Дополнительные настройки выполняются присваиванием значений параметрам
или атрибутам. Значения атрибутам присваиваются внутри открывающего дескриптора (внутри угловых скобок после имени деструктора). Вся конструкция имеет вид . Здесь äå-

18

Книга о JavaScript

ñêðèïòîð обозначает имя дескриптора, àòðèáóò обозначает имя атрибута,
а çíà÷åíèå (обычно заключается в двойные кавычки), соответственно,
является значением àòðèáóòà.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Например, для создания гипертекстовой ссылки в документе используется парный дескриптор (закрывающий дескриптор ).
У дескриптора есть атрибут href. Значение данного атрибута определяет адрес страницы для выполнения перехода при щелчке на
гиперссылке. Более конкретно, инструкция Ñàéò àâòîðà êíèãè добавляет в документ гиперссылку с текстом
Ñàéò àâòîðà êíèãè, щелчок на которой приводит к переходу по адресу www.
vasilev.kiev.ua.

Для -дескриптора желательно указать значение "text/javascript"
для атрибута type. Фактически данное значение указывает, что сценарий написан на языке JavaScript. Шаблон включения блока со сценарием в веб-документ следующий:

// êîä ñöåíàðèÿ

Вся эта конструкция будет размещаться внутри -блока, а непосредственно код сценария находится там, где размещен комментарий
// êîä ñöåíàðèÿ.
i


НА ЗАМЕТКУ
Две обратных косых черты // в программном коде сценария являются началом комментария. Все, что находится справа от двойной
косой черты, при выполнении сценария игнорируется.
Также стоит отметить, что, если не указать инструкцию type="text/
javascript" в дескрипторе , сценарий все равно будет выполняться.
Дело в том, что по умолчанию, если явно не указан язык, на котором
написан сценарий, предполагается язык JavaScript. Но все же лучше
не полагаться на случай и указывать язык сценария в явном виде.

Как иллюстрацию к использованию сценариев рассмотрим небольшой пример, в котором вывод текста в диалоговое окно выполняется с помощью сценария, добавленного в HTML-код веб-документа.

19

Вступление

Рассмотрим листинг В.2, в котором представлен соответствующий
HTML-код, в том числе внутри этого кода имеется небольшой сценарий.

 Листинг В.2. Документ со сценарием




Ñöåíàðèé JavaScript



Äîêóìåíò ñî ñöåíàðèåì JavaScript
Ïðè çàãðóçêå ýòîãî äîêóìåíòà âûïîëíÿåòñÿ ñöåíàðèé…
Ðåçóëüòàò âûïîëíåíèÿ ñöåíàðèÿ:



// Êîìàíäà âûâîäà òåêñòà â îêíî äîêóìåíòà:
document.write("Ñöåíàðèé íà ÿçûêå JavaScript")



Âûïîëíåíèå ñöåíàðèÿ çàâåðøåíî.


На рис. В.6 показано окно текстового редактора, в котором открыт
соответствующий веб-документ, так что мы можем видеть, как в реальности выглядит документ с HTML-кодом и вставленным в него
блоком сценария.
Прежде чем посмотреть, как будет выглядеть этот же самый документ
в окне браузера, кратко проанализируем код документа — нас интересует прежде всего код сценария.

20

Книга о JavaScript

Рис. В.6. Веб-документ со сценарием открыт в текстовом редакторе



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
На всякий случай напомним назначение основных дескрипторов
в HTML-коде. Итак, документ стандартно начинается инструкцией
, идентифицирующей принадлежность документа к семейству HTML. Весь код документа помещен между дескрипторами и . Внутри -блока между дескрипторами
и указывается рабочее название документа (отображается
во вкладке браузера при отображении документа). То, что отображается в области документа, содержится в -блоке. В частности, там имеется заголовок 3-го уровня (между дескрипторами
и ), обычный текст и текст, выделенный жирным шрифтом. Текст
для выделения жирным шрифтом заключается между дескрипторами и . Для перехода к новой строке использована инструкция
. Инструкцией отображается горизонтальная линия (на ширину всей рабочей области окна браузера).
Программный код сценария размещен между дескрипторами
и . Открывающий дескриптор содержит инструкцию type="text/
javascript", определяющую язык программирования, на котором написан сценарий.
Также в HTML-коде представлены комментарии, которые выделяют
блок со сценарием. В HTML комментарий начинается инструкцией
. Все, что находится между инструкциями , при открытии веб-документа в окне браузера не
отображается.

21

Вступление

Внутри -блока, если не считать комментарий (строка, которая
начинается с двойной косой черты //), всего одна команда document.
write("Ñöåíàðèé íà ÿçûêå JavaScript"). Как несложно догадаться, данной командой в окно браузера выводится текст, указанный в двойных кавычках. Читателю, не знакомому с принципами объектно-ориентированного программирования, команда может показаться странной. Хотя
на самом деле команда самая обычная: из объекта document вызывается
метод write().
i


НА ЗАМЕТКУ
Далее приводится небольшое формальное объяснение назначения команды и смысла входящих в нее идентификаторов. Более
детально объекты и методы разбираются в основной части книги.
Здесь же даются лишь минимальные сведения, необходимые для
общего понимания принципов выполнения программного кода.

Объект document отождествляется с веб-документом, открытым в окне
браузера (документ, в котором размещен код сценария). С данным
объектом можно выполнять некоторые действия. В частности, среди таких действий есть операция «отобразить в документе текст».
Реализуется она методом write(). Чтобы отобразить в документе текст,
необходимо вызвать метод write(), передав ему аргументом текст, предназначенный для отображения. Но метод существует не сам по себе,
а только в привязке к объекту, с которым или по отношению к которому выполняются действия. Метод вызывается из объекта. Последнее
означает, что при вызове метода должен быть явно указан и объект,
из которого метод вызывается. Объект указывается первым, после
чего через точку следует имя метода (используется так называемый
точечный синтаксис). Во всем остальном метод похож на обычную
функцию или процедуру: именованный блок программного кода, который вызывается через имя.
Здесь речь идет об объекте document и методе write(). Команда вызова
метода write() из объекта document выглядит как document.write(). Только
в круглых скобках нужно указать текст для отображения в документе.
В результате получается команда document.write("Ñöåíàðèé íà ÿçûêå JavaScript").
Помимо нее, в программном коде сценария имеется еще комментарий — он находится одной строкой выше команды вывода текста в документ и начинается с двойной косой черты. Назначение комментария — в пояснении смысла инструкций программного кода.

22

Книга о JavaScript
i


НА ЗАМЕТКУ
Обратите внимание, что комментарии в HTML-коде и в коде сценария выполняются по-разному. Комментарий для HTML-кода заключается между инструкциями . Такие комментарии используются в HTML-коде, но не используются внутри программного кода
сценария.
В сценарии комментарий начинается с двойной косой черты //. Вне
сценария такие комментарии неприменимы.

Если загрузить веб-документ в окно браузера, получим результат,
как, например, на рис. В.7. Там показан документ, открытый в окне
браузера Internet Explorer.

Рис. В.7. Документ со сценарием открыт в браузере Internet Explorer

Аналогичным образом все выглядит и при использовании других браузеров. Для сравнения на рис. В8 показано окно браузера Mozilla Firefox
с открытым в нем документом со сценарием. Окно браузера Google
Chrome с открытым документом показано на рис. В.9. Как будет выглядеть документ в окне браузера Opera, показано на рис. В.10.
Во всех перечисленных случаях результат примерно одинаков, с поправкой на незначительные декоративные детали. Важно также подчеркнуть, что результат выполнения сценария каждый раз отображается
между двумя горизонтальными линиями. Если из всего текста в рабочей области браузера исключить содержимое, обязанное своим существованием HTML-коду документа (за исключением кода сценария), то
результат выполнения исключительно сценария будет следующим.

23

Вступление

Рис. В.8. Документ со сценарием открыт в браузере Mozilla Firefox

Рис. В.9. Документ со сценарием открыт в браузере Google Chrome

Рис. В.10. Документ со сценарием открыт в браузере Opera

24

Книга о JavaScript

 Результат выполнения сценария (из листинга В.2)
Ñöåíàðèé íà ÿçûêå JavaScript
Еще раз подчеркнем, что здесь речь идет о той части содержимого вебдокумента, которая отображается при выполнении сценария.

Концепция книги
Ален ноби, ностра алис! Что означает — ежели один человек построил, другой завсегда
разобрать может.
из к/ф «Формула любви»

Как отмечалось выше, эта книга — о языке программирования
JavaScript. Но здесь имеется некоторая проблема методологического характера. Не будет преувеличением сказать, что язык JavaScript
достаточно специфичный — не в плане синтаксиса (хотя во многом
он оригинальный), но в плане прикладного применения языка. Как
отмечалось ранее, язык используется для написания сценариев, которые выполняются под управлением браузера.
i


НА ЗАМЕТКУ
Современные тенденции таковы, что область применимости языка
JavaScript постоянно расширяется. На данный момент написание
браузерных сценариев — далеко не единственное поле применения языка JavaScript. По-видимому, такой тренд сохранится и в будущем. Мы же ограничимся изучением языка JavaScript как такового, а также познакомимся с практической стороной применения
JavaScript для написания сценариев.

Отделить собственно язык JavaScript от вопросов его прикладного
применения не очень просто. Другимисловами, обсуждая JavaScript,
приходится постоянно делать рефрен в сторону прикладного аспекта
сценарного программирования. Реализация программного взаимодействия с браузером подразумевает владение основами объектной
модели документа. То есть речь фактически идет об объектно-ориентированном программировании. Причем в JavaScript объектно-ориентированный подход реализован специфически.

25

Вступление
i


НА ЗАМЕТКУ
Будет много сюрпризов для тех, кто знаком с языками программирования С++, C# или Java.

И хотя ситуацию с реализацией объектно-ориентированной парадигмы в языке JavaScript нельзя назвать сложной, простой ее тоже не назовешь. В результате получается, что для иллюстрации работы сценариев JavaScript нужно использовать браузер, а использование браузера
подразумевает неплохое (на уровне объектной модели документа) владение основами языка. Чтобы разорвать этот замкнутый круг, мы поступим просто и прагматично. Сначала сведем «взаимодействие» сценария с браузером к минимуму и сконцентрируемся исключительно
на особенностях языка программирования JavaScript. После того как
вершины JavaScript будут «взяты», мы переключимся на вопросы прикладного использования языка JavaScript для написания сценариев.
Структура книги соответствует подходу, задекларированному выше.
Книга состоит из трех частей. В первой части обсуждаются основные
синтаксические конструкции языка JavaScript. Во второй части описываются методы объектно-ориентированного программирования
в JavaScript. Третья часть посвящена написанию сценариев для работы с веб-документами.
i


НА ЗАМЕТКУ
В третьей части книги рассматривается объектная модель документа — набор средств языка JavaScript, предназначенных для работы с веб-документом.

В первой и второй частях книги браузер будет использоваться как
средство вывода информации сценарием. Что касается задач и примеров, рассматриваемых в первых двух частях книги, то они имеют
достаточно общий характер (встречаются в том числе и математические задачи).
i


НА ЗАМЕТКУ
Сказанное не означает, что читателю необходимо иметь специальную математическую подготовку. Во-первых, примеры рассматриваются простые. А во-вторых, в книге приводится полная ин-

26

Книга о JavaScript

формация, необходимая для понимания сути задачи и методов ее
решения.

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



Д Е ТА Л И
В прикладной плоскости проблема совместного использования
языка JavaScript и браузера связана с тем, что в веб-документе присутствует как HTML-код, так и непосредственно код сценария (даже
если он включен в веб-документ не в явном виде, а через файл сценария). Хотя в принципе HTML-код и JavaScript-код легко различить,
все равно остается некоторая эстетическая незавершенность, особенно если веб-документ большой и нетривиальный. Ведь как сценарий, так и HTML-код влияют на вид и функциональность веб-документа. Отделить последствия выполнения сценария от результатов
настроек посредством HTML-кода бывает непросто.

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



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Для лучшего восприятия информация относительно HTML-кодов
в основной части книги выносится в специальные блоки.

В первой и второй частях книги анализируются в основном сценарии
на языке JavaScript и, соответственно, интерес представляет резуль-

27

Вступление

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

Тестирование сценариев и программное
обеспечение
— Вот по этому поводу первый тост.
— Сейчас запишу.
— Потом запишешь.
из к/ф «Кавказская пленница»

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

28

Книга о JavaScript

сделать самому вручную. Помимо этого, настройка среды разработки
может оказаться задачей не самой простой. Поэтому далее мы опишем способ создания и тестирования сценариев, который не подразумевает использование какого-либо специализированного программного обеспечения. Нам понадобится только браузер и текстовый
редактор.
i


НА ЗАМЕТКУ
В принципе подойдет любой браузер, поддерживающий язык
JavaScript. Все наиболее популярные браузеры язык JavaScript
поддерживают.

Для большей конкретики процесс работы со сценарием проиллюстрируем на примере браузера Mozilla Firefox и текстового редактора
Notepad. Браузер и редактор могут быть другими, но принципы работы с ними те же.



Д Е ТА Л И
Выполнение сценариев считается занятием потенциально опасным. Поэтому в настройках браузеров имеются опции, позволяющие заблокировать выполнение сценариев. Для работы со сценариями JavaScript в браузере должен быть установлен режим,
разрешающий выполнение сценариев. Более детальную информацию о настройках браузера обычно можно найти в его справочной системе.

Идея состоит в том, чтобы открыть один и тот же документ одновременно в браузере и текстовом редакторе. Далее схема действий
такая.


Вносим изменения в код в окне текстового редактора.



Сохраняем внесенные изменения в текстовом редакторе.



Анализируем изменения в окне браузера. Для этого в браузере
щелкаем по кнопке Reload (Обновить текущую страницу), выполняя тем самым перезагрузку открытого документа.

На рис. В.11 показано окно браузера Mozilla Firefox с открытым пустым документом, а на фоне окна браузера этот же документ открыт
в текстовом редакторе Notepad.

29

Вступление

Рис. В.11. На фоне окна браузера Mozilla Firefox открыто окно текстового
редактора с пустым веб-документом



Д Е ТА Л И
Предварительно следует создать новый текстовый файл myscript.txt,
заменив в нем расширение с .txt на .html. В итоге получаем пустой (не
содержит ничего) файл myscript.html. Этот файл открываем браузером,
и этот же файл открываем в текстовом редакторе (при этом файл
остается открытым и в браузере).

На следующем этапе вводим в текстовый файл код, представленный
в листинге В.3.

 Листинг В.3. Код для веб-документа



Ôàéë myscript.html


Òåñòèðóåì ñöåíàðèé



30

Книга о JavaScript

После того как код введен в окне текстового редактора, изменения
следует сохранить (в меню Файл текстового редактора выбирается
команда Сохранить). Процесс сохранения введенного в текстовом редакторе кода показан на рис. В.12.
При этом в браузере изменения в документ в силу еще не вступили.
Чтобы увидеть эффект от введения в документ кода, в окне браузера
щелкаем по пиктограмме обновления открытого документа, как показано на рис. В.13.

Рис. В.12. Сохранение в текстовом редакторе введенного кода

Рис. В.13. Обновление открытого документа после внесения в него кода

31

Вступление
i


НА ЗАМЕТКУ
Для обновления документа в окне можно щелкнуть правой кнопкой
мыши на корешке вкладки документа и в раскрывшемся контекстном меню выбрать команду Обновить вкладку.

Изменения в браузере вступят в силу. Результат показан на рис. В.14.

Рис. В.14. Внесенные в документ изменения вступили в силу в окне браузера

Если в последующем нам понадобится отредактировать документ,
повторяем описанные выше действия. Например, мы хотим добавить
в веб-документ сценарий. Для этого в окне текстового редактора добавляем несколько строк кода. Весь новый, измененный код представлен в листинге В.4.

 Листинг В.4. Измененный код для веб-документа



Ôàéë myscript.html



32

Книга о JavaScript

Òåñòèðóåì ñöåíàðèé

document.write("Èçó÷àåì JavaScript")



Изменения в коде документа незначительные.
i


НА ЗАМЕТКУ
По сравнению с листингом В.3 в листинге В.4 появился -блок
с инструкцией document.write("Èçó÷àåì JavaScript"). Как следствие в области
документа выводится текстовое сообщение Èçó÷àåì JavaScript.

Вносим изменения в текстовый документ и сохраняем файл в окне
текстового редактора (рис. В.15).

Рис. В.15. После внесения изменений в программный код выполняем
сохранение документа

Далее обновляем содержимое окна браузера, как показано на
рис. В.16.

33

Вступление

Рис. В.16. Обновление содержимого окна браузера

Результат представлен на рис. В.17.

Рис. В.17. Документ в окне браузера после внесения изменений и обновления
страницы

В принципе данный подход вполне приемлем для создания и тестирования программных кодов. Вместе с тем у него есть существенный
недостаток. Дело в том, что набор и редактирование кода выполня-

34

Книга о JavaScript

ются в текстовом редакторе. Это означает, что ни «подсветки» кода,
ни автоматической проверки его на корректность нет. Как нет и автоматического заполнения ввода (когда при вводе команды выпадает
список подстановки или появляется подсказка по вводимой инструкции). Ничего особо страшного здесь нет, но и приятного тоже мало.
i


НА ЗАМЕТКУ
Желающие могут поискать более «изысканный» редактор для набора и работы со сценариями JavaScript и HTML-кодами. Благо
недостатка в них нет. Правда, многое зависит от используемой
операционной системы, да и программные продукты изменяются
достаточно интенсивно. Так что данная почетная миссия всецело
перекладывается на плечи читателя — здесь вопрос с редакторами
обсуждать не будем.

В некоторых браузерах имеются довольно неплохие встроенные
средства разработки сценариев. Скажем, в браузере Mozilla Firefox
в меню Инструменты есть подменю Веб-разработка. Среди команд
данного подменю можно видеть (рис. В.18) команду Простой редактор JavaScript.

Рис. В.18. Отображение встроенного редактора кодов JavaScript

После выбора указанной команды открывается окно встроенного редактора кодов JavaScript, представленное на рис. В.19.

35

Вступление

Рис. В.19. Окно встроенного редактора кодов JavaScript
i


НА ЗАМЕТКУ
Мы уже знаем, что комментарий в JavaScript начинается с двойной
косой черты //. Это однострочный комментарий. Еще есть многострочные комментарии. Многострочный комментарий начинается
с символов /* и заканчивается символами */. Все, что находится
между символами /* и */, является комментарием и при выполнении
кода игнорируется.
При отображении окна редактора JavaScript в нем появляется
текст, который, как несложно заметить, начинается символами /*
в первой строке и завершается символами */ в последней строке
(см. рис. В.19). Таким образом, текст, отображаемый в окне редактора кодов, является комментарием JavaScript.

В окно редактора кодов можно ввести программный код сценария
и запустить его на выполнение, щелкнув по пиктограмме Запустить,
как показано на рис. В.20.
В данном случае код сценария состоит всего из одной команды
alert("Âàñ ïðèâåòñòâóåò Firefox!"). При выполнении команды отображается
диалоговое окно с текстовым сообщением Âàñ ïðèâåòñòâóåò Firefox! и кнопкой ОК. Окно показано на рис. В.21.
Учитывая, что в JavaScript существует возможность отобразить
диалоговое окно с полем ввода, в сценарии можно организовать
процесс ввода и вывода информации через диалоговые окна. Одна-

36

Книга о JavaScript

ко в этом случае браузер все равно должен быть запущен, да и диалоговые окна, откровенно говоря, не очень удобные. Поэтому мы
предпочтем данному подходу метод вставки сценария в HTML-код
веб-документа.

Рис. В.20. Программный код JavaScript перед запуском в окне редактора кодов

Рис. В.21. В окне браузера появляется диалоговое окно с приветствием

Сценарии можно сохранять в отдельных файлах с расширением .js.

37

Вступление
i


НА ЗАМЕТКУ
Далее подразумевается, что используется операционная система
Windows.

Например, создаем текстовый файл с названием ShowWindow.txt, вводим
туда команду WScript.echo("Èçó÷àåì JavaScript!"), сохраняем файл, закрываем его
и меняем расширение на .js (получается файл ShowWindow.js). Если попытаться открыть этот файл, появится диалоговое окно, как на рис. В.22.

Рис. В.22. Диалоговое окно отображается при открытии файла со сценарием

В данном случае окно браузера не отображается — только диалоговое
окно.



Д Е ТА Л И
Если быть откровенным, то единственная команда сценария WScript.
echo("Èçó÷àåì JavaScript!") не совсем относится к JavaScript. Здесь используется библиотека MSDN, в которой описан объект WScript,
а у этого объекта есть метод echo(), позволяющий отображать окно
с сообщением. Весь код выполняется под управлением специального компонента Windows, который называется Windows Script Host
и предназначен для выполнения сценариев на таких языках, как,
например, JavaScript или VBScript. Сценарий в файле с расширением .js выполняется этим компонентом. То есть в данном случае речь
идет о «специфическом» сценарии, «понятном» только для операционной системы Windows. И хотя теоретически мы можем добавить в сценарий совершенно корректные с точки зрения JavaScript
команды, а объект WScript использовать только для вывода информации, это все равно будет Windows-ориентированное программирование. В принципе допустимо, но не универсально.

Вывод из всего сказанного следует простой: при работе с JavaScript
существует достаточно широкое окно возможностей. Читатель при

38

Книга о JavaScript

желании легко (хочется в это верить) найдет для себя приемлемый
путь для реализации сценариев на языке JavaScript. Мы же пойдем,
может быть, не самым эффективным, зато самым надежным путем:
будем вставлять блоки со сценариями в HTML-коды и тестировать
результат в окне браузера.
i


НА ЗАМЕТКУ
По умолчанию (если явно не указан тип браузера) имеется в виду,
что коды сценариев тестировались в браузере Mozilla Firefox.

Обратная связь
— Высокие, высокие отношения!
— Нормальные для духовных людей.
из к/ф «Покровские ворота»

Свои замечания, предложения и просто мысли по поводу книги читатели могут сообщить автору по электронной почте vasilev@univ.kiev.ua или
alex@vasilev.kiev.ua. На сайте www.vasilev.kiev.ua обычно представлена краткая
информация о книге. Там же (нередко по просьбе читателей) размещаются некоторые дополнительные сведения — в основном речь идет
о программных кодах примеров. В любом случае важно и интересно
знать мнение читателей о книге: что понравилось и, самое главное,
что не удалось. Критический взгляд со стороны полезен в любом деле,
а в таком, как написание книг, — вдвойне. Правда, в силу очевидных
и объективных причин всем приславшим письма ответить не получится, но письма будут прочитаны и приняты к сведению.

Об авторе
Вам что, уже и Лермонтов не угодил?! Или
у вас другие любимые авторы?
из к/ф «Покровские ворота»

Автор книги, Васильев Алексей Николаевич, доктор физико-математических наук по специальности «Теоретическая физика» и «Теплофизика и молекулярная физика», профессор кафедры теоретической
физики физического факультета Киевского национального университета имени Тараса Шевченко. Сфера научных интересов:

39

Вступление



компьютерное моделирование;



синергетика;



биофизика;



теория жидких кристаллов;



математическая экономика;



математическая лингвистика.

Имеет многолетний опыт чтения лекционных курсов по программированию и математическому моделированию. Автор книг по
программированию на языках C++, C#, Java и Python, книг по математическим пакетам Maple, Mathematica, Mathcad и Matlab, книг по
использованию в прикладных математических вычислениях офисного приложения Microsoft Excel.

Благодарности
Не надо громких слов. Они потрясают воздух, но не собеседника.
из к/ф «Формула любви»

Написание книг дает возможность не только научить чему-то полезному читателей, но еще и выразить слова признательности хорошим
людям за их явную и неявную поддержку.
Пользуясь случаем, хочу поблагодарить своих родителей, дочку Настю, сына Богдана и жену Илону за их терпение, понимание и любовь,
которые значат для меня очень много и дают силы двигаться вперед.
Спасибо моим читателям и студентам за их требовательность, готовность учиться и, конечно, за внимание к книгам. До того как попасть
в руки к читателю, книга проходит длинный путь. С ней работают
редакторы, переводчики, верстальщики и многие другие замечательные профессионалы своего дела. Далеко не со всеми из них я знаком
лично, но всем им я искренне благодарен.

Часть I
ОСНОВЫ JAVASCRIPT

Глава 1
ЗНАКОМСТВО С JAVASCRIPT

Так где мы можем обсудить аспекты, так
сказать, нашего общего дела?
из к/ф «Клуб самоубийц, или
Приключения титулованной особы»

Итак, мы начинаем знакомство с языком JavaScript. При этом нам придется постоянно иметь дело со сценариями. В основном наша стратегия будет заключаться в том, чтобы включать блок с программным
кодом, написанным на языке JavaScript, в документ с гипертекстовой
разметкой. Поскольку пока что нас язык JavaScript интересует сам по
себе, вне контекста веб-документа, мы будем использовать определенный очень простой шаблон документа с HTML-кодом, в который
«инкапсулирована» инструкция по включению в документ сценария
на языке JavaScript.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Для вставки в HTML-документ блока сценария используются дескрипторы и . В открывающем дескрипторе
описывается атрибут type со значением "text/javascript". Поэтому начинаться блок сценария будет инструкцией ,
а заканчиваться — инструкцией .

i


НА ЗАМЕТКУ
Прежде чем перейти непосредственно к рассмотрению основ языка JavaScript, некоторое внимание придется уделить способам «инкапсуляции» сценарного кода в документ.

Сценарий должен выполнять определенные действия (иначе какой
в нем смысл?). Результат этих действий должен как-то проявляться.
Самый простой способ «проявления» сценария в документе — вывод информации. Информацию будем выводить в рабочую область

42

Глава 1. Знакомство с JavaScript

документа. Как иллюстрацию для начала рассмотрим очень простой
пример, в котором средствами языка JavaScript в рабочем документе
отображается текст.

Отображение текста в рабочем документе
Вы получите то, что желали, согласно намеченным контурам.
из к/ф «Формула любви»

В самом первом примере мы решим очень простую задачу: сценарий
при выполнении отобразит в рабочем документе текст. В принципе,
как решается такая задача, иллюстрировалось во вступлении. Здесь
мы немножко расширим наше представление о возможностях сценариев в плане отображения текста в документе. Обратимся к программному коду, представленному в листинге 1.1.

 Листинг 1.1. Отображение сценарием текста в документе



Ëèñòèíã 1.1

Ëèñòèíã 1.1


document.write("Ïðèñòóïàåì ê èçó÷åíèþ JavaScript")




Речь идет об HTML-коде, содержащем, кроме прочего, сценарий. На
рис. 1.1 показано окно текстового редактора с данным кодом.

43

Часть I. Основы JavaScript

Рис. 1.1. Окно текстового редактора с кодом документа

Если этот же документ открыть в браузере, получим результат, как
на рис. 1.2.

Рис. 1.2. Результат отображения веб-документа в окне браузера

Результат выполнения сценария — текст под горизонтальной линией
в области документа. Все, что выше горизонтальной линии, включая
саму линию, определяется HTML-кодом документа.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Код гипертекстовой разметки кратко обсуждался во вступлении.
Здесь на всякий случай напомним назначение основных блоков
HTML-кода.
Инструкция является стандартным началом HTML-кода
документа. Сам программный код заключается между дескрипто-

44

Глава 1. Знакомство с JavaScript

рами и . В -блоке описываются основные свойства
документа. В частности, в блоке между дескрипторами и
указывается рабочее название документа (отображается в корешке вкладки окна документа в браузере). Основной код документа
размещается в -блоке. В данном конкретном примере этот
блок содержит заголовок 3-го уровня (специально выделенный
текст, отображается в рабочей области документа). Заголовок выделяется дескрипторами и . Инструкция используется
для отображения горизонтальной линии в области рабочего документа. Все, что находится между инструкциями , является
комментарием. Сценарий, как отмечалось ранее, размещается
в -блоке.

Фактически код сценария состоит всего из одной команды:
document.write("Ïðèñòóïàåì ê èçó÷åíèþ JavaScript")
Здесь мы имеем дело с вызовом метода write() из объекта document. Объект document — это объект рабочего документа (документ, в котором
размещен код сценария). Метод write(), который вызывается из объекта
документа document, отображает в данном документе текст, переданный
аргументом методу. Причем отображаемый текст может содержать не
только собственно текст, но и HTML-кодировку, как это имеет место
в рассмотренном примере. В частности, в текстовом выражении "Ïðèñòóïàåì ê èçó÷åíèþ JavaScript", которое передается аргументом методу
write(), слово JavaScript выделено дескрипторами и . В кодировке
HTML это означает выделение жирным шрифтом соответствующего
текстового фрагмента при отображении его в окне браузера. Так, собственно, и происходит (см. рис. 1.2).
Таким образом, при отображении текста в рабочем документе с помощью метода write() речь идет не просто об отображении текста в окне,
а об отображении HTML-кода. Такой код может содержать всевозможные HTML-дескрипторы (то есть дело не ограничивается дескрипторами и или подобными им).
i


НА ЗАМЕТКУ
Здесь открываются достаточно широкие возможности в плане динамического наполнения веб-документа. Другими словами, с помощью сценария можно управлять наполнением веб-документа
в динамическом режиме. Технологии, подобные этой, рассматриваются в третьей части книги.

45

Часть I. Основы JavaScript

Способы реализации сценария
Да, это от души. Замечательно. Достойно восхищения. Ложки у меня пациенты много раз глотали, не скрою. Но вот чтобы так, за обедом на
десерт, и острый предмет — замечательно. За это
вам наша искренняя сердечная благодарность.
из к/ф «Формула любви»

Выше мы помещали код сценария непосредственно в HTML-код вебдокумента. Для не очень больших примеров такой подход вполне удобен. Фактически речь идет об использовании определенного шаблонного кода. Если отталкиваться от предыдущего примера, то шаблон
такой, как показано ниже:



Ëèñòèíã 1.1

Ëèñòèíã 1.1


…// Êîä ñöåíàðèÿ




Программный код сценария размещается в том месте, где размещен
комментарий // Êîä ñöåíàðèÿ (для удобства восприятия соответствующее место в коде выделено жирным шрифтом).
Существует и другой способ «подключения» сценария к документу.
В этом случае сценарий записывается в отдельный файл, а в HTMLкоде документа добавляется ссылка на этот файл. Ссылка на файл со
сценарием указывается значением атрибута src в дескрипторе .

46

Глава 1. Знакомство с JavaScript

Например, если файл со сценарием называется Listing01_02.js и находится
в той же папке, что и файл с HTML-кодом, то HTML-код может быть
таким, как показано в листинге 1.2. Жирным шрифтом выделена инструкция с указанием загружаемого в документ файла со сценарием.

 Листинг 1.2. Загрузка сценария из внешнего файла



Ëèñòèíã 1.2

Ëèñòèíã 1.2






Здесь, как и в предыдущем примере, мы используем дескриптор
для включения в веб-документ блока сценария. Вместе с тем
сам -блок пустой, а в открывающем дескрипторе появилась инструкция src="Listing01_02.js", которой для атрибута src задается
значение "Listing01_02.js" — имя файла со сценарием.
i


НА ЗАМЕТКУ
Если файл со сценарием находится в папке, отличной от той, где находится файл веб-документа, значением атрибута src указывается
полный путь к файлу сценария.

В файл Listing01_02.js со сценарием помещаем такой код:
document.write("Ñöåíàðèé çàãðóæàåòñÿ èç ôàéëà")
На рис. 1.3 показан веб-документ с кодом из листинга 1.2, открытый
в текстовом редакторе.

47

Часть I. Основы JavaScript

Рис. 1.3. Веб-документ открыт в текстовом редакторе

На рис. 1.4 показано окно текстового редактора с открытым в нем
файлом со сценарием.

Рис. 1.4. Документ с кодом сценария открыт в текстовом редакторе

Наконец, веб-документ с кодом из листинга 1.2, который открыт
в браузере, показан на рис. 1.5.
С точки зрения конечного результата загрузка сценария из внешнего
файла не отличается от ситуации, когда код сценария включался непосредственно в код веб-документа.
i


НА ЗАМЕТКУ
Различия, конечно, есть: например, в части скорости загрузки сценария (да и надежности — файл со сценарием при неправильной
ссылке на файл сценария не будет найден вовсе). Но нас такие подробности пока не интересуют.

48

Глава 1. Знакомство с JavaScript

Рис. 1.5. Веб-документ открыт в браузере

На будущее, в зависимости от обстоятельств, мы будем или включать
код сценария непосредственно в HTML-код документа, или записывать сценарный код в отдельный файл, а в код веб-документа добавлять инструкцию загрузки внешнего файла со сценарием.

Знакомство с переменными
Видала я такую чепуху, по сравнению с которой эта чепуха — толковый словарь!
Л. Кэрролл «Алиса в Стране чудес»

Вывод информации в окно рабочего документа — это хорошо. Но
сценарий, состоящий из одной-единственной команды, выглядит уж
слишком скромно.
Мы усложняем ситуацию и переходим на новый уровень в освоении
премудростей JavaScript. Пришло время познакомиться с переменными.
Вообще переменная представляет собой именованную область памяти, к которой можно обращаться через имя для считывания значения
и записи значения. Таким образом, у переменной есть имя (которое
задается программистом). Еще у переменной есть тип.
i


НА ЗАМЕТКУ
Размер памяти, выделяемой для переменной, в принципе зависит
от ее типа. Во многих языках программирования (но не в JavaScript)
тип переменной указывается при ее объявлении и впоследствии не
может быть изменен.

49

Часть I. Основы JavaScript

Значения, которыми оперируют в программном коде JavaScript, относятся к одному из следующих типов:


текстовая строка;



числовое значение;



логическое значение (истина или ложь);



объект;



функция.

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



Д Е ТА Л И
Если переменная принимает значение логического типа, то это означает, что она принимает одно из двух возможных значений: true
(истина) или false (ложь). Обычно логические значения используются при проверке условий. Для работы с логическими значениями
предназначена группа операторов, которые называются логическими.

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


НА ЗАМЕТКУ
Для тех, кто знаком с такими языками программирования, как C++,
C# или Java, означенный «демократизм» языка JavaScript в плане
типизации переменных может вызвать некоторый шок. Тем не менее имеем то, что имеем.

Обычно переменные объявляются. Объявление переменной — некая декларация, цель которой в том, чтобы сообщить о намерении

50

Глава 1. Знакомство с JavaScript

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

Сценарий с одной переменной
Как объявить переменную в сценарии? Достаточно просто. Используем ключевое слово var, после которого указываем название переменной. Если переменных несколько, их названия указываются через запятую. Как иллюстрация в листинге 1.3 приведен программный код
сценария (сценарий вынесен в отдельный файл).

 Листинг 1.3. Использование переменной (файл Listing01_03.js)
var txt
txt="Èñïîëüçóåì ïåðåìåííóþ"
document.write(txt)
Для тестирования кода создаем веб-документ с таким кодом:



Ëèñòèíã 1.3

Ëèñòèíã 1.3







51

Часть I. Основы JavaScript

В результате выполнения сценария выводится следующее сообщение.

 Результат выполнения сценария (из листинга 1.3)
Èñïîëüçóåì ïåðåìåííóþ
Сценарий содержит три команды. Командой var txt объявляется переменная с названием txt. Командой txt="Èñïîëüçóåì ïåðåìåííóþ" переменной
txt присваивается текстовое значение "Èñïîëüçóåì ïåðåìåííóþ". Наконец,
с помощью команды document.write(txt) значение переменной txt отображается в рабочей области документа.



Д Е ТА Л И
При желании для большей наглядности в конце команд можно ставить точку с запятой. Если каждая команда находится в новой строке, этого можно не делать. Если несколько команд находятся в одной строке, они разделяются точкой с запятой.
Текстовые литералы заключаются в двойные или одинарные кавычки. Поэтому вместо выражения txt="Èñïîëüçóåì ïåðåìåííóþ" можно было
использовать команду txt='Èñïîëüçóåì ïåðåìåííóþ'.
Объявление переменной можно совмещать с присваиванием переменной значения. Например, вместо команд var txt и txt="Èñïîëüçóåì ïåðåìåííóþ" мы могли бы использовать одну команду var txt="Èñïîëüçóåì ïåðåìåííóþ".

На рис. 1.6 показан веб-документ с результатом выполнения сценария.

Рис. 1.6. Результат выполнения сценария, в котором использована переменная

Понятно, что переменных может быть больше, чем одна.

52

Глава 1. Знакомство с JavaScript

Сценарий с двумя переменными
Следующий пример иллюстрирует использование двух переменных.
Код сценария приведен в листинге 1.4.

 Листинг 1.4. Использование двух переменных (файл Listing01_04.js)
var txt,num
txt="Çíà÷åíèå ÷èñëà: "
num=123
document.write(txt+num)
Код сценария тестируем с помощью такого веб-документа:



Ëèñòèíã 1.4

Ëèñòèíã 1.4






Результат выполнения сценария приведен ниже.

 Результат выполнения сценария (из листинга 1.4)
Çíà÷åíèå ÷èñëà: 123
В окне браузера все выглядит так, как показано на рис. 1.7. Как и в предыдущем случае, здесь все достаточно просто. Командой var txt,num объявляются две переменные: одна называется txt, другая называется num.
Командами txt="Çíà÷åíèå ÷èñëà: " и num=123 переменным присваиваются

53

Часть I. Основы JavaScript

значения (текстовое переменной txt и числовое переменной num). После присваивания переменным значения командой document.write(txt+num)
в рабочей области документа отображается текст. Здесь аргументом
методу write() передано выражение txt+num, которым формально вычисляется сумма текстового значения и числового значения. Подобные
операции обрабатываются так: число автоматически переводится
в текстовый формат, и выполняется объединение (конкатенация) текстовых строк. Скажем, если значение переменной txt равно "Çíà÷åíèå ÷èñëà: ", а значение переменной num равно 123, то результатом выражения
txt+num будет текст "Çíà÷åíèå ïåðåìåííîé: 123".

Рис. 1.7. Результат выполнения сценария с двумя переменными

Присваивание переменной значений разных типов
Практически такой же сценарий реализуем, с использованием не
двух, а всего одной переменной. Сценарий приведен в листинге 1.5.

 Листинг 1.5. Использование двух переменных (файл Listing01_05.js)
var x
x="Çíà÷åíèå ÷èñëà: "
document.write(x)
x=123
document.write(x)
В сценарии объявляется переменная x, которая сначала принимает
текстовое значение, и это значение командой document.write(x) выводится
в рабочее окно. Затем переменной x присваивается числовое значение,
и снова в игру вступает команда document.write(x), благодаря которой текущее числовое значение переменной x также отображается в рабо-

54

Глава 1. Знакомство с JavaScript

чем окне (в той же строке, что и предыдущее сообщение). Чтобы проверить корректность работы данного сценария, файл со сценарием
загружаем в веб-документ с помощью такого кода:



Ëèñòèíã 1.5

Ëèñòèíã 1.5






Непосредственно результат выполнения сценария приведен ниже.

 Результат выполнения сценария (из листинга 1.5)
Çíà÷åíèå ÷èñëà: 123
Соответствующий веб-документ в окне браузера выглядит так, как
показано на рис. 1.8.

Рис. 1.8. Результат выполнения сценария с одной переменной, принимающей
значения разных типов

55

Часть I. Основы JavaScript

В рассмотренном примере наиболее важный и показательный момент связан с возможностью присваивать переменной значения разных типов.

Вычисление выражений
Язык JavaScript очень гибкий в плане синтаксиса и структуры программного кода. В частности, в JavaScript есть очень полезная и эффективная функция eval().
Если аргументом функции передать текст, то результатом функции
возвращается значение, которое получается при вычислении выражения, «спрятанного» в тексте. Например, выражение "3+(5*2+6)/4" является текстом. Но в этом тексте записано арифметическое выражение 3+(5*2+6)/4, которое имеет смысл: очевидно, речь идет о выражении
, которое равно 7.
Если воспользоваться командой eval("3+(5*2+6)/4"), то результат такой
команды — значение выражения 3+(5*2+6)/4.
В листинге 1.6 приведен код сценария, в котором с помощью функции
eval() вычисляется значение выражения, «упакованного» в текстовую
строку.

 Листинг 1.6. Вычисление выражения (файл Listing01_06.js)
var x="3 + (5*2 + 6) / 4"
document.write(x+" = ")
document.write(eval(x))
Здесь мы объявляем переменную x, а значением переменной присваивается текст "3 + (5*2 + 6) / 4" (для удобства восприятия между арифметическими операторами добавлены пробелы).
Командой document.write(x+" = ") данный текст (с добавленным знаком равенства) отображается в рабочем окне. А вот при выполнении команды document.write(eval(x)) отображается значение 7 (результат вычисления
выражения в тексте).
Файл со сценарием загружаем в веб-документ, для чего используем
следующий код:

56

Глава 1. Знакомство с JavaScript




Ëèñòèíã 1.6

Ëèñòèíã 1.6






В итоге результат выполнения сценария такой.

 Результат выполнения сценария (из листинга 1.6)
3 + (5*2 + 6) / 4 = 7
На рис. 1.9 показан веб-документ, открытый в браузере.

Рис. 1.9. Результат выполнения сценария с вычислением значения выражения

Понятно, что здесь проиллюстрирована довольно простая ситуация.
Вместе с тем функция eval() находит самое широкое применение на
практике. Особенно она полезна при реализации взаимодействия
сценария с веб-документом.

57

Часть I. Основы JavaScript

Основные операторы
Живьём брать демонов!.. Живьём брать самозванцев!
из к/ф «Иван Васильевич меняет профессию»

Наряду с переменными в JavaScript используется достаточно много
операторов. Обычно их разделяют на четыре группы:


арифметические операторы;



операторы сравнения;



логические операторы;



побитовые операторы.

Далее рассмотрим операторы каждой группы отдельно и еще кое-что.

Арифметические операторы
К арифметическим относятся операторы, предназначенные для выполнения арифметических действий. Арифметические операторы
языка JavaScript описаны в табл. 1.1. Операнды у арифметических
операторов, как правило, числовые (хотя могут быть и исключения —
они обсуждаются отдельно). Все операторы, за исключением операторов инкремента и декремента, бинарные — у них по два операнда.
Операторы инкремента и декремента унарные. Такие операторы используются с одним операндом.
i


НА ЗАМЕТКУ
У операторов инкремента и декремента есть постфиксная и префиксная формы. Результат вычисления выражений с такими операторами может зависеть от формы (префиксная или постфиксная)
оператора.

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

58

Глава 1. Знакомство с JavaScript

чения операнда A на значение операнда B. Например, результатом выражения 13%5 будет 3. Объяснение такое: при делении 13 нацело на 5
получаем 2. Остаток от такого деления вычисляется как 13 – 5 · 2 = 3.
Если операнды нецелые числа, принцип вычисления результата такой
же. Скажем, результатом выражения 10.5%3.3 является значение 0.6, поскольку при целочисленном делении 10,5 на 3,3 получаем 3, а остаток
от деления 10,5 – 3,3 · 3 = 10,5 – 9,9 = 0,6.

Таблица 1.1. Арифметические операторы JavaScript
Оператор

Описание

+

Оператор сложения. Результатом выражения A+B является сумма
значений числовых операндов A и B

-

Оператор вычитания. Результатом выражения A-B является разность
значений числовых операндов A и B

*

Оператор умножения. Результатом выражения A*B является произведение значений числовых операндов A и B

/

Оператор деления. Результатом выражения A*B является отношение
значений числовых операндов A и B

%

Остаток от целочисленного деления. Результатом выражения A%B
является остаток от деления нацело значения операнда A на значение
операнда B. Операнды могут быть целыми или нецелыми числами

++

Оператор инкремента. В результате вычисления выражения A++
(постфиксная форма) или ++A (префиксная форма) операнд A увеличивает свое значение на 1. Таким образом, инструкция A++, равно как
и инструкция ++A, эквивалентна команде A=A+1

--

Оператор декремента. При вычислении выражений --A (префиксная
форма) и A-- (постфиксная форма) значение операнда A уменьшается
на 1. Таким образом, каждая из инструкций --A или A-- эквивалентна
команде A=A-1

i


НА ЗАМЕТКУ
Из-за ошибок округления при выполнении операций с числами
с плавающей точкой реальный результат может несколько отличаться от «точного» значения.

Как отмечалось выше, у операторов инкремента ++ и декремента -есть префиксная и постфиксные формы. В префиксной форме оператор указывается перед операндом (например, ++A или --A), а в постфиксной форме оператор указывается после операнда (например, A++
или A--). По отношению к значению операнда разницы в префиксной
и постфиксной формах нет. Так, что в результате выполнения инс-

59

Часть I. Основы JavaScript

трукции A++, что в результате выполнения инструкции ++A значение
переменной A будет увеличено на 1. Но если инструкция с оператором
инкремента или декремента сама является частью более сложного
выражения, то имеет значение, в какой форме (префиксной или постфиксной) использован оператор. Общее правило состоит в следующем. Если в выражении использовано подвыражение с оператором
инкремента/декремента в префиксной форме, то сначала выполняется операция инкремента/декремента, и только после этого вычисляется значение выражения. Если же в выражении есть подвыражение с оператором инкремента/декремента в постфиксной форме,
то сначала вычисляется значение выражения, а затем выполняется
операция инкремента/декремента. Проиллюстрируем сказанное на
простом примере.
Рассмотрим следующий программный код:
var x,y
x=10
y=x++
Код достаточно простой: объявляются две переменные x и y, и каждой
из них присваивается значение. Вопрос такой: каковы будут значения переменных x и y после выполнения кода? Ответ состоит в том,
что переменная x будет иметь значение 11, а переменная y будет иметь
значение 10. Теперь постараемся разобраться, почему результат именно такой. Начнем с команды x=10, которой переменной x присваивается значение 10. Далее выполняется команда y=x++. Здесь единственная
интрига связана со способом вычисления выражения. А именно, возникает вопрос, как нам следует поступить: сначала увеличить на единицу значениепеременной x и уже это значение присвоить переменной y или следует сначала присвоить переменной y текущее значение
10 переменной x, а затем увеличить значение x на единицу? Поскольку
оператор инкремента использован в постфиксной форме, то сначала
выполняется присваивание (переменная y получает значение 10), а затем значение переменной x увеличивается на единицу (становится
равным 11).
Достаточно немного изменить код, и мы получим иной результат:
var x,y
x=10
y=++x

60

Глава 1. Знакомство с JavaScript

В этом случае обе переменные x и y в конечном счете получат значение 11. Причина в том, что при выполнении команды y=++x, поскольку оператор инкремента использован в префиксной форме, сначала
значение переменной x становится равным 11, а затем данное значение
присваивается переменной y.
На ситуацию можно посмотреть и по-другому (и это будет даже лучше). Например, рассмотрим такой код:
var x,y
x=10
y=x++ + x++
Вопрос традиционный: какими будут значения переменных x и y?
Чтобы дать ответ, будем исходить из того, что в постфиксной форме
оператор инкремента увеличивает значение операнда, но возвращает старое значение. Выражение y=x++ + x++ в правой части содержит
сумму двух слагаемых: x++ и x++. Значения слагаемых вычисляются
справа налево. При вычислении первого слагаемого x++ значение переменной x увеличивается на 1 (значение переменной x становится
равным 11), но результатом выражения x++ возвращается старое значение 10. При вычислении второго слагаемого x++ значение переменной x
еще раз увеличивается на 1 (теперь значение переменной равно 12), но
результатом выражения x++ возвращается старое значение 11. Таким
образом, значение переменной y представляет собой сумму чисел 10
и 11, что дает 21. А значение переменной x равно 12.
Если исходить из того, что оператор инкремента в префиксной форме
увеличивает на единицу значение операнда и возвращает результатом новое значение, легко проанализировать следующий программный код:
var x,y
x=10
y=++x + ++x
Теперь значение переменной x будет равно 12, а значение переменной y
равняется 23. Почему? Потому что при вычислении первого слагаемого ++x получаем значение 11, переменная x также имеет значение 11.
При вычислении второго слагаемого ++x получаем 12. Значение переменной x тоже равно 12. В итоге x равно 12, а значение y равно 23 (сумма 11 и 12).

61

Часть I. Основы JavaScript
i


НА ЗАМЕТКУ
Мы уже знаем, что операндами оператора + могут быть не только
числа, но и текстовые значения. Если оба операнда — текстовые, то
выполняется конкатенация строк. Если один из операндов — текст,
а другой операнд — число, то выполняется автоматическое приведение числового значения к текстовому с последующей конкатенацией строк.
Помимо этого, допускается использование других арифметических
операторов с текстовыми операндами, которые являются текстовым представлением числа. Подробнее данная тема обсуждается
в разделе, посвященном преобразованию типов.

Операторы сравнения
Операторы сравнения используются для сравнения значений (обычно числовых). В табл. 1.2 представлены основные операторы сравнения, используемые в JavaScript. Все операторы бинарные (у каждого
по два операнда), а результатом возвращается значение логического
типа (true, если соответствующее соотношение истинно, и false в противном случае).
Если речь идет о сравнении числовых значений, то назначение каждого из операторов достаточно очевидно без дополнительных пояснений. Подчеркнем лишь принципиальные отличия между операторами == и ===, а также != и !==.
Опять же, если речь идет о сравнении числовых значений, то разницы, например, между операторами == и === нет. Скажем, результатом
выражения 123==123 является значение true, и результатом выражения
123===123 также является значение true. Но вот результаты выражений
123=="123" и 123==="123" разные. Результатом выражения 123="123" является
значение true. Значение выражения 123==="123" равно false. Второй случай, скорее всего, вопросов не вызовет: вполне логично, что числовое
значение 123 не равно тексту "123". Почему же тогда результат выражения 123=="123" равен true? Все дело в автоматическом преобразовании
типов. При вычислении выражения 123==="123" автоматическое преобразование типов не применяется, поэтому получаем вполне ожидаемый результат. А вот при вычислении выражения 123=="123" текстовое
значение "123", являющееся на самом деле текстовым представлением
числа 123, преобразуется в числовое значение 123. В результате получается, что оба операнда одинаковы.

62

Глава 1. Знакомство с JavaScript

Таблица 1.2. Операторы сравнения JavaScript
Оператор

Описание

==

Оператор «равно». Результатом выражения A==B является значение
true, если значение операнда A равно значению операнда B. В противном случае значение выражения равно false. Если значения операндов A и B относятся к разным типам, автоматически предпринимается попытка приведения значений к одному типу для проведения
сравнения

!=

Оператор «неравно». Результатом выражения A!=B является значение
true, если значение операнда A не равно значению операнда B. В противном случае значение выражения равно false. Для значений операндов A
и B разных типов автоматически выполняется приведение к одному
типу (если возможно) для проведения сравнения

===

Оператор «строго равно». Результатом выражения A===B является
значение true, если значение операнда A равно значению операнда B.
В противном случае значение выражения равно false. В отличие от
оператора == в данном случае приведение к одному типу значений
операндов не выполняется

!==

Оператор «строго неравно». Результатом выражения A!==B является
значение true, если значение операнда A не равно значению операнда B. В противном случае значение выражения равно false. В отличие
от оператора != не выполняется приведение к одному типу значений
операндов

>

Оператор «больше». Результатом выражения A>B является значение
true, если значение операнда A больше значения операнда B. В противном случае значение выражения равно false

>=

Оператор «больше или равно». Результатом выражения A>=B является
значение true, если значение операнда A больше или равно значению
операнда B. В противном случае значение выражения равно false

<

Оператор «меньше». Результатом выражения A>

Оператор побитового сдвига вправо (с замещением старших битов нулями). Оператор побитового сдвига вправо. Результатом выражения
A>>n является число, которое получается из двоичного представления
числа A путем смещения (или сдвига) всего кода вправо на n позиций.
Младшие биты при этом теряются, а старшие заполняются нулями

Попарно сравнивая биты, находящиеся на одинаковых позициях
в представлении операндов, получаем побитовое представление для
числа-результата. Фактически разница лишь в правилах, которые используются при сравнении битов. Для операции побитового и набор
возможных исходов сравнения проиллюстрирован в табл. 1.8.
Таблица 1.8. Таблица истинности для операции побитового и
A B

1

0

1

1

0

0

0

0

70

Глава 1. Знакомство с JavaScript

Например,

вычислим

значение

выражения 22&27. Поскольку
, то в двоичном пред-

ставлении число 22 имеет вид 10110.
Аналогично имеем

,

что в итоге дает код 11011. Таким образом, получаем:
Число

соответствует значению
. То есть результатом выражения 22&27 является значение 18.



с

двоичным

кодом

.

10010

Д Е ТА Л И
Вообще в JavaScript для представления чисел используется 4 байта
или 32 бита. Но, поскольку здесь мы оперируем с небольшими по
модулю положительными числами, старшие нулевые биты можно
игнорировать — наличие этих битов на конечном результате не сказывается в силу как раз того обстоятельства, что они нулевые. Хотя
технически такие биты присутствуют, и забывать об этом не стоит.

В табл. 1.9 представлены правила сравнения битов при выполнении
операции побитового или.
Таблица 1.9. Таблица истинности для операции побитового или
A B
1
0

1
1
1

0
1
0

Если вычислить результат выражения 22|27, получим значение 31.
Действительно, учитывая правила сравнения битов для операции
побитового или, можем записать следующее:

. Отсюда, учиты -

вая, что
,
получаем результат 31. Для операции побитового исключающего или
правила сравнения битов такие, как в табл. 1.10.
Таблица 1.10. Таблица истинности для операции побитового исключающего или
A B

1

0

1

0

1

0

1

0

71

Часть I. Основы JavaScript

Рассмотрим выражение 22^27.
.

Легко получаем следующее:
Поскольку
результат выражения 22^27 равен 13.

, то

Наконец, простой для понимания является операция побитовой инверсии (правила преобразования битов приведены в табл. 1.11).
Таблица 1.11. Таблица истинности для операции побитовой инверсии
A

1

0

~A

0

1

Учитывая правила записи и обработки отрицательных чисел, легко
сообразить, что если в переменную A записано некоторое число x, то
результатом выражения ~A является значение –(x + 1). Например, значение выражения ~6 равно -7, а результат выражения ~-5 равен 4.
i


НА ЗАМЕТКУ
Стоит заметить, что операции побитового и, побитового или и побитовой инверсии имеют много общего соответственно с операциями
логического и, логического или и логического отрицания.

Нелишним будет прокомментировать принципы вычисления выражений с операторами сдвига. Для простоты будем исходить из того,
что числа кодируются восемью битами. Начнем с оператора >> отличается от оператора >> тем, что старшие биты
заполняются нулями вне зависимости от значения знакового
бита.

Сокращенные формы оператора присваивания
В JavaScript существуют специальные сокращенные формы оператора присваивания. Поясним на примере. Допустим, необходимо
выполнить команду вида A = A ☺ B, причем символом ☺ обозначен
один из бинарных арифметических или побитовых операторов.
Тогда соответствующую команду можно записать в более простом,
или, точнее, компактном виде, а именно как A ☺ = B. В табл. 1.12 пе-

73

Часть I. Основы JavaScript

речислены основные варианты сокращенных форм оператора присваивания.
Таблица 1.12. Сокращенные формы оператора присваивания в JavaScript
Оператор

Пример использования

Эквивалентная команда

+=

A+=B

A=A+B

-=

A-=B

A=A-B

*=

A*=B

A=A*B

/=

A/=B

A=A/B

%=

A%=B

A=A%B

&=

A&=B

A=A&B

|=

A|=B

A=A|B

^=

A^=B

A=A^B

n

>>>=

A>>>=n

A=A>>>n

Перечисленные операторы будут время от времени использоваться
в программных кодах. Наличие возможности не означает необходимости ее использовать. Поэтому читатель волен в своем выборе способа оформления программного кода и, в частности, использования
или неиспользования сокращенных форм оператора присваивания.

Тернарный оператор
Обычно операторы — унарные или бинарные. У них соответственно
один и два операнда. Но в языке JavaScript есть тернарный оператор:
у этого оператора сразу три операнда. Тернарный оператор представляет собой упрощенную форму условного оператора (о котором речь
пойдет позже). Шаблон вызова тернарного оператора такой (жирным
шрифтом выделены ключевые элементы шаблона):
óñëîâèå ? çíà÷åíèå_1 : çíà÷åíèå_2
Синтаксис у команды вызова оператора достаточно простой. Первым
операндом указывается условие, которое проверяется при вычислении выражения с тернарным оператором. После условия следует вопросительный знак ? (синтаксический элемент тернарного оператора) и значение (второй операнд), которое возвращается результатом

74

Глава 1. Знакомство с JavaScript

выражения, если условие истинно. Затем следует двоеточие : и еще
одно значение (третий операнд тернарного оператора). Данное значение возвращается, если условие ложно.
i


НА ЗАМЕТКУ
Поскольку разделителями операндов в тернарном операторе является вопросительный знак ? и двоеточие :, то обычно тернарный
оператор обозначают как ?:.

Например, рассмотрим выражение X?A:B. Если значение X равно true
(или интерпретируется как true), то результатом выражения будет
значение A. Если значение X равно false (или интерпретируется как
false), результат выражения равен B.

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


НА ЗАМЕТКУ
Наша стратегия состоит в том, чтобы решать проблемы по мере
их возникновения. Вопросы, связанные с преобразованием типов,
критичные для понимания материала книги, будут освещаться по
мере необходимости, чтобы не перегружать читателя. Здесь мы
рассмотрим лишь некоторые аспекты, связанные с преобразованием типов.

Выделим основные моменты, на которые следует обратить внимание.


Отличные от нуля числа могут использоваться в качестве логических значений. Отличные от нуля числа интерпретируются как
true, нуль интерпретируется как false.

75

Часть I. Основы JavaScript



Если в арифметическом выражении с вычислением разности,
произведения, частного (но не сложения!), побитового сдвига или
другой побитовой операции имеются операнды, являющиеся текстовым представлением числа, такое текстовое представление автоматически преобразуется в число. Например, результатом выражения "300"-"200" является число 100. Или, скажем, результатом
выражения "32">>"3" является число 4. Это правило, однако, не распространяется на операцию сложения. Например, результатом
выражения "300"+"200" является текстовое значение "300200" (объединение текстовых строк).



Если один из операндов в выражении вычисления суммы текстовый, а другой операнд числовой, то числовой операнд приводится
к текстовому формату и выполняется конкатенация строк. Это
же имеет место в том случае, если текстовый операнд является
представлением числа. Выше отмечалось, что результат выражения 300+"200" равен "300200", — так же, как и результат выражения
"300"+200.



Для приведения текстового представления числа к числовому
типу можно указать унарный оператор плюс перед соответствующим текстовым литералом. Например, результатом выражения
+"300"+200 является число 500. Такой же результат получим при вычислении значения выражения +"300"+ +"200". Здесь использованы
два оператора плюс подряд (но между ними есть пробел!). Это не
оператор инкремента, а два разных оператора. Первый оператор
плюс — бинарный, а второй оператор плюс — унарный (знаковый).
Данная схема работает, даже если в текстовом литерале «спрятано» отрицательное число. Например, выражение +"300"+ +"-200" имеет
смысл. Значение выражения есть число 100. То есть здесь унарный
оператор «плюс» выполняет роль «извлекателя» числа из текста.

Могут иметь место и другие достаточно специфические ситуации,
связанные с явным или неявным преобразованием типов. Все они
при необходимости будут прокомментированы.
i


НА ЗАМЕТКУ
Вообще есть хороший рецепт, который состоит в том, чтобы избегать
всяческих непонятных ситуаций. Обычно надежнее реорганизовать
программный код так, чтобы результат его выполнения был предсказуем. Как говорится, самый короткий путь — тот, который знаком.
Хотя и для нового нужно быть открытым. Такая вот диалектика.

76

Глава 1. Знакомство с JavaScript

Приоритет операторов
В JavaScript довольно много операторов. Рассмотренные выше операторы составляют далеко не полный список. Анализ сложных выражений, состоящих из большого количества внутренних инструкций,
может представлять значительные сложности. Поэтому желательно
иметь хотя бы общее представление о приоритете операторов.



Д Е ТА Л И
Приоритет — понятие относительное. Приоритет операторов влияет на прядок вычисления подвыражений в сложном выражении.
Подвыражения с операторами, имеющими более высокий приоритет, вычисляются первыми. После них вычисляются подвыражения
с операторами, имеющими более низкий приоритет.
Если некоторые операторы имеют одинаковый приоритет, то соответствующие подвыражения вычисляются последовательно одно
за другим (обычно слева направо в порядке появления подвыражений в выражении).

В табл. 1.13 рассмотренные нами ранее операторы собраны в группы по приоритетности. Приоритет каждой группы операторов указан в виде целого числа (хотя это условный показатель). Чем меньше
число, тем выше приоритет операторов — то есть в таблице операторы
размещены в порядке убывания приоритета.
i


НА ЗАМЕТКУ
Помимо уже знакомых нам операторов, в табл. 1.13 представлены еще
два оператора: унарный «плюс» и унарный «минус». Речь идет об операторах, которые определяют знак числа. Знак минус для отрицательных чисел указывается в обязательном порядке. Для положительных
чисел знак плюс обычно не указывают (хотя это можно сделать).
Также стоит заметить, что у префиксной и постфиксной форм операторов инкремента и декремента разный приоритет.

Понятно, что запомнить всю эту таблицу практически нереально. Да
это и не нужно. Что можно порекомендовать? Хороший подход состоит в использовании круглых скобок. Круглые скобки имеют высший
приоритет и позволяют изменять порядок вычисления подвыражений
в выражении. Помимо этого, выражения, содержащие круглые скобки,
обычно легче анализировать. Так что круглые скобки — это не только
функциональный, но еще и неплохой «декоративный» элемент.

77

Часть I. Основы JavaScript

Таблица 1.13. Приоритетность операторов в JavaScript
Приоритет (по убыванию)
1
2
3

4

5
6

7

8

9
10
11
12
13
14
15

78

Название
Круглые скобки (используются для
группировки выражений)
Постфиксная форма инкремента
Постфиксная форма декремента
Логическое отрицание
Побитовая инверсия
Унарный «плюс»
Унарный «минус»
Префиксная форма инкремента
Префиксная форма декремента
Умножение
Деление
Остаток от целочисленного деления
Сложение
Вычитание
Побитовый сдвиг влево
Побитовый сдвиг вправо
Побитовый оператор сдвига вправо
(с заполнением старшего бита нулями)
Меньше
Меньше или равно
Больше
Больше или равно
Равенство
Неравенство
Строгое равенство
Строгое неравенство
Побитовое «и»
Побитовое «исключающее или»
Побитовое «или»
Логическое «и»
Логическое «или»
Тернарный оператор
Операторы присваивания

Оператор
(и)
++
-!
~
+
++
-*
/
%
+
>
>>>
<

>=
==
!=
===
!==
&
^
|
&&
||
?:
=
+=
-=
*=
/=
%=
=
>>>=
&=
^=
|=

Глава 1. Знакомство с JavaScript

Резюме
Прием окончен. Обеденный перерыв!
из к/ф «Иван Васильевич меняет
профессию»

В этой главе мы узнали следующее.


Сценарий можно непосредственно разместить в HTML-коде вебдокумента, а можно сохранить в отдельном файле и затем загрузить файл со сценарием при открытии веб-документа. При сохранении сценария в отдельном файле в блоке адрес файла для
загрузки указывается значением атрибута src.



Метод write() объекта document отображает текст с учетом HTMLразметки, так что в отображаемый текст можно включать HTMLдескрипторы.



Переменные объявляются с использованием ключевого слова var.
Тип переменной не указывается. В процессе выполнения программы одна и та же переменная может иметь значения разных
типов.



Основные типы данных в JavaScript: текст, числа, логические значения, объекты и функции. Текстовые литералы заключаются
в одинарные или двойные кавычки. Логических значений два: true
(истина) и false (ложь).



Функция eval() позволяет вычислять выражения, «упакованные»
в текстовый литерал.



Для реализации основных действий в JavaScript имеются встроенные операторы (арифметические, логические, операторы сравнения и побитовые операторы).



При вычислении выражений в случае необходимости имеет место
автоматическое преобразование типов. Также необходимо учитывать приоритет операторов. Для изменения порядка вычисления
подвыражений в выражении рекомендуется использовать круглые скобки.

Глава 2
УПРАВЛЯЮЩИЕ ИНСТРУКЦИИ

Да ты, батюшка, только скажи, а мы переймем.
из к/ф «Иван Васильевич меняет
профессию»

В этой главе речь пойдет об управляющих инструкциях языка
JavaScript. Управляющие инструкции — это условный оператор, операторы цикла, оператор выбора, ну и все, что с ними связано. Фактически далее обсуждается тот аппарат языка программирования
JavaScript, который лежит в фундаменте всей концепции создания
сценариев. Мы познакомимся с рабочим инструментом, без успешного владения которым невозможно стать хорошим программистом.
Начнем с условного оператора.

Условный оператор
На улице идет дождь, а у нас идет концерт.
из к/ф «Покровские ворота»

Условный оператор позволяет выполнять разные блоки команд в зависимости от истинности или ложности некоторого условия. Нечто
подобное мы наблюдали в предыдущей главе при обсуждении тернарного оператора. Но теперь все намного серьезнее.

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

80

Глава 2. Управляющие инструкции

выполняется второй блок команд. Это общая схема. Теперь возникает вопрос: как ее реализовать? Для условного оператора используется
определенный шаблон, который представлен ниже (основные элементы шаблона выделены жирным шрифтом):
if(óñëîâèå){
// ïåðâûé áëîê êîìàíä
}
else{
// âòîðîé áëîê êîìàíä
}
Описание условного оператора начинается с ключевого слова if, после
которого в круглых (( и )) скобках указывается условие. Далее в фигурных скобках ({ и }) размещается блок команд, выполняемых при
истинности условия (то есть когда при вычислении значения выражения, указанного в круглых скобках после ключевого слова if, получаем значение true). Если условие ложно (значение выражения после
ключевого слова if равно false), блок команд в фигурных скобках после ключевого слова if не выполняется, а выполняются другие команды, указанные в блоке (определяется парой фигурных скобок) после
ключевого слова else.



Д Е ТА Л И
То есть выполнение условного оператора означает, что будет выполнен один и только один блок команд из двух возможных. Кроме
рассматриваемой здесь общей формы условного оператора, существует еще упрощенная форма условного оператора, в которой
нет else-блока. Мы рассмотрим ее позже.
Также следует отметить, что если блок команд состоит всего из одной команды, то фигурные скобки можно не использовать. Тем не
менее фигурные скобки рекомендуется использовать всегда, поскольку их наличие улучшает восприятие кода и позволяет избежать случайных ошибок.

Общая схема выполнения условного оператора проиллюстрирована
блок-схемой, представленной на рис. 2.1.
Поскольку лучший критерий теории — практика, имеет смысл посмотреть на условный оператор в реальном программном коде.

81

Часть I. Основы JavaScript

Рис. 2.1. Схема выполнения условного оператора

Пример с условным оператором
Небольшой пример, в котором используется условный оператор, приведен в листинге 2.1.

 Листинг 2.1. Сценарий с условным оператором (файл Listing02_01.js)
var txt,name
txt="Äàâàéòå ïîçíàêîìèìñÿ! Êàê Âàñ çîâóò?"
name=prompt(txt)
if(name==""){
document.write("Æàëü, íî Âû íå ïðåäñòàâèëèñü!")
}
else{
document.write("Ïðèÿòíî ïîçíàêîìèòüñÿ, "+name+"!")
}
В первую очередь мы рассмотрим результат выполнения сценария
в браузере, а уже затем проанализируем программный код сценария.
Для загрузки файла со сценарием в веб-документ используем следующий HTML-код:



82

Глава 2. Управляющие инструкции


Ëèñòèíã 2.1

Ëèñòèíã 2.1






При открытии документа с таким кодом в окне браузера появляется диалоговое окно с полем ввода. Как все это выглядит, показано на
рис. 2.2.

Рис. 2.2. Окно браузера при загрузке веб-документа со сценарием
с условным оператором

Окно документа временно (до закрытия диалогового окна) заблокировано. У диалогового окна есть поле ввода и две кнопки (ОК и Отмена). Здесь возможны варианты. Сначала рассмотрим ситуацию, когда

83

Часть I. Основы JavaScript

все происходит стандартно: пользователь вводит в поле ввода окно
и щелкает по кнопке ОК, как показано на рис. 2.3.

Рис. 2.3. В поле ввода вводится имя пользователя

Диалоговое окно закрывается, а в рабочем окне браузера появляется
текстовое сообщение, содержащее, кроме прочего, имя, которое было
введено пользователем в поле ввода. Результат описанных действий
показан на рис. 2.4.

Рис. 2.4. Содержимое рабочего окна браузера после ввода пользователем
имени

84

Глава 2. Управляющие инструкции
i


НА ЗАМЕТКУ
Если в браузере щелкнуть по пиктограмме перезагрузки, диалоговое окно с полем ввода появится снова.

Ситуация может развиваться в несколько ином ключе. Допустим,
пользователь щелкает по кнопке ОК в диалоговом окне, но при этом
поле ввода остается пустым (рис. 2.5).

Рис. 2.5. Пользователь щелкает в диалоговом окне по кнопке ОK,
но при этом поле ввода не заполнено

Если так, то в рабочей области браузера появится сообщение, но уже
другое (рис. 2.6).



Д Е ТА Л И
Откровенно говоря, пользователь может и не щелкать по кнопке ОК
в диалоговом окне, а щелкнуть по кнопке Отмена. В этом случае
метод prompt() возвращает пустую ссылку. В результате значением
переменной name будет null — специальное значение, обозначающее
пустую ссылку. При попытке отобразить значение (которое равно
null) переменной name с помощью метода write() получим текст "null".
Во избежание ненужного усложнения кода в рассматриваемом сценарии обработка ситуации, когда пользователь щелкает по кнопке
Отмена, не предусмотрена.

Вкратце так выполняется сценарий. Теперь настал черед проанализировать его код.

85

Часть I. Основы JavaScript

Рис. 2.6. Содержимое рабочего окна браузера после щелчка пользователем
в диалоговом окне по кнопке OK при пустом поле ввода

В сценарии мы используем условный оператор и метод prompt(), с помощью которого отображается окно с полем для ввода текста (имени
пользователя).
i


НА ЗАМЕТКУ
Хотя может показаться, что prompt() является функцией (поскольку
как бы вызывается не из объекта), на самом деле — это метод. Это
метод объекта окна window. Полная инструкция вызова метода выглядела бы как window.prompt(). Но поскольку объект окна window разрешается не указывать (такой вот особенный объект), то мы использовали упрощенную форму вызова метода, что создает некоторые
иллюзии. Пока такой поворот дел для нас не принципиален. Нам
просто необходима утилита, которая позволила бы ввести информацию в сценарий.

При вызове метода prompt() появляется, как отмечалось, окно с полем
ввода. Текст, переданный аргументом методу prompt(), отображается
над полем ввода. Метод возвращает результат — текст, который пользователь вводит в поле ввода в диалоговом окне (тут, правда, есть некоторые особенности, но о них немного позже).
В сценарии объявляются две переменные. Переменной txt значением
присваивается текст, который предстоит отобразить в окне с полем
ввода. Поэтому переменная txt передается аргументом методу prompt().

86

Глава 2. Управляющие инструкции

Результат вызова метода записывается в переменную name. Вся команда выглядит как name=prompt(txt).
Далее на сцену выходит условный оператор. В условном операторе
проверяется условие name=="". Условие истинно, если значение переменной name равно "" (пустая текстовая строка). Если так, то командой document.write("Æàëü, íî Âû íå ïðåäñòàâèëèñü!") в рабочей области документа отображается текст "Æàëü, íî Âû íå ïðåäñòàâèëèñü!". В противном случае
(если условие ложно), будет выполнена команда document.write("Ïðèÿòíî
ïîçíàêîìèòüñÿ, "+name+"!").
Текст, который отображается в документе, является объединением
текстовой строки "Ïðèÿòíî ïîçíàêîìèòüñÿ, ", значения переменной name и текста "!".

Упрощенная форма условного оператора
Как отмечалось ранее, у условного оператора есть упрощенная форма, которая не содержит else-ветки кода. Шаблон упрощенной формы
условного оператора такой:
if(óñëîâèå){
// áëîê êîìàíä
}
То есть имеется if-часть оператора, где, собственно, указывается проверяемое условие, и блок команд в фигурных скобках, которые выполняются при истинном условии. Если условие ложно, команды не
выполняются.
i


НА ЗАМЕТКУ
Таким образом, блок команд в условном операторе выполняется
только при истинном условии. При ложном условии не происходит
ничего.

На рис. 2.7 показана блок-схема, иллюстрирующая механизм выполнения условного оператора в сокращенной форме.
Наличие упрощенной формы условного оператора позволяет проявлять значительную «гибкость» при составлении программных кодов.

87

Часть I. Основы JavaScript

Посмотрим, как мог бы выглядеть сценарий, рассмотренный выше,
если вместо полной формы условного оператора использовать сокращенную форму.

Рис. 2.7. Схема выполнения условного оператора в упрощенной форме

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

 Листинг 2.2. Сценарий с условным оператором в упрощенной форме
(файл Listing02_02.js)

var txt,name,msg
txt="Äàâàéòå ïîçíàêîìèìñÿ! Êàê Âàñ çîâóò?"
msg="Æàëü, íî Âû íå ïðåäñòàâèëèñü!"
name=prompt(txt)
if(name!=""){
msg="Ïðèÿòíî ïîçíàêîìèòüñÿ, "+name+"!"
}
document.write(msg)
При создании веб-документа используем следующий HTML-код:

88

Глава 2. Управляющие инструкции




Ëèñòèíã 2.2

Ëèñòèíã 2.2






Результат выполнения данного сценария точно такой же, как и в предыдущем примере (с поправкой на название листинга, которое отображается рабочем документе). Посмотрим, что изменилось в коде сценария и почему изменения в коде не сказались на внешнем результате
выполнения сценария.
Формальных изменений в коде сценария не очень много. Но вся стратегия выполнения кода изменилась. Более конкретно, в сценарии,
помимо переменных txt и name, появляется еще и переменная msg. Переменные txt и msg сразу получают значения. В переменную txt записывается текст, который отображается в диалоговом окне над полем ввода.
В переменную msg записывается текст "Æàëü, íî Âû íå ïðåäñòàâèëèñü!", который должен появиться в рабочем документе, если пользователь при
щелчке по кнопке ОК оставит поле ввода пустым. Затем командой
name=prompt(txt) отображается диалоговое окно и введенное пользователем значение записывается в переменную name. Следующая команда — условный оператор в упрощенной форме. Причем теперь проверяется условие name!="". Условие истинно, если значение переменной
name не является пустой текстовой строкой "". Так вот если значение
переменной name — не пустая строка, то командой msg="Ïðèÿòíî ïîçíàêîìèòüñÿ, "+name+"!" переопределяется значение переменной msg. Если значение
переменной name — пустая строка (и условие name!="" ложно), то команда в условном операторе по изменению значения переменной msg

89

Часть I. Основы JavaScript

не выполняется, и переменная остается со своим старым значением
"Æàëü, íî Âû íå ïðåäñòàâèëèñü!". Наконец, командой document.write(msg) значение
переменной msg отображается в рабочем документе.

Вложенные условные операторы
Среди выполняемых в условном операторе команд могут быть другие условные операторы. В этом случае говорят о вложенных условных операторах. Вложенные условные операторы позволяют создавать множественные точки ветвления в программном коде. Как
иллюстрацию рассмотрим небольшой пример. Соответствующий
программный код представлен в листинге 2.3.



Д Е ТА Л И
Немного с опережением графика мы в сценарии создаем функцию.
Вообще функциям посвящена отдельная глава. Здесь будет только
маленькая зарисовка.
Вложенные условные операторы использованы в программном
коде функции, которая является реализацией кусочно-гладкой

зависимости

В сценарии описывается соответствующая функция, а затем она
вызывается с разными аргументами.

 Листинг 2.3. Сценарий с вложенными условными операторами
(файл Listing02_03.js)

// Îïèñàíèå ôóíêöèè:
function f(x){
// Åñëè x







n){
break
}
}
document.write(txt+s)

112

Глава 2. Управляющие инструкции

Надо заметить, что пустой второй блок (блок, в котором размещается
условие) эквивалентен истинности условия в операторе цикла. Формально в этом случае цикл бесконечный. Поэтому в самом теле оператора цикла необходимо предусмотреть возможность завершения
оператора цикла. Мы используем условный оператор, в котором проверяется условие k>n. При истинности условия выполняется инструкция
break. Выполнение инструкции break завершает работу оператора цикла.



Д Е ТА Л И
Во втором блоке мы использовали условие kn. Это условие завершения
работы оператора цикла. Истинность условия kn, и наоборот.

Оператор выбора switch
— Вопрошайте.
— Готовы ли вы сказать нам всю правду?
— Ну, всю — не всю… А что вас интересует?
из к/ф «Формула любви»

Достаточно полезным является оператор выбора switch. В некотором
смысле оператор switch напоминает конструкцию из вложенных условных операторов (хотя отождествлять их нельзя).

Синтаксис оператора выбора
Синтаксис оператора иллюстрирует следующий шаблонный код
(жирным шрифтом выделены ключевые элементы шаблона):
switch(óñëîâèå){
case çíà÷åíèå_1:
// êîìàíäû 1-ãî áëîêà
break
case çíà÷åíèå_2:
// êîìàíäû 2-ãî áëîêà
break
// ïðî÷èå case-áëîêè

113

Часть I. Основы JavaScript

case çíà÷åíèå_N:
// êîìàíäû N-ãî áëîêà
break
default:
// êîìàíäû áëîêà ïî óìîë÷àíèþ
}
Начинается описание оператора выбора с ключевого слова switch.
В круглых скобках после ключевого слова switch указывается некоторое
выражение. Далее следуют case-блоки. Каждый такой блок начинается
ключевым словом case, после которого указывается некоторое значение (контрольное значение) и двоеточие. Затем следуют команды данного case-блока. Последней командой обычно (но не всегда) является
инструкция break. Последний блок помечается ключевым словом default,
после которого стоит двоеточие. Это блок команд, выполняемых по
умолчанию. Инструкцию break в конце default-блока не ставят (в ней
просто нет смысла). Вся конструкция из case-блоков и default-блока (который, кстати, необязательный) заключается в фигурные скобки.
При выполнении оператора выбора значение данного выражения вычисляется и последовательно сравнивается со значениями, указанными в case-блоках (после ключевого слова case). Если найдено совпадение (имеется в виду совпадение значения выражения и контрольного
значения в case-блоке), начинается выполнение команд данного caseблока. Если ни в одном из case-блоков контрольное значение не совпадает со значением выражения, выполняются команды в default-блоке.
i


НА ЗАМЕТКУ
Блок с командами, выполняемыми по умолчанию (default-блок), не
является обязательным. Если такого блока в операторе выбора нет
и поиск совпадения контрольного значения и значения выражения не
увенчался успехом, то выполнение оператора выбора завершится.

Как отмечалось выше, обычно последней в case-блоке является инструкция break. Выполнение данной инструкции приводит к завершению работы оператора выбора. Дело в том, что если инструкцию break
в конце блока не разместить, то после выполнения команд данного
блока автоматически начнут выполняться команды следующего блока. Если в нем нет break-инструкции, будут выполняться команды еще

114

Глава 2. Управляющие инструкции

одного блока, и так далее до конца тела оператора. Если во всем этом
потребности нет, используют инструкцию break.
Общая схема выполнения оператора выбора проиллюстрирована на
рис. 2.16 (штрихованными линиями со стрелками показано «направление» выполнения кода при отсутствии инструкции break в case-блоке).

Рис. 2.16. Схема выполнения оператора выбора switch

Составить общее представление об операторе выбора нам поможет
небольшой пример.

Примеры использования оператора выбора
Мы рассмотрим два примера, в которых используется оператор выбора switch. Оба они достаточно простые. В первом примере, программный код которого представлен в листинге 2.11, описывается функция, основу которой составляет оператор выбора switch. У функции
три аргумента. Предполагается, что первые два аргумента являются
числовыми, а третий — текстовый. Текстовый аргумент определяет
арифметическую операцию (вычисление суммы, разности, произведения или частного), которую необходимо выполнить с первыми двумя аргументами функции. Результат отображается в документе. Для

115

Часть I. Основы JavaScript

«идентификации» типа операции по текстовому значению третьего
аргумента функции и используется оператор switch. Теперь проанализируем программный код.

 Листинг 2.11. Оператор выбора switch (файл Listing02_11.js)
// Ôóíêöèÿ ñ îïåðàòîðîì âûáîðà:
function show(x,y,op){
// Ëîêàëüíàÿ ïåðåìåííàÿ:
var msg
// Ïðîâåðÿåòñÿ çíà÷åíèå àðãóìåíòà:
switch(op){
case "ñóììà":

// Ïåðâûé áëîê

msg=x+" + "+ y+" = "+(x+y)+""
break
case "ðàçíîñòü": // Âòîðîé áëîê
msg=x+" - "+ y+" = "+(x-y)+""
break
case "ïðîèçâåäåíèå": // Òðåòèé áëîê
msg=x+" * "+ y+" = "+(x*y)+""
break
case "÷àñòíîå":

// ×åòâåðòûé áëîê

msg=x+" / "+ y+" = "+(x/y)+""
break
default:

// Ïî óìîë÷àíèþ

msg=""+op+" - íåèçâåñòíàÿ îïåðàöèÿ"
}
// Îòîáðàæåíèå òåêñòà â äîêóìåíòå:
document.write(msg)
} // Îêîí÷àíèå îïèñàíèÿ ôóíêöèè
// Âûçîâ ôóíêöèè ñ ðàçíûìè àðãóìåíòàìè:
show(8,4,"ïðîèçâåäåíèå")
show(8,4,"ñóììà")
show(8,4,"÷àñòíîå")
show(8,4,"ðàçíîñòü")
show(8,4,"ïîäìèãèâàíèå")

116

Глава 2. Управляющие инструкции

Функция называется show(). Ее первые два аргумента обозначены как
x и y. Это операнды некоторого выражения. Нужно только определить
оператор. Оператор определяется текстовым значением третьего аргумента op функции show(). Другими словами, в функции по значению
операнда op нужно выяснить, какую операцию следует выполнить
с аргументами x и y. И здесь нам поможет оператор выбора switch.
В операторе switch проверяется значение выражения op — то есть значение третьего аргумента функции show(). В case-блоках представлены
контрольные значения «сумма», «разность», «произведение» и «частное». Также есть default-блок на случай, если значение аргумента op не
совпадет ни с одним из контрольных значений в case-блоках. В каждом
блоке в соответствии с типом выполняемой операции присваивается
значение переменной msg. После завершения выполнения оператора
выбора командой document.write(msg) значение переменной msg отображается в рабочем документе.



Д Е ТА Л И
Переменная msg объявлена в теле функции. Это локальная переменная. Она доступна только в теле функции. За пределами программного кода функции show() об этой переменной ничего не известно.
Значение переменной msg формируется исходя из выполняемой арифметической операции. Более конкретно, в переменную записывается
символьное выражение для операции и ее результат. Например, командой msg=x+" + "+ y+" = "+(x+y)+"" в первом case-блоке в переменную msg
записывается текстовая строка, которая получается объединением
значения аргумента x, оператора сложения " + ", значения аргумента y,
знака равенства " = ", результата вычисления суммы операндов (x+y)
и инструкции перехода к новой строке "". Скобки при вычислении
суммы операндов нужны для того, чтобы выполнялось именно сложение чисел, а не конкатенация их текстовых представлений.
Конечно, описанную выше процедуру можно было реализовать более эффектно, воспользовавшись, например, функцией eval(), которая, напомним, позволяет вычислять значение выражений, «спрятанных» в текстовой строке. Но в рассматриваемом примере это не
столь принципиально.
Также стоит заметить, что функция show() не возвращает результат.
Она просто выполняет некоторые действия. Подробнее функции
обсуждаются в следующей главе.

После того как функция show() описана в сценарии, она несколько раз
вызывается с разными аргументами (фактически меняется значение

117

Часть I. Основы JavaScript

только третьего аргумента функции). Чтобы протестировать сценарий, используем веб-документ со следующим HTML-кодом:



Ëèñòèíã 2.11

Ëèñòèíã 2.11






На рис. 2.17 показано, как будет выглядеть данный документ в окне
браузера.

Рис. 2.17. Документ со сценарием, содержащим оператор выбора switch

В следующем примере также используется оператор выбора. Но на
сей раз некоторые case-блоки оператора пустые. Чтобы понять причину, сначала сформулируем суть задачи, которую мы пытаемся решить.

118

Глава 2. Управляющие инструкции

Создается сценарий, в котором генерируется последовательность целых случайных чисел в диапазоне значений от 6 до 15 включительно.
После того как очередное число сгенерировано, оно проверяется на
предмет того, простое это число или нет, является ли оно совершенным и делится ли оно на пять.



Д Е ТА Л И
Простыми называются числа, которые не имеют никаких иных делителей, кроме единицы и самого себя. В диапазоне чисел от 6 до
15 простыми являются числа 7, 11 и 13.
Совершенным называется число, сумма делителей которого (не
учитывая самого числа) равняется данному числу. Так, например,
число 6 является совершенным, поскольку оно делится на числа
1, 2 и 3, суммакоторых равняется 6. Других совершенных чисел
в диапазоне значений от 6 до 15 нет (ближайшее совершенное число 28).
Наконец, в диапазоне значений от 6 до 15 на пять без остатка делятся только числа 10 и 15.

В зависимости от того, попадает ли сгенерированное число в одну
из указанных категорий, в рабочем документе выводится поясняющее сообщение (с указанием числа и комментарием относительно его
свойств). Сортировка чисел реализуется с помощью оператора выбора switch. Проверяемым выражением является значение случайного
числа. Но пикантность ситуации в том, что для нескольких разных
значений числа нужно выполнять одни и те же действия. Например,
числа 7, 11 и 13 относятся к простым. Тогда соответствующие таким
контрольным значениям case-блоки должны были бы содержать одинаковые команды. Вместо создания нескольких формально одинаковых (за исключением контрольного значения) case-блоков используем
пустые case-блоки. Как именно реализуется данный подход, показано
в сценарии в листинге 2.12.

 Листинг 2.12. Оператор выбора switch с пустыми case-блоками
(файл Listing02_12.js)

document.write("Ñëó÷àéíûå ÷èñëà")
var rnd,msg
// Îïåðàòîð öèêëà, â êîòîðîì ãåíåðèðóþòñÿ öåëûå
// ñëó÷àéíûå ÷èñëà â äèàïàçîíå îò 6 äî 15 âêëþ÷èòåëüíî:
for(var k=1,n=20;k












































200

Глава 4. Знакомство с объектами и принципы ООП












216

Глава 4. Знакомство с объектами и принципы ООП










êðàñíûé | number -> 123 |
A: color -> êðàñíûé | number -> 123 |
B: color -> êðàñíûé | number -> 123 |
X: color -> êðàñíûé | number -> 123 |
A: color -> æåëòûé | number -> 321 |
B: color -> çåëåíûé | number -> 123 |
X: color -> êðàñíûé | number -> 123 | name -> ïðîòîòèï |
A: color -> æåëòûé | number -> 321 | state -> true | name -> ïðîòîòèï |
B: color -> çåëåíûé | number -> 123 | name -> ïðîòîòèï |
X: color -> êðàñíûé | name -> ïðîòîòèï |
A: number -> 321 | state -> true | color -> êðàñíûé | name -> ïðîòîòèï |
B: color -> çåëåíûé | name -> ïðîòîòèï |
Итак, в сценарии создается объект X, который мы планируем использовать в качестве прототипа при создании двух других объектов (мы
их создадим и назовем A и B). В объекте X описано свойство color со
значением "êðàñíûé" и свойство number со значением 123. Также в объекте
описан метод show(). Этим методом при вызове отображаются названия и значения всех свойств объекта, за исключением метода show().
i


НА ЗАМЕТКУ
Напомним, что технически метод реализуется как свойство, значением которого является функция. При вызове метода show() отображаются значения всех свойств объекта, за исключением свойства
show. То есть себя метод «игнорирует».

У метода show() один аргумент (обозначен как arg). Предполагается, что
аргумент текстовый. Через этот аргумент мы планируем передавать

233

Часть II. JavaScript и ООП

в метод название объекта, для которого отображается набор свойств.
Делается это исключительно для большей наглядности. Командой
document.write(""+arg+": ") в теле метода значение аргумента (название
объекта) отображается в рабочем документе с применением жирного
шрифта. Затем запускается оператор цикла for-in, в котором переменная s перебирает свойства объекта, из которого вызывается метод.
Для идентификации объекта, из которого вызывается метод, используем ключевое слово this. В теле оператора цикла всего одна команда
document.write(s+" -> "+this[s]+" | "), которой отображается название свойства
и его значение.



Д Е ТА Л И
Командой document.write(s+" -> "+this[s]+" | ") пары свойство/значение,
разделенные стрелкой, отображаются в одну строку. Разделителем между парами с названием и значением свойства служит
вертикальная черта. Название свойства содержится в качестве
значения в переменной s. Чтобы по названию свойства получить
значение свойства, используем инструкцию this[s]. Данная инструкция означает буквально следующее: свойство с названием,
записанным в переменную s, для объекта, из которого вызывается метод.

Но сама эта команда помещена внутрь условного оператора с проверяемым условием s!="show". Условие истинно, если название свойства (значение переменной s) не совпадает с названием метода
show() (методу, как отмечалось, соответствует свойство show). Таким
образом, команда по отображению названий и значений свойств
выполняется для всех свойств, кроме метода show(), чего мы и добивались. По завершении выполнения оператора цикла командой
document.write("") в веб-документ вставляется инструкция разрыва
строки .
На основе объекта X создаются объекты A и B. Для них объект X служит прототипом. Создаются объекты командами var A=Object.create(X)
и var B=Object.create(X) соответственно. Сразу после создания объектов
проверяются значения их свойств. Для этого вызывается функция
showAll(), которая не возвращает результат и у которой нет аргументов.
Сама функция описана в конце сценария, и ее код состоит из команд
вызова метода show() из объектов X, A и B. В результате для всех трех
объектов отображаются названия и значения их свойств.

234

Глава 4. Знакомство с объектами и принципы ООП



Д Е ТА Л И
В теле функции showAll() последовательно выполняются команды
X.show("X") (отображение свойств объекта-прототипа), A.show("A") (отображение свойств первого объекта) и B.show("B") (отображение свойств
второго объекта), после чего командой document.write("") в рабочий документ добавляется дескриптор вставки горизонтальной линии.
Вообще функция showAll() имеет вспомогательный характер и призвана уменьшить объем программного кода. Дело в том, что по
ходу сценария после внесения изменений в структуру и свойства
объектов каждый раз выполняется проверка свойств всех трех объектов (X, A и B). Чтобы каждый раз не вызывать в явном виде метод
show() для всех трех объектов, мы поместили команды вызова метода в отдельную функцию.

При первой проверке свойств объектов X, A и B оказывается, что у них
не только одинаковый набор свойств, но и значения этих свойств
совпадают (первый блок из трех строчек в сообщениях, выводимых
в рабочий документ сценарием). Это не случайно. Дело в том, что оба
объекта A и B созданы на основе объекта X, который стал их прототипом. Никакие дополнительные свойства (кроме тех, что описаны
в объекте X) в объектах A и B не объявлялись. Поэтому когда выполняется обращение к свойству color или number объекта A или B, то на самом
деле значение свойства считывается из прототипа, коим является
объект X. Здесь фактически срабатывает правило, что если у объекта
собственного свойства с определенным именем нет, то поиск такого
свойства выполняется в объекте-прототипе.
i


НА ЗАМЕТКУ
Стоит заметить, что в операторе for-in перебираются не только собственные свойства объекта, но и свойства, унаследованные им из
прототипа.

Далее командами A.color="æåëòûé" и A.number=321 свойствам color и number объекта A присваиваются новые значения. Но это так кажется со стороны,
что значения «новые». На самом деле у объекта появляются собственные свойства color и number, и указанные свойства получают значения
соответственно "æåëòûé" и 321. Очень важное обстоятельство: значения
полей color и number объекта X в данной ситуации совершенно не меняются.

235

Часть II. JavaScript и ООП

У объекта B меняется только значение свойства color (команда
B.color="çåëåíûé"). Свойство number объекта B мы не трогаем (оно остается,
каким было). Что это означает? У объекта B появляется свое собственное свойство color, а при обращении к свойству number объекта B будет
вступать в игру, как и ранее, объект-прототип X. Более конкретно, когда обращение (для считывания значения) выполняется к свойствам
color и number объекта A, то, поскольку у объекта A теперь есть собственные свойства с такими названиями, они и будут задействованы. При
обращении к свойствам color и number объекта B свойство color используется собственное, а значение свойства number (поскольку у объекта B
нет такого собственного свойства) считывается из объекта-прототипа X. Сказанное подтверждается вызовом функции showAll(): во втором
блоке из трех строк для результата сценария значения свойств объекта X остались неизменны, у объекта A изменились значения обоих
свойств, а у объекта B изменилось лишь значение свойства color.
На следующем этапе в объект прототипа X добавляется свойство name,
и этому свойству присваивается значение "ïðîòîòèï". Добавляется новое свойство в прототип просто: выполняется команда присваивания
значения свойству X.name="ïðîòîòèï".
Помимо добавления нового свойства в прототип, объекту A добавляется свойство state со значением true (команда A.state=true). После добавления свойства name в объект-прототип X и свойства state в объект
A вызываем функцию showAll() для проверки значений свойств объектов X, A и B. Результат проверки такой (третий блок из трех строк для
результата сценария).


У всех объектов появилось свойство name со значением "ïðîòîòèï".



У объекта A появилось свойство state со значением true.

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


НА ЗАМЕТКУ
Технически свойство name появляется у объекта X. У объектов A и B
такого собственного свойства нет. Но поскольку объект X является прототипом объектов A и B, при обращении к свойству name этих
объектов на самом деле возвращается значение свойства name
объекта X.

236

Глава 4. Знакомство с объектами и принципы ООП

Добавление нового свойства в обычный объект (не прототип) приводит к появлению данного свойства у объекта и никак не влияет на
свойства других объектов.
Наконец, командой delete X.number у объекта-прототипа удаляется
свойство number. Еще командой delete A.color у объекта A удаляется свойство color. После выполнения данных команд вызывается функция
showAll(). Каков будет результат? Вкратце ситуация такая (последние
три строки в сообщениях, которые выводятся при выполнении сценария).


У объектов X и B свойство number пропадает.



У объекта A свойство number остается.



Несмотря на удаление свойства color у объекта A, color в списке
свойств объекта присутствует, но значение данного свойства изменилось — теперь значение у свойства color объекта A такое же, как
и значение свойства color у объекта X.

Разберем данную ситуацию по пунктам. Итак, почему пропадает
свойство number у объекта X, в принципе понятно: оно удаляется из этого объекта, и поэтому его там больше нет.
У объекта B свойство number пропадает, поскольку собственного свойства с таким названием у объекта до этого не было и объект B «получал» значение свойства number из своего прототипа — то есть из объекта X. После удаления у объекта X свойства number оно естественным
образом «исчезает» из объекта B.
У объекта A было собственное свойство number. Поэтому при обращении к свойству number объекта A используется значение собственного
свойства number. Свойство number объекта-прототипа X в этом процессе
не участвует. Поэтому удаление свойства number у объекта X никак на
собственном свойстве number объекта A не сказывается.
Со свойством color объекта A ситуация более интересная. До удаления
свойства color из объекта A у него было собственное свойство с таким
именем. При удалении свойства color из объекта A удаляется именно
собственное свойство. Но остается свойство color у объекта-прототипа X. Поэтому, если после удаления свойства color из объекта A выполнить обращение к этому «удаленному» свойству, будет считано
значение свойства color объекта-прототипа X. Что, собственно, и происходит.

237

Часть II. JavaScript и ООП
i


НА ЗАМЕТКУ
Важное обстоятельство, на которое хочется обратить особое внимание, относится к методу show(), которым отображаются свойства
объектов и который в сценарии вызывается при вызове функции
showAll(). Метод show() описан в объекте X. При вызове из объекта A или
B метода show() на самом деле используется свойство show из объектапрототипа X, поскольку в объектах A и B собственного свойства show
нет. Тем не менее при вызове метода show() из объекта A или B отображаются свойства объекта, из которого формально вызывается метод (соответственно объекта A или B), а не свойства объекта X.

Получение доступа к прототипу
В рассмотренном выше примере при создании объектов прототип
указывался в явном виде. Если при создании объекта прототип явно
не указан, он все равно существует. Естественно, возникает вопрос:
как получить доступ к объекту, который является прототипом данного объекта? Ответ состоит в том, что получить ссылку на прототип
объекта можно, если воспользоваться методом getPrototypeOf() объекта
Object. Аргументом методу getPrototypeOf() передается объект, к прототипу которого необходимо получить доступ. Результатом возвращается
ссылка на объект, являющийся прототипом объекта, переданного аргументом методу getPrototypeOf().
i


НА ЗАМЕТКУ
Напомним, что у конструктора объектов Object есть свойство prototype,
которое позволяет получить доступ к прототипу верхнего уровня.
Соответствующая инструкция имеет вид Object.prototype. У объекта
Object.prototype нет прототипа. Поэтому, если попытаться получить прототип объекта Object.prototype с помощью команды Object.getPrototypeOf(Object.
prototype), получим значение null. Это пустая ссылка — то есть ссылка,
которая не указывает ни на какой объект.

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

238

Глава 4. Знакомство с объектами и принципы ООП

 Листинг 4.12. Добавление свойств в прототип верхнего уровня
(файл Listing04_12.js)

// Âñïîìîãàòåëüíàÿ òåêñòîâàÿ ïåðåìåííàÿ:
var txt='"name" in Math'
// Ïðîâåðêà íàëè÷èÿ ñâîéñòâà name ó îáúåêòà Math:
document.write(txt+" -> "+eval(txt)+"")
// Ïåðâûé îáúåêò:
var A={color:"êðàñíûé"}
// Âòîðîé îáúåêò:
var B=new Object()
// Äîáàâëåíèå ñâîéñòâà number â îáúåêò B:
B.number=100
// Îòîáðàæàþòñÿ ñâîéñòâà îáúåêòîâ:
showAll()
// Äîáàâëÿåòñÿ ñâîéñòâî name â ïðîòîòèï Object.prototype:
Object.prototype.name="îáúåêò À"
// Ïðîâåðêà íàëè÷èÿ ñâîéñòâà name ó îáúåêòà Math:
document.write(txt+" -> "+eval(txt)+"")
// Îòîáðàæàþòñÿ ñâîéñòâà îáúåêòîâ:
showAll()
// Äîáàâëÿåòñÿ ñâîéñòâî name â îáúåêò B:
B.name="îáúåêò B"
// Îòîáðàæàåòñÿ çíà÷åíèå ñâîéñòâà name îáúåêòà Math:
document.write("Math.name -> "+Math.name+"")
// Îòîáðàæàþòñÿ ñâîéñòâà îáúåêòîâ:
showAll()
// Óäàëåíèå ñâîéñòâà name ó ïðîòîòèïà Object.prototype:
delete Object.prototype.name
// Ïðîâåðêà íàëè÷èÿ ñâîéñòâà name ó îáúåêòà Math:
document.write(txt+" -> "+eval(txt)+"")
// Îòîáðàæàþòñÿ ñâîéñòâà îáúåêòîâ:
showAll()
// Ôóíêöèè äëÿ îòîáðàæåíèÿ ñâîéñòâ îáúåêòîâ:
function show(obj){
for(var s in obj){

239

Часть II. JavaScript и ООП

document.write(s+" -> "+obj[s]+" | ")
}
document.write("")
}
function showAll(){
document.write("Îáúåêò À: ")
show(A)
document.write("Îáúåêò B: ")
show(B)
document.write("")
}
В представленном сценарии командой var A={color:"êðàñíûé"} создается
объект A со свойством color, а также командой var B=new Object() создается
пустой объект B (здесь мы воспользовались «услугами» конструктора
объектов Object). После создания пустого объекта B командой B.number=100
в объект B добавляется свойство number.
На следующем этапе мы проверяем, какие свойства имеются у объектов A и B. Также в силу определенных причин нас интересует вопрос
о наличии у встроенного объекта Math (напомним, что данный встроенный объект используется при работе с математическими функциями) свойства с названием name.
Для получения ответа на этот вопрос мы используем оператор in. Точнее, мы определяем вспомогательную переменную txt со значением
'"name" in Math', которое представляет собой «упакованную» в текст команду проверки наличия у объекта Math свойства name.
i


НА ЗАМЕТКУ
Поскольку в тексте, присваиваемом значением переменной txt, использовано слово в двойных кавычках, то весь текст заключается
в одинарные кавычки.

Для проверки наличия свойства name у объекта Math используется команда document.write(txt+" -> "+eval(txt)+""). Для вычисления выражения,
содержащегося в текстовой строке, строку передаем аргументом функции eval(). Далее вызовом функции showAll() отображаются свойства
(и их значения) для объектов A и B.

240

Глава 4. Знакомство с объектами и принципы ООП



Д Е ТА Л И
Функция showAll() описана и используется исключительно из соображений удобства и экономии программного кода. В теле функции showAll()
вызывается функция show(), которой отображаются названия и значения свойств объекта, переданного аргументом функции. Обе функции описаны в конце сценария. Подобные программные коды мы уже
рассматривали, и поэтому анализировать их нет особого смысла.

Функция showAll() вызывается в сценарии в общей сложности четыре раза, поэтому результат выполнения сценария, представленный
ниже, состоит из четырех блоков (каждый блок — по три строки).

 Результат выполнения сценария (из листинга 4.12)
"name" in Math -> false
Îáúåêò À: color -> êðàñíûé |
Îáúåêò B: number -> 100 |

"name" in Math -> true
Îáúåêò À: color -> êðàñíûé | name -> îáúåêò À |
Îáúåêò B: number -> 100 | name -> îáúåêò À |
Math.name -> îáúåêò À
Îáúåêò À: color -> êðàñíûé | name -> îáúåêò À |
Îáúåêò B: number -> 100 | name -> îáúåêò B |
"name" in Math -> false
Îáúåêò À: color -> êðàñíûé |
Îáúåêò B: number -> 100 | name -> îáúåêò B |
В первом блоке первая строка свидетельствует о том, что свойства name
у объекта Math нет, что вполне ожидаемо. Следующие две строки в первом блоке дают представление о свойствах объектов A и B соответственно (у объекта A есть свойство color, а у объекта B есть свойство number).
Далее, после вызова функции showAll(), выполняется команда Object.
prototype.name="îáúåêò À". Этой командой прототипу верхнего уровня
Object.prototype добавляется свойство name, и ему присваивается значение "îáúåêò À". После добавления свойства name в прототип Object.prototype

241

Часть II. JavaScript и ООП

выполняются команды document.write(txt+" -> "+eval(txt)+"") (проверка наличия свойства name у объекта Math) и showAll() (отображение названий
и значений свойств объектов A и B). Результат выполнения команд
(три строки во втором блоке) свидетельствует о том, что у объекта Math
появилось свойство name, равно как и у объектов A и B. Значение свойства name для объектов A и B возвращается равным "îáúåêò À".
Объяснение данного факта базируется на том обстоятельстве, что при
создании объекта с помощью литерала (как в случае с объектом A) или
с помощью конструктора объектов Object (как в случае с объектом B)
прототипом является объект Object.prototype, то есть прототип наивысшего уровня. Поэтому вполне ожидаемо, что после добавления свойства name в прототип Object.prototype у объектов A и B появляется свойство
name. Здесь ситуация такая же, как и в рассмотренном ранее примере,
когда прототип объекта задавался в явном виде.
Что касается объекта Math, то, хотя он и встроенный, его прототипом
также является объект Object.prototype. Так что причины появления
свойства name у объекта Math те же, что и для объектов A и B.



Д Е ТА Л И
Даже если бы для рассмотренных объектов прототип отличался от
Object.prototype, результат был бы аналогичным (правда, при условии,
что прототип объектов является частью иерархии прототипов с объектом Object.prototype в вершине иерархии). Причина кроется в механизме получения значения свойства: если у объекта нет собственного свойства с заданным именем, поиск выполняется в прототипе,
затем в прототипе прототипа, и так далее — вплоть до объекта-прототипа высшего уровня, которым является Object.prototype.
Далее мы узнаем, что объект может быть создан совсем без прототипа. Добавление или удаление свойств в объект-прототип Object.
prototype на объектах, созданных без прототипов, не сказывается.

После добавления в объект B собственного свойства name (команда
B.name="îáúåêò B") обращение к свойству name для объектов A и Math означает
обращение к свойству name объекта-прототипа Object.prototype, а вот при обращении к свойству name объекта B используется собственное свойство
данного объекта. В последнем легко убедиться, если проанализировать
последствия удаления свойства name из объекта-прототипа Object.prototype
(команда delete Object.prototype.name). А последствия такие: у объектов A и Math
свойства name больше нет, а у объекта B свойство name остается.

242

Глава 4. Знакомство с объектами и принципы ООП

Для тестирования сценария используем представленный ниже веб-код:



Ëèñòèíã 4.12

Ëèñòèíã 4.12






Результат выполнения сценария представлен на рис. 4.11.

Рис. 4.11. Результат выполнения сценария, в котором в прототип высшего
уровня добавляется свойство

243

Часть II. JavaScript и ООП

Еще один небольшой пример касается использования метода
getPrototypeOf() для определения прототипа объекта. Рассмотрим сценарий, представленный в листинге 4.13.

 Листинг 4.13. Получение доступа к прототипу с помощью метода
getPrototypeOf() (файл Listing04_13.js)

// Ïåðâûé îáúåêò (ñîçäàåòñÿ íà îñíîâå Math):
var A=Object.create(Math)
// Âòîðîé îáúåêò (ñîçäàåòñÿ íà îñíîâå A):
var B=Object.create(A)
// Òðåòèé îáúåêò (ñîçäàåòñÿ íà îñíîâå B):
var C=Object.create(B)
// ×åòâåðòûé îáúåêò (ñîçäàåòñÿ íà îñíîâå C):
var D=Object.create(C)
// Äîáàâëåíèå ìåòîäà f() â ïðîòîòèï îáúåêòà A:
Object.getPrototypeOf(A).f=function(x){
return 2*x+1
}
// Ïåðåìåííàÿ ñ öåëî÷èñëåííûì çíà÷åíèåì:
var t=2
// Âûçîâ ìåòîäà f() èç îáúåêòà Math:
document.write("Ôóíêöèÿ f("+t+") = "+Math.f(t)+"")
// Íîâîå çíà÷åíèå ïåðåìåííîé t:
t=3
// Âûçîâ ìåòîäà f() èç îáúåêòà D:
document.write("Ôóíêöèÿ f("+t+") = "+D.f(t)+"")
// Ññûëêà íà ïðîòîòèï ïðîòîòèïà îáúåêòà D:
var obj=Object.getPrototypeOf(Object.getPrototypeOf(D))
// Ïðîâåðêà ðàâåíñòâà îáúåêòîâ:
document.write("obj == B -> "+(obj==B)+"")
В сценарии с помощью метода create() создаются четыре объекта. Объект A
создается на основе прототипа Math (встроенный класс). Объект A, в свою
очередь, служит прототипом для объекта B. На основе объекта B создается объект C, который является прототипом для объекта D.

244

Глава 4. Знакомство с объектами и принципы ООП

Командой Object.getPrototypeOf(A).f=function(x){return 2*x+1} в прототип объекта A
(то есть фактически в объект Math) добавляется свойство f, значением
которого является функция с одним аргументом (обозначен как x),
возвращающая результатом значение 2*x+1. Проще говоря, в объект
Math добавляется метод f().
i


НА ЗАМЕТКУ
С помощью метода f() определяется математическая функция
f(x) = 2x + 1.

Затем мы определяем переменную t, которая нужна для передачи
аргумента методу f(). Для вызова метода f() в сценарии используются
инструкции Math.f(t) и D.f(t). Если с инструкцией Math.f(t) все более-менее
понятно, то использование команды D.f(t) возможно благодаря тому,
что объект D наследует свойства и методы объекта Math через цепочку
объектов-прототипов.
Также с помощью команды var obj=Object.getPrototypeOf(Object.getPrototypeOf(D))
в переменную obj записывается ссылка на прототип, который является прототипом для объекта D (то есть ссылка на объект B).
Данное обстоятельство подтверждается при проверке значения выражения obj==B, которое равно true и означает, что ссылки в переменной
obj и переменной B указывают на один и тот же объект.
Результат выполнения сценария такой.

 Результат выполнения сценария (из листинга 4.13)
Ôóíêöèÿ f(2) = 5
Ôóíêöèÿ f(3) = 7
obj == B -> true
Чтобы проверить работу сценария, используем представленный ниже
HTML-код:



Ëèñòèíã 4.13

245

Часть II. JavaScript и ООП


Ëèñòèíã 4.13






На рис. 4.12 показано, как в окне браузера выглядит веб-документ, содержащий данный сценарий.

Рис. 4.12. Результат выполнения сценария, в котором использован метод
getPrototypeOf()

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



Д Е ТА Л И
У объекта Object есть метод setPrototypeOf(), который позволяет задавать прототип для объекта. Метод вызывается из объекта Object,
а аргументами ему передаются: ссылка на объект, для которого
задается прототип, и ссылка на объект, который является прототипом.

246

Глава 4. Знакомство с объектами и принципы ООП

Создание объектов без прототипа
Создать объект, у которого нет прототипа, исключительно просто.
Для этого достаточно указать аргументом метода create() пустую ссылку null. Например, командой вида var obj=Object.create(null) создается объект
obj, у которого нет прототипа. Некоторые особенности объектов, созданных без прототипа, иллюстрируются в примере, представленном
в листинге 4.14.

 Листинг 4.14. Создание объекта без прототипа (файл Listing04_14.js)
// Ïóñòîé îáúåêò ñ ïðîòîòèïîì:
var A={}
// Ïóñòîé îáúåêò áåç ïðîòîòèïà:
var B=Object.create(null)
// Ïóñòîé îáúåêò ñ ÿâíî óêàçàííûì ïðîòîòèïîì:
var C=Object.create(B)
// Äîáàâëåíèå ñâîéñòâà number â îáúåêò B:
B.number=100
// Äîáàâëåíèå ñâîéñòâà name â ïðîòîòèï âåðõíåãî óðîâíÿ:
Object.prototype.name="Îáúåêò À"
// Îòîáðàæåíèå ñâîéñòâ îáúåêòîâ:
show(A)
show(B)
show(C)
// Ôóíêöèÿ äëÿ îòîáðàæåíèÿ ñâîéñòâ îáúåêòà:
function show(obj){
for(var s in obj){
document.write(s+" -> "+obj[s])
}
document.write("")
}
В сценарии создаются три пустых объекта.


Объект A создается описанием литерала объекта (пустые фигурные скобки), поэтому по умолчанию его прототипом является
прототип высшего уровня Object.prototype.

247

Часть II. JavaScript и ООП



Объект B создается с помощью инструкции Object.create(null), в которой аргументом методу create() передана пустая ссылка null. Поэтому прототипа у объекта B нет.



Объект C создается (использована инструкция Object.create(B)) на основе прототипа, которым является объект B (у которого нет прототипа).

После создания объектов командой B.number=100 в объект B добавляется
свойство number, а командой Object.prototype.name="Îáúåêò À" в прототип верхнего уровня добавляется свойство name. Затем с помощью функции
show() отображаются названия и значения свойств объектов A, B и C.



Д Е ТА Л И
Функция show() описана в конце сценария. Аргументом функции
передается объект. Функция не возвращает результат, а при вызове функции отображаются названия и значения свойств объекта, переданного аргументом функции. Код функции должен быть
знаком и понятен читателю, поэтому нет смысла его комментировать.

Результат выполнения сценария представлен ниже.

 Результат выполнения сценария (из листинга 4.14)
name -> Îáúåêò À
number -> 100
number -> 100
Поскольку собственных свойств у объекта A нет, а в прототип Object.
prototype добавлено свойство name, то только это свойство отождествляется с объектом A. У объекта B нет прототипа, поэтому наличие свойства name у прототипа верхнего уровня Object.prototype на объекте B никак
не сказывается. Отсюда получается, что свойства name у объекта B нет.
Зато у объекта B есть собственное свойство number. Наконец, такое же
свойство number (а если точнее, то это, буквально, то же самое свойство)
имеется у объекта C. Хотя собственных свойств у объекта нет, но его
прототипом является объект B, у которого есть данное свойство. На
объекте B цепочка прототипов обрывается. Как следствие, прототип
верхнего уровня Object.prototype в объекте C не наследуется, и свойства
name у объекта C нет.

248

Глава 4. Знакомство с объектами и принципы ООП

Представленный ниже HTML-код используем для тестирования работы сценария:



Ëèñòèíã 4.14

Ëèñòèíã 4.14






Как будет выглядеть окно браузера с веб-документом, в котором выполняется рассмотренный нами сценарий, показано на рис. 4.13.

Рис. 4.13. Результат выполнения сценария, в котором создается объект без
прототипа

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

249

Часть II. JavaScript и ООП

Конструкторы и прототипы
Мы уже знаем, что для создания объектов удобно описать специальную функцию, которая называется конструктором и которую (с оператором new) вызывают для создания объектов. Очевидное удобство
конструктора связано с тем, что объекты создаются унифицированные, с одинаковым набором свойств (обычно). Но есть еще одно важное
обстоятельство: все объекты, созданные с помощью некоторого конструктора, имеют один общий прототип. Данный факт означает, что уже
после создания объектов мы можем добавлять и удалять свойства в эти
объекты путем выполнения соответствующей операции с прототипом
объектов. Доступ к прототипу объектов, которые создаются с помощью
конструктора, получаем с помощью свойства prototype конструктора.



Д Е ТА Л И
Если речь идет о некотором Êîíñòðóêòîðå, то объект создается посредством инструкции вида new Êîíñòðóêòîð(àðãóìåíòû), а для получения
доступа к прототипу объектов, которые создаются с помощью Êîíñòðóêòîðà, используют инструкцию формата Êîíñòðóêòîð.prototype.

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

 Листинг 4.15. Конструкторы и прототипы (файл Listing04_15.js)
// Êîíñòðóêòîð îáúåêòîâ:
function MyObject(name,number){
this.name=name
this.number=number
this.show=function(){
for(var s in this){
if(s!="show"){
document.write(s+" -> "+this[s]+" | ")
}
}
document.write("")
}
}

250

Глава 4. Знакомство с объектами и принципы ООП

// Ñîçäàíèå îáúåêòîâ íà îñíîâå êîíñòðóêòîðà:
var A=new MyObject("Îáúåêò À",100)
var B=new MyObject("Îáúåêò B",200)
// Îòîáðàæåíèå ñâîéñòâ îáúåêòîâ:
document.write("Ñâîéñòâà ñîçäàííûõ îáúåêòîâ:")
A.show()
B.show()
// Äîáàâëåíèå ñâîéñòâà color â ïðîòîòèï îáúåêòîâ:
MyObject.prototype.color="ïðîçðà÷íûé"
// Îòîáðàæåíèå ñâîéñòâ îáúåêòîâ:
document.write("Ïîñëå äîáàâëåíèÿ ñâîéñòâà:")
A.show()
B.show()
// Äîáàâëåíèå ñîáñòâåííîãî ñâîéñòâà color â îáúåêò A:
A.color="áåëûé"
// Óäàëåíèå ñâîéñòâà color èç ïðîòîòèïà îáúåêòîâ:
delete MyObject.prototype.color
// Îòîáðàæåíèå ñâîéñòâ îáúåêòîâ:
document.write("Ïîñëå óäàëåíèÿ ñâîéñòâà:")
A.show()
B.show()
// Ïðîâåðêà ïðîòîòèïà îáúåêòà:
document.write("Ïðîâåðêà ïðîòîòèïà îáúåêòà:")
var txt="Object.getPrototypeOf(A)==MyObject.prototype"
document.write(txt+" -> "+eval(txt))
В сценарии описывается конструктор MyObject, которым создаются
объекты с двумя свойствами и методом show(), предназначенным для
отображения названий и значений свойств (за исключением названия и значения свойства, соответствующего методу). Конструктору
передаются два аргумента, которые определяют значения свойств
name и number создаваемого объекта.
i


НА ЗАМЕТКУ
Свойства, которые получает создаваемый с помощью конструктора объект, являются собственными свойствами данного
объекта.

251

Часть II. JavaScript и ООП

Объекты A и B создаются командами A=new MyObject("Îáúåêò À",100) и B=new My
Object("Îáúåêò B",200). То есть объекты создаются с помощью конструктора
MyObject (с передачей аргументов). Это означает, что через конструктор
MyObject можно получить доступ к прототипам объектов, созданных
на его основе, что мы, собственно, и делаем. В частности, командой
MyObject.prototype.color="ïðîçðà÷íûé" в прототип объектов добавляется свойство color со значением "ïðîçðà÷íûé". После выполнения данной операции
у прототипа объектов A и B появляется свойство color, и, как следствие,
оно отождествляется и с данными объектами (по схеме определения
свойств объектов). После проверки свойств объектов A и B в объект A
добавляется собственное свойство color со значением "áåëûé", а из
прототипа MyObject.prototype данное свойство удаляется. В результате
у объек та B свойства color (унаследованного из прототипа) больше нет,
а у объек та A остается собственное свойство color.
В описанной схеме ничего особенного нет. Нечто похожее мы наблюдали и ранее. Специфика ситуации связана с тем, что доступ
к прототипам объектов мы получаем через их конструктор. Также следует понимать, что никто не запрещает нам получить доступ
к прототипу объекта с помощью метода getPrototypeOf(). Например, командой Object.getPrototypeOf(A) возвращается ссылка на объект, являющийся прототипом объекта A. Несложно догадаться, что речь идет
о том же самом объекте, что и при использовании инструкции MyObject.
prototype. Поэтому в сценарии при проверке значения выражения Object.
getPrototypeOf(A)==MyObject.prototype получаем значение true. В итоге результат выполнения сценария будет следующим.

 Результат выполнения сценария (из листинга 4.15)
Ñâîéñòâà ñîçäàííûõ îáúåêòîâ:
name -> Îáúåêò À | number -> 100 |
name -> Îáúåêò B | number -> 200 |
Ïîñëå äîáàâëåíèÿ ñâîéñòâà:
name -> Îáúåêò À | number -> 100 | color -> ïðîçðà÷íûé |
name -> Îáúåêò B | number -> 200 | color -> ïðîçðà÷íûé |
Ïîñëå óäàëåíèÿ ñâîéñòâà:
name -> Îáúåêò À | number -> 100 | color -> áåëûé |
name -> Îáúåêò B | number -> 200 |
Ïðîâåðêà ïðîòîòèïà îáúåêòà:
Object.getPrototypeOf(A)==MyObject.prototype -> true

252

Глава 4. Знакомство с объектами и принципы ООП

Для включения сценария в веб-документ используем следующий
HTML-код:



Ëèñòèíã 4.15

Ëèñòèíã 4.15






Окно браузера с веб-документом, в котором выполняется описанный
выше сценарий, представлено на рис. 4.14.

Рис. 4.14. Результат выполнения сценария, в котором используется прототип
объектов, создаваемых на основе конструктора

253

Часть II. JavaScript и ООП



Д Е ТА Л И
Выше речь шла о прототипе объектов, которые создаются с помощью конструктора MyObject. Но конструктор MyObject сам является объектом, и у него есть прототип. И это совсем не тот прототип, который у объектов, создаваемых с помощью конструктора. Другими
словами, объекты MyObject.prototype (прототип объектов, создаваемых
с помощью конструктора) и Object.getPrototypeOf(MyObject) (прототип конструктора) являются разными.

Выше мы рассматривали ситуацию, когда через конструктор получали доступ к прототипу, на основе которого создаются объекты. Ситуация может быть несколько иной, когда по объекту необходимо определить конструктор, который позволяет создавать объекты с таким же
прототипом, как у данного. Чтобы через объект получить ссылку на
его конструктор, используется свойство constructor объекта. В листинге 4.16 приведен программный код сценария, в котором используется
означенный подход для получения доступа к конструктору объекта.

 Листинг 4.16. Определение конструктора по объекту
(файл Listing04_16.js)

// Êîíñòðóêòîð:
function MyObj(){
this.number=0
}
// Îáúåêò:
var A=new MyObj()
// Îïðåäåëåíèå êîíñòðóêòîðà ÷åðåç îáúåêò:
var F=A.constructor
// Ñîçäàíèå îáúåêòà:
var B=new F()
// Ïðîâåðêà çíà÷åíèÿ ñâîéñòâà number îáúåêòà:
document.write("Ñâîéñòâî B.number = "+B.number+"")
// Íîâûé îáúåêò:
A={}
// Îïðåäåëåíèå êîíñòðóêòîðà:
F=A.constructor

254

Глава 4. Знакомство с объектами и принципы ООП

// Ïðîâåðêà îáúåêòà êîíñòðóêòîðà:
document.write("F==Object -> "+(F==Object)+"")
// Ñîçäàíèå îáúåêòà ñ ïîìîùüþ àíîíèìíîãî êîíñòðóêòîðà:
A=new function(){
this.name="îáúåêò"
}()
// Îïðåäåëåíèå êîíñòðóêòîðà:
F=A.constructor
// Ñîçäàíèå îáúåêòà:
B=new F()
// Ïðîâåðêà çíà÷åíèÿ ñâîéñòâà name îáúåêòà:
document.write("Ñâîéñòâî B.name = "+B.name)
В сценарии рассматривается три способа создания объектов: с помощью явно описанного конструктора, с помощью литерала и с помощью анонимного конструктора (это когда функция-конструктор
является анонимной). В каждом из этих трех случаев с помощью
свойства constructor, созданного тем или иным способом объекта, получаем доступ к конструктору и для проверки создаем с помощью «выловленного» таким способом конструктора еще один объект.
Итак, в сценарии описывается конструктор MyObj, в теле которого всего одна команда this.number=0. Таким образом, все создаваемые на основе данного конструктора объекты получают поле number с нулевым
значением.
i


НА ЗАМЕТКУ
Поле number является собственным полем объекта.

Далее с помощью конструктора MyObj создается объект, для чего используется инструкция new MyObj() и ее результат записывается в переменную A. В переменную F записывается значение выражения
A.constructor. Последнее представляет собой ссылку на конструктор, которым создавался объект A (то есть речь идет о конструкторе MyObj).
Поэтому переменную F можно использовать так, как если бы это был
конструктор (что в общем-то так и есть). Новый объект создается инструкцией new F(), а ее результат записывается в переменную B. Факти-

255

Часть II. JavaScript и ООП

чески данный объект создан с помощью конструктора MyObj, поэтому
у объекта B есть свойство number. Значение этого свойства проверяется
в сценарии.
Новый пустой объект создается командой A={}. Доступ к конструктору объекта получаем с помощью команды F=A.constructor. При проверке
условие F==Object оказывается, что оно истинно.
Истинность условия F==Object означает, что для объекта A, созданного
описанием литерала, конструктором является объект Object.
Наконец, еще один объект создается с использованием анонимного
конструктора. Переменной A присваивается инструкция, состоящая
из ключевого слова new и вызова анонимной функции. Вся конструкция выглядит так:
A=new function(){
this.name="îáúåêò"
}()
Непосредственно код анонимной функции следующий:
function(){
this.name="îáúåêò"
}
Круглые скобки после описания анонимной функции в команде создания объекта означают, что она вызывается. В теле функции в силу
наличия команды this.name="îáúåêò" создаваемый объект получает собственное свойство name со значением "îáúåêò". Но пикантность ситуации в том, что так как функция-конструктор анонимная, то она как
бы «одноразовая»: мы ее вызвали лишь один раз, а ссылку на нее
никуда не записали. Но функция все равно не «потеряется». О ней
«знает» объект, созданный описанным способом. Воспользовавшись
командой F=A.constructor, в переменную F записываем ссылку на функцию-конструктор, использованную при создании объекта, на который ссылается переменная A. Теперь можем с помощью этой функции
создать новый объект, что и делается командой B=new F(). Непосредственной проверкой убеждаемся, что у объекта B есть свойство name со
значением "îáúåêò".
Ниже представлен результат выполнения сценария.

256

Глава 4. Знакомство с объектами и принципы ООП

 Результат выполнения сценария (из листинга 4.16)
Ñâîéñòâî B.number = 0
F==Object -> true
Ñâîéñòâî B.name = îáúåêò
Для тестирования кода используем следующий веб-документ:



Ëèñòèíã 4.16

Ëèñòèíã 4.16






На рис. 4.15 показано, как выглядит веб-документ с результатом выполнения сценария.

Рис. 4.15. Результат выполнения сценария, в котором по объекту определяется
его конструктор

257

Часть II. JavaScript и ООП

Далее мы рассмотрим некоторые аспекты, связанные с использованием свойств и методов объектов.

Свойства и методы
— Чего они от него хотят?
— Боятся за крайне хрупкую жизнь. Ступил
человек на скользкий путь…
— Ну, на то и лед, чтоб скользить.
из к/ф «Покровские ворота»

Со свойствами и методами мы уже имели дело многократно, но, как
будет показано далее, рассмотрели далеко не все темы, которые важны для понимания принципов их использования. Сейчас пришло
время восполнить пробелы в знаниях.

Перечисляемые и неперечисляемые свойства
В рассмотренных ранее примерах мы обращались к свойству constructor
объектов, которые создавали разными способами. Пикантность ситуации в том, что при создании объекта такое свойство в него не добавлялось, а если запустить оператор цикла for-in для перебора свойств
объекта, то данное свойство отображено не будет. Но вот если мы проверим наличие этого свойства с помощью оператора in, то результатом
будет значение true, что свидетельствует о наличии указанного свойства у объекта. Проще говоря, здесь мы сталкиваемся с ситуацией,
когда свойство есть, но оно как-то «замалчивается».
Ранее мы уже отмечали, что свойства объектов делятся на две группы: перечисляемые и неперечисляемые свойства. При переборе
свойств объекта с помощью оператора for-in обрабатываются только
перечисляемые свойства, а неперечисляемые игнорируются. Если
наличие свойства в объекте проверяется с помощью оператора in, то
в расчет принимаются как перечисляемые, так и неперечисляемые
свойства. По умолчанию свойства, добавляемые в объект непосредственно в сценарии, являются перечисляемыми, а многие свойства,
наследуемые из стандартных прототипов, являются неперечисляемыми. Хотя положение дел может быть изменено. Как иллюстрацию
к сказанному рассмотрим небольшой пример, в котором описанием
литерала создается объект, а затем объект проверяется на наличие

258

Глава 4. Знакомство с объектами и принципы ООП

в нем некоторых свойств. Программный код представлен в листинге 4.17.

 Листинг 4.17. Перечисляемые и неперечисляемые свойства объекта
(файл Listing04_17.js)

// Ñîçäàíèå îáúåêòà:
var A={number:100}
// Äîáàâëåíèå ñâîéñòâà â ïðîòîòèï:
Object.prototype.name="îáúåêò À"
for(var a in A){
document.write(a+" | ")
}
// Ïðîâåðêà íàëè÷èÿ ó îáúåêòà ñâîéñòâ:
test('"toString" in A')
test('"valueOf" in A')
test('"constructor" in A')
// Ôóíêöèÿ äëÿ ïðîâåðêè íàëè÷èÿ ñâîéñòâà ó îáúåêòà:
function test(txt){
document.write(""+txt+" -> "+eval(txt))
}
В сценарии для проверки наличия у объекта определенного свойства
описана и используется функция test(). Аргументом функции передается текстовое выражение, содержащее команду (предполагается,
что это команда проверки наличия у объекта свойства). При вызове
функции отображается команда (переданная в текстовом аргументе)
и результат ее выполнения.
В сценарии объявляется переменная A, и значением ей присваивается
объект со свойством number (команда var A={number:100}). Также командой
Object.prototype.name="îáúåêò À" в прототип верхнего уровня добавляется
свойство name. При переборе свойств объекта A с помощью оператора
цикла for-in мы увидим названия свойств number (собственное свойство
объекта A) и name (свойство объекта-прототипа). Но проверка объекта A
на предмет наличия в нем свойств toString, valueOf и constructor показывает,
что такие свойства у объекта тоже есть (собственно, первые два — это
методы toString() и valueOf()).

259

Часть II. JavaScript и ООП



Д Е ТА Л И
Со свойством constructor мы уже сталкивались — с его помощью получаем ссылку на объект-конструктор, которым создавался объект.
Методы toString() и valueOf() мы еще не обсуждали.
Метод toString() наследуется объектами из прототипа Object.prototype
и вызывается автоматически каждый раз, когда формат команды,
в которой использована ссылка на объект, подразумевает использование текстового значения (например, когда объект передается
аргументом методу write()).
Метод valueOf() также наследуется из прототипа Object.prototype и вызывается в тех случаях, когда объект должен преобразовываться
в числовое (или иное, отличное от текстового) значение. Методы
toString() иvalueOf() еще будут обсуждаться.

Результат выполнения сценария представлен ниже.

 Результат выполнения сценария (из листинга 4.17)
number | name |
"toString" in A -> true
"valueOf" in A -> true
"constructor" in A -> true
Для тестирования сценария полезным будет следующий HTML-код:



Ëèñòèíã 4.17

Ëèñòèíã 4.17







260

Глава 4. Знакомство с объектами и принципы ООП

Как выглядит в окне браузера документ с результатом выполнения
сценария, показано на рис. 4.16.

Рис. 4.16. Результат выполнения сценария, в котором проверяются
перечисляемые и неперечисляемые свойства объекта

Таким образом, часть свойств отображается при переборе свойств
объекта с помощью оператора for-in, а часть — нет. Критерий для дифференциации свойств — их «перечисляемойсть» или «неперечисляемость». Желательно иметь возможность как-то управлять этим процессом.
Далее мы рассмотрим подходы и методы, которые позволяют в той
или иной степени влиять на характеристики свойств (такие как, например, «перечисляемость»).
В JavaScript свойства объектов имеют определенные характеристики,
или атрибуты. В частности, для каждого свойства важны и поддерживаются на программном уровне следующие позиции.


Собственно, значение свойства.



Отображается ли свойство при переборе свойств объекта с помощью оператора for-in.



Можно ли свойству присвоить значение.



Можно ли удалить свойство.

Все перечисленные характеристики свойств допускается «регулировать» программными методами. Наиболее полное представление
о характеристиках свойства объекта дает объект, который называется дескриптором свойства.

261

Часть II. JavaScript и ООП
i


НА ЗАМЕТКУ
Другими словами, ситуация такая: имеется некоторое свойство
с определенными атрибутами. Каковы значения этих атрибутов —
определяется специальным объектом, который называется дескриптором свойства. У каждого свойства свой объект-дескриптор.

Объект-дескриптор по умолчанию имеет четыре свойства.


Свойство value содержит значение, которое является фактическим
значением свойства, описываемого объектом-дескриптором.



Свойство enumerable определяет, относится свойство, описываемое
дескриптором, к перечисляемым или к неперечисляемым. Свойство enumerable может принимать значение true (исходное свойство
перечисляемое) или false (исходное свойство неперечисляемое).



Свойство writable определяет, можно ли свойству, описываемому
дескриптором, присвоить новое значение. Если значение свойства
writable равно false, то исходное свойство фактически является константой — его значение изменить нельзя. Если значение свойства
writable равно true, то исходному свойству можно присвоить новое
значение.



Свойство configurable может принимать значение true или false. Если
значение свойства configurable равно false, то свойство, описываемое
дескриптором, нельзя удалить из объекта. Чтобы исходное свойство можно было удалить из объекта, в дескрипторе свойства значение свойства configurable должно равняться true.

i


НА ЗАМЕТКУ
Свойство configurable объекта-дескриптора «отвечает» не только за
возможность удалять описываемое дескриптором свойство, но
и за возможность изменения типа свойства (его характеристик).
Если свойство configurable объекта-дескриптора равняется false, то исходное свойство не только нельзя удалить, но еще и не получится
изменить значения его атрибутов.

Ссылку на объект-дескриптор получают с помощью метода
getOwnPropertyDescriptor() объекта Object. Аргументами методу передаются
ссылки на объект и его свойство, для которого определяется дескриптор. Например, если нам необходимо получить ссылку на объект-де-

262

Глава 4. Знакомство с объектами и принципы ООП

скриптор определенного ñâîéñòâà некоторого îáúåêòà, то соответствующая инструкция могла бы выглядеть как Object.getOwnPropertyDescriptor
(îáúåêò,ñâîéñòâî). Результатом данного выражения является ссылка на
объект-дескриптор.
i


НА ЗАМЕТКУ
С помощью метода getOwnPropertyDescriptor() можно получить ссылку на
дескриптор только для собственного свойства объекта. Для получения дескрипторов для свойств, наследуемых объектом из прототипа, придется явно использовать объект-прототип.

Совсем небольшой пример, в котором для свойства объекта вычисляется объект-дескриптор, приведен в листинге 4.18.

 Листинг 4.18. Получение доступа к дескриптору свойства
(файл Listing04_18.js)

// Ñîçäàíèå îáúåêòà:
var A={number:100}
// Äåñêðèïòîð ñâîéñòâà number:
var descriptor=Object.getOwnPropertyDescriptor(A,"number")
// Îòîáðàæåíèå ñâîéñòâ îáúåêòà A:
show(A)
// Îòîáðàæåíèå ñâîéñòâ îáúåêòà descriptor:
show(descriptor)
// Ôóíêöèÿ äëÿ îòîáðàæåíèÿ ñâîéñòâ îáúåêòà:
function show(obj){
document.write("{| ")
for(var s in obj){
document.write(" "+s+" : "+obj[s]+" |")
}
document.write("}")
}
Результат выполнения сценария следующий.

 Результат выполнения сценария (из листинга 4.18)
{| number : 100 |}
{| configurable : true | enumerable : true | value : 100 | writable : true |}

263

Часть II. JavaScript и ООП

В сценарии создается объект A со свойством number. Значение свойства
number равно 100. Для этого свойства командой Object.getOwnPropertyDescriptor
(A,"number") вычисляется ссылка на объект-дескриптор. Ссылка на дескриптор записывается в переменную descriptor.
В сценарии описана функция show(), которой отображаются свойства
объекта, переданного аргументом функции. Названия свойств выделяются жирным шрифтом, между названием и значением свойства
размещается двоеточие. Вся конструкция заключается в фигурные
кавычки, а разделителем между блоками с названием и значением
свойств служит вертикальная черта.
В сценарии командами show(A) и show(descriptor) отображаются свойства
для объектов A и descriptor. У объекта A всего одной свойство (имеется
в виду свойство number). У объекта descriptor, являющегося дескриптором свойства number объекта A, четыре свойства.


Свойство value объекта descriptor имеет значение 100: это фактическое
значение свойства number объекта A.



Свойство enumerable объекта descriptor имеет значение true (является
перечисляемым): при переборе свойств объекта A с помощью оператора for-in свойство number попадает во множество перебираемых
свойств.



Свойство writable объекта descriptor имеет значение true: значение
свойства number объекта A может быть изменено в сценарии.



Свойство configurable объекта descriptor имеет значение true: свойство
number объекта A может (при необходимости) быть удалено.

Для проверки работы сценария используем HTML-код, представленный ниже:



Ëèñòèíã 4.18

Ëèñòèíã 4.18



264

Глава 4. Знакомство с объектами и принципы ООП





На рис. 4.17 показано, как выглядит окно браузера с веб-документом,
в котором выполняется описанный сценарий.

Рис. 4.17. Результат выполнения сценария, в котором для свойства вычисляется
объект-дескриптор

В данном случае мы получили доступ к объекту-дескриптору, который содержит описание характеристик свойства. Возникает вопрос:
а как задать (или изменить) значения атрибутов свойства? На этот
случай есть метод defineProperty() объекта Object.



Д Е ТА Л И
Выше мы получали доступ к объекту-дескриптору, содержащему описание атрибутов некоторого свойства. Может возникнуть
желание изменить значения атрибутов свойства непосредственно в объекте-дескрипторе. Хотя формально так можно сделать,
на самом свойстве это не отразится. Причина в том, что методом
getOwnPropertyDescriptor() возвращается одна из копий объекта-дескриптора. Поэтому внесение в нее изменений не сказывается на исходном свойстве.

Аргументами методу defineProperty() передается ссылка на объект, в который добавляется свойство, название добавляемого свойства, а так-

265

Часть II. JavaScript и ООП

же объект-дескриптор данного свойства. Объект-дескриптор может
быть передан в виде литерала объекта и может содержать не все свойства (имеются в виду свойства value, enumerable, writable и configurable объекта-дескриптора). Пример использования метода defineProperty() приведен в листинге 4.19.

 Листинг 4.19. Использование метода defineProperty()
(файл Listing04_19.js)

// Ñîçäàíèå ïóñòîãî îáúåêòà:
var A={}
// Äîáàâëåíèå ñâîéñòâà number:
Object.defineProperty(A,"number",{value:100,enumerable:true,writable:true,configurable:true})
// Îáúåêò-äåñêðèïòîð íîâîãî ñâîéñòâà:
var descriptor={value:"îáúåêò À",enumerable:true,writable:true,configurable:true}
// Äîáàâëåíèå ñâîéñòâà name:
Object.defineProperty(A,"name",descriptor)
// Îòîáðàæåíèå ñâîéñòâ îáúåêòà A:
show(A)
// Èçìåíåíèå àòðèáóòîâ ñâîéñòâà number:
Object.defineProperty(A,"number",{value:200,enumerable:false})
// Îòîáðàæåíèå ñâîéñòâ îáúåêòà A:
show(A)
// Îòîáðàæåíèå çíà÷åíèÿ ñâîéñòâà number:
document.write("A.number = "+A.number)
// Ôóíêöèÿ äëÿ îòîáðàæåíèÿ ñâîéñòâ îáúåêòà:
function show(obj){
document.write("{|")
for(var s in obj){
document.write(" "+s+" : "+obj[s]+" |")
}
document.write("}")
}
В результате выполнения сценария в рабочем документе отображаются такие сообщения.

266

Глава 4. Знакомство с объектами и принципы ООП

 Результат выполнения сценария (из листинга 4.19)
{| number : 100 | name : îáúåêò À |}
{| name : îáúåêò À |}
A.number = 200
Сценарий начинается с команды создания пустого объекта A, после
чего в объект добавляется свойство number. Свойство добавляем командой Object.defineProperty(A,"number",{value:100,enumerable:true,writable:true,configurab
le:true}). Здесь метод defineProperty() вызывается из объекта Object, а аргументами методу передаются:


ссылка на объект A, в который добавляется свойство;



название "number" добавляемого свойства;



объект-дескриптор для добавляемого свойства. Объект задан в виде литерала {value:100,enumerable:true,writable:true,configurable:true}.
В объек те-дескрипторе определяются атрибуты свойства number
объекта A. В частности, свойство относится к перечисляемым (будет отображаться при переборе с помощью оператора цикла for-in),
а его значение устанавливается равным 100.

Командой var descriptor={value:"îáúåêò À",enumerable:true,writable:true,configurable:true}
создается объект-дескриптор, и ссылка на объект записывается в переменную descriptor.
Эта переменная используется в команде Object.defineProperty(A,"name",
descriptor), которой в объект A добавляется свойство name. Затем с помощью функции show() (описание функции в конце сценария) отображаются названия и значения свойств объекта A. Поскольку в теле
функции show() для перебора свойств объекта, переданного аргументом функции, используется оператор for-in, а свойства number и name
создавались со значением true атрибута enumerable, то оба эти свойства
отобража ются.
На следующем этапе изменяются значения некоторых атрибутов
свойства number. Нами использована команда Object.defineProperty(A,"number",
{value:200,enumerable:false}). Формально она похожа на команду добавления
свойства "number" в объект A, но здесь речь идет именно об изменении
атрибутов свойства. Более того, в объекте-дескрипторе явно указаны
значения только двух свойств (value и enumerable), определяющих новые
значения для атрибутов свойства number объекта A. Результат проверя-

267

Часть II. JavaScript и ООП

ем командой show(A). Поскольку теперь свойство number относится к неперечисляемым (так как новое значение свойства enumerable в объектедескрипторе равно false), то в списке свойств объекта A свойство number
не отображается. Тем не менее такое свойство у объекта A есть, в чем
легко убедиться с помощью команды document.write("A.number = "+A.number).
Для тестирования сценария используем следующий HTML-код:



Ëèñòèíã 4.19

Ëèñòèíã 4.19






На рис. 4.18 показано, как выглядит веб-документ с результатом выполнения сценария.

Рис. 4.18. Результат выполнения сценария, в котором используется метод
defineProperty()

268

Глава 4. Знакомство с объектами и принципы ООП



Д Е ТА Л И
Есть ряд методов объекта Object, которые бывают полезны при работе с объектами. Метод preventExtensions() применяет режим запрета
для добавления свойств в объект, указанный аргументом метода.
С помощью метода seal() блокируется добавление новых и удаление
существующих свойств объекта, переданного аргументом методу
(значение атрибута configurable для всех свойств объекта устанавливается равным false).
Методом freeze() блокируется добавление, удаление и изменение
свойств объекта, переданного аргументом методу. При этом значение атрибутов configurable и writable для всех свойств объекта устанавливается равным false.
Методы isExtensible(), isSealed() и isFrozen() вызываются из объекта Object,
аргументом им передается объект, а результатом является true,
если для объекта ранее вызывались соответственно методы
preventExtensions(), seal() и freeze() (в противном случае методами возвращается значение false).
Среди методов объекта Object имеется метод keys(). Результатом метода возвращается список перечисляемых свойств объекта. Объект передается аргументом методу. Результат возвращается в виде
массива (массивы обсуждаются в следующей главе). Массив с названиями всех (не только перечисляемых) свойств объекта возвращается методом getOwnPropertyNames(). Аргументом методу передается
ссылка на объект.
Полезным может быть метод hasOwnProperty(), позволяющий проверить, является ли свойство собственным свойством объекта. Метод вызывается из объекта, свойство которого проверяется. Название свойства передается аргументом методу. Результатом метода
является значение true, если свойство является собственным свойством объекта, и false в противоположном случае.

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

269

Часть II. JavaScript и ООП

ния значения свойства. Технически все это реализуется с помощью
двух методов: метода присваивания значения и метода считывания
значения. Первый из означенных методов описывается с ключевым
словом get (метод считывания значения), а второй — с ключевым словом set (метод для присваивания значения).
В частности, если мы хотим выполнить эмуляцию свойства, то метод
для присваивания значения такому свойству выглядит так (ключевые элементы шаблона выделены жирным шрифтом):
get ñâîéñòâî(){
// êîä ïîëó÷åíèÿ äîñòóïà ê çíà÷åíèþ
}
Метод для присваивания значения ñâîéñòâó описывается в соответствии со следующим шаблоном (жирным шрифтом выделены основные элементы кода):
set ñâîéñòâî(àðãóìåíò){
// êîä ïîëó÷åíèÿ äîñòóïà ê çíà÷åíèþ
}
Важно понимать, что даже если для свойства описаны метод считывания значения (get-метод) и метод для присваивания значения (setметод), то считывается значение свойства и присваивается значение
свойству так, как если бы свойство было самым обычным: указывается имя объекта и через точку название свойства.
Небольшой пример, в котором иллюстрируются простые приемы по
созданию и использованию свойств с режимом доступа, представлен
в листинге 4.20.

 Листинг 4.20. Свойства с режимом доступа (файл Listing04_20.js)
// Îáúåêò è ñâîéñòâî ñ ðåæèìîì äîñòóïà:
var A={
// Îáû÷íîå ñâîéñòâî:
name:"îáúåêò À",
// Âñïîìîãàòåëüíîå ñâîéñòâî:
n:0,
// Ìåòîä äëÿ ñ÷èòûâàíèÿ çíà÷åíèÿ ñâîéñòâà number:
get number(){
return this.n%10
},

270

Глава 4. Знакомство с объектами и принципы ООП

// Ìåòîä äëÿ ïðèñâàèâàíèÿ çíà÷åíèÿ ñâîéñòâó number:
set number(x){
this.n=(x%10)
}
}
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=123
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=5
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ âñïîìîãàòåëüíîìó ñâîéñòâó:
A.n=12
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
Результат выполнения сценария представлен на рис. 4.19.

Рис. 4.19. Результат выполнения сценария, в котором у объекта создается
свойство с режимом доступа

Тестируем сценарий с помощью следующего HTML-кода:




271

Часть II. JavaScript и ООП

Ëèñòèíã 4.20

Ëèñòèíã 4.20






Что касается самого сценария, то он, как отмечалось, небольшой. Самое
интересное в нем — описание объекта A. Объект создается описанием
литерала объекта. У объекта имеется обычное свойство name с текстовым значением "îáúåêò À", числовое «вспомогательное» свойство n с начальным значением 0, а также свойство number с режимом доступа. Нас
интересует в первую очередь свойство number. Создается оно описанием
методов доступа к свойству. Метод для считывания значения свойства
number описывается с get-инструкцией следующим образом:
get number(){
return this.n%10
}
В соответствии с приведенным программным кодом результатом getметода (оно же считываемое значение свойства number) возвращается
остаток от деления значения поля n на 10 (целое число в диапазоне
значений от 0 до 9).
Что происходит при присваивании значения полю number, можно понять, проанализировав программный код set-метода:
set number(x){
this.n=(x%10)
}
У метода есть аргумент (обозначен как x) — значение, что присваивается свойству number. Но на самом деле командой this.n=(x%10) свойству n
объекта присваивается остаток от деления на 10 значения, формально
присваиваемого свойству number.

272

Глава 4. Знакомство с объектами и принципы ООП



Д Е ТА Л И
Таким образом, у объекта A есть свойство n. При обращении к свойству number для считывания или присваивания значения на самом
деле выполняется обращение к свойству n. При этом используются
определенные алгоритмы считывания значения и записи значения.
Так, при присваивании значения полю number в поле n записывается
остаток от деления на 10 числа, которое присваивается полю number.
При считывании значения поля number возвращается остаток от деления на 10 числа, записанного в поле n.
Следует также учесть, что никто не запрещает нам изменить значение свойства n, не обращаясь к полю number. Другими словами, мы
можем изменить непосредственно значение поля n (командой вида
A.n=çíà÷åíèå), что повлечет за собой изменение значения поля number.
Данная ситуация еще будет нами обсуждаться.

При присваивании значения полю number командой A.number=123 технически в поле n записывается значение 3 (остаток от деления числа 123 на 10). Оно же возвращается при считывании значения поля
number в команде document.write(A.name+""+A.number+""). Аналогичная
ситуация имеет место при выполнении команд A.number=5 и document.write
(A.name+""+A.number+"").
Важная особенность представленного кода иллюстрируется выполнением команд A.n=12 и document.write(A.name+""+A.number+""). Здесь мы
напрямую присваиваем значение свойству n и после этого, когда считывается значение свойства number, получаем значение 2 (остаток от
деления числа 12 на 10).
i


НА ЗАМЕТКУ
Подобных ситуаций, когда значение свойства (в данном случае
number) может изменяться неявно, лучше избегать. В таких языках
программирования, как C++, C# и Java, нужного эффекта добиваются с помощью механизма ограничения доступа к «техническим»
и «вспомогательным» полям. В JavaScript нет простого механизма
создания закрытых полей. Тем не менее решение у проблемы есть,
но его мы обсудим немного позже.

В приведенном выше примере мы описывали свойство с режимом доступа в литерале объекта. Подобное свойство также можно добавить
в объект методом defineProperty(), описав атрибуты свойства с помощью
объекта-дескриптора.

273

Часть II. JavaScript и ООП



Д Е ТА Л И
Напомним, что в объекте-дескрипторе описываются четыре свойства: value, enumerable, writable и configurable. Но если в объекте-дескрипторе описывается свойство с режимом доступа (такой объект-дескриптор содержит get-метод и set-метод), то свойства value и writable
в объекте-дескрипторе не описываются.

Альтернативный способ реализации рассмотренного выше примера (но теперь с использованием объекта-дескриптора) представлен
в листинге 4.21.

 Листинг 4.21. Свойства с режимом доступа и объект-дескриптор
(файл Listing04_21.js)

// Ñîçäàíèå îáúåêòà:
var A={name:"îáúåêò À",n:0}
// Äîáàâëåíèå â îáúåêò ñâîéñòâà ñ ðåæèìîì äîñòóïà:
Object.defineProperty(A,"number",{
get:function(){
return this.n%10
},
set:function(x){
this.n=(x%10)
}
})
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=123
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=5
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ âñïîìîãàòåëüíîìó ñâîéñòâó:
A.n=12
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
Результат выполнения данного сценария абсолютно такой же, как
в предыдущем случае.

274

Глава 4. Знакомство с объектами и принципы ООП
i


НА ЗАМЕТКУ
Для тестирования сценария можно воспользоваться стандартным
HTML-шаблоном, который мы использовали ранее. Достаточно
лишь изменить название загружаемого файла со сценарием.

По сравнению с предыдущим примером, здесь несколько иначе описывается объект A. В частности, сначала описанием литерала создается
объект со свойствами name и n, а затем вызовом метода defineProperty() в объект добавляется свойство с режимом доступа number. Интерес представляет дескриптор свойства, переданный третьим аргументом методу
defineProperty(). С формальной точки зрения в дескрипторе описываются
методы get и set. То, что это методы доступа к свойству number, становится
понятным, исходя из значения второго аргумента метода defineProperty().
Изменение способа создания объекта A cо свойством доступа number не
снимает главной проблемы: технически все операции со свойством number
выполняются через свойство n, которое является открытым в том смысле, что с ним можно выполнять любые операции. Это все равно, как если
бы мы строили дом на ненадежном фундаменте. Самый простой способ
решить проблему состоит в том, чтобы «спрятать» свойство n объекта.
Мы воспользуемся особенностью локальных переменных, которые объявляются в функции и доступны только в пределах функции. Такой
«маскирующей» функцией станет конструктор объектов. Рассмотрим
программный код, представленный в листинге 4.22. В данном примере
для создания объекта используется функция-конструктор.

 Листинг 4.22. Свойства с режимом доступа и конструктор объектов
(файл Listing04_22.js)

// Êîíñòðóêòîð äëÿ ñîçäàíèÿ îáúåêòîâ:
function MyObj(name){
// Ëîêàëüíàÿ ïåðåìåííàÿ:
var n
// Ñâîéñòâî name îáúåêòà:
this.name=name
// Äîáàâëåíèå â îáúåêò ñâîéñòâà
// number ñ ðåæèìîì äîñòóïà:
Object.defineProperty(this,"number",{
// Ìåòîä äëÿ ñ÷èòûâàíèÿ çíà÷åíèÿ ñâîéñòâà:
get:function(){
return n%10

275

Часть II. JavaScript и ООП

},
// Ìåòîä äëÿ ïðèñâàèâàíèÿ çíà÷åíèÿ ñâîéñòâó:
set:function(x){
n=(x%10)
}
})
} // Îêîí÷àíèå îïèñàíèÿ ôóíêöèè-êîíñòðóêòîðà
// Ñîçäàíèå îáúåêòà:
var A=new MyObj("îáúåêò À")
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=123
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó ñ ðåæèìîì äîñòóïà:
A.number=5
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
// Ïðèñâàèâàíèå çíà÷åíèÿ ñâîéñòâó (äîáàâëåíèå ñâîéñòâà):
A.n=12
// Ñ÷èòûâàíèå çíà÷åíèÿ ñâîéñòâà ñ ðåæèìîì äîñòóïà:
document.write(A.name+""+A.number+"")
Для тестирования сценария используем следующий шаблонный код:



Ëèñòèíã 4.22

Ëèñòèíã 4.22







276

Глава 4. Знакомство с объектами и принципы ООП

На рис. 4.20 показан результат выполнения сценария.

Рис. 4.20. Результат выполнения сценария, в котором свойство с режимом
доступа добавляется в конструкторе объекта

Функция-конструктор, описанная в сценарии, называется MyObj().
У нее один аргумент (обозначен как name). Это значение для свойства
name объекта, который создается с помощью функции-конструктора
(в теле конструктора выполняется команда this.name=name).
В функции-конструкторе объявляется локальная переменная n. Ей не
присваивается значение, но она «упоминается» в объекте-дескрипторе, который, в свою очередь, передается третьим аргументом методу
defineProperty() (первые два — ссылка this на объект, в который добавляется свойство, и название свойства "number").
В объекте-дескрипторе описывается get-метод:
get:function(){
return n%10
}
и set-метод
set:function(x){
n=(x%10)
}

277

Часть II. JavaScript и ООП

Здесь важно то, что операции выполняются не со свойством объекта,
а с локальной переменной n (в частности, результат get-метода вычисляется на основе значения локальной переменной n функции-конструктора). Поэтому после вызова функции-конструктора при создании объекта локальная переменная n из памяти не выгружается и ее
можно использовать (не напрямую, но через вызовы методов) и после
завершения выполнения функции-конструктора.
i


НА ЗАМЕТКУ
У созданного с помощью функции-конструктора MyObj() объекта нет
свойства n. Это принципиальный момент. Переменная, которая
неявно используется при работе со свойством number, не является
свойством объекта, и к ней в общем-то нет прямого доступа.

Объект в данном случае создается командой var A=new MyObj("îáúåêò À").
Далее следуют знакомые нам команды, которыми присваивается
значение свойству number и считывается значение данного свойства.
Результат выполнения таких команд уже описывался и должен быть
понятен читателю. Что требует пояснения, так это результат выполнения команды A.n=12. Поскольку у объекта A свойства n нет, то выполнение данной команды приводит к добавлению такого свойства
в объект. Но поскольку операции со свойством number теперь не подразумевают использование свойства n, то на значении свойства number
наличие или отсутствие свойства n никак не сказывается.
Еще одно небольшое замечание касается того, что совсем необязательно описывать для свойства с режимом доступа get-метод и set-метод. Небольшой пример, в котором свойство с режимом доступа может быть только считано (для него описан только get-метод), приведен
в листинге 4.23.

 Листинг 4.23. Свойство с режимом доступа предназначено только
для считывания значения (файл Listing04_23.js)

// Ñîçäàíèå îáúåêòà:
var z={re:3,im:4}
// Äîáàâëåíèå â îáúåêò ñâîéñòâà abs ñ ðåæèìîì äîñòóïà:
Object.defineProperty(z,"abs",{
get:function(){
return Math.sqrt(this.re*this.re+this.im*this.im)
}

278

Глава 4. Знакомство с объектами и принципы ООП

})
// Îòîáðàæåíèå ðåçóëüòàòà âû÷èñëåíèé:
document.write("z = "+z.re+" + "+z.im+"i")
document.write("|z| = "+z.abs)
Тестируем данный сценарий с помощью такого HTML-кода:



Ëèñòèíã 4.23

Ëèñòèíã 4.23






Результат выполнения сценария представлен на рис. 4.21.

Рис. 4.21. Результат выполнения сценария, в котором свойство с режимом
доступа предназначено только для считывания значения

279

Часть II. JavaScript и ООП

В данном примере мы сначала создаем командой var z={re:3,im:4} объект z
со свойствами re и im, а затем добавляем в объект свойство abs с режимом доступа.
Для свойства abs предусмотрен только get-метод, поэтому свойство доступно только для считывания значения.



Д Е ТА Л И
Речь идет о вычислении модуля комплексного числа. Если комплексное число z = x + iy, где x = Re(z) и y = Im(z),соответственно, действительная и мнимая части комплексного числа, а мнимая единица i та2
кая, что i = –1, то модулем комплексного числа называется значение
. Именно оно и вычисляется в get-методе, описанном для
свойства abs объекта z.
Для вычисления квадратного корня использован метод sqrt()
объек та Math. Ссылка в теле get-метода на объект, из которого метод фактически вызывается, реализована через ключевое слово this.

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

Резюме
Сейчас я вам покажу, где здесь хунд
беграбен!
из к/ф «Покровские ворота»

Таким образом, выше мы узнали следующее.


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

280

Глава 4. Знакомство с объектами и принципы ООП



Существует несколько способов создания объекта: через описание литерала объекта, с помощью конструктора объектов Object,
с помощью метода create() объекта Object или с помощью функцииконструктора.



Литерал объекта описывается так: в фигурных скобках перечисляются через запятую пары из названий свойств и их значений.
Название свойства и его значение разделяются двоеточиями.



Метод представляет собой свойство, значением которого является функция. Для ссылки на объект, из которого вызывается метод,
используется ключевое слово this.



Переменные ссылаются на объект. При присваивании объектов
копирования объекта не происходит, просто ссылка на объект из
одной переменной копируется в другую (в результате две переменные ссылаются на один и тот же объект).



Если несуществующему свойству объекта присваивается значение, данное свойство добавляется в объект. Для удаления свойств
используют инструкцию delete.



Функция-конструктор объектов обычно содержит команды создания свойств объектов. Если в сценарии описана функция-конструктор, то объекты создаются с помощью оператора new и вызова
функции-конструктора.



Ряд операторов упрощает обработку объектов. Среди них оператор with (позволяет не использовать явную ссылку на объект), forin (используется для перебора свойств объекта), in (используется
для проверки наличия у объекта данного свойства).



Прототипом называется объект, свойства которого наследуются
в другом объекте. При обращении к свойству объекта (для считывания значения) поиск свойства сначала выполняется непосредственно в объекте, затем в его прототипе, затем в прототипе прототипа и так далее. В соответствии с этим алгоритмом все свойства
объекта делятся на собственные свойства и свойства, унаследованные из прототипов. Также свойства делятся на перечисляемые и неперечисляемые. Перечисляемые свойства отображаются
в операторе for-in, а неперечисляемые — нет.



Для создания объекта на основе прототипа можно прототип передать аргументом методу create() объекта Object (если методу create()
передать аргумент null, объект создается без прототипа). Для по-

281

Часть II. JavaScript и ООП

лучения доступа к прототипу используют метод getPrototypeOf(). Доступ к прототипу верхнего уровня получаем инструкцией Object.
prototype. Применить прототип к созданному объекту можно с помощью метода setPrototypeOf() объекта Object.


Объекты, созданные с помощью функции-конструктора, имеют
общий прототип. Доступ к данному прототипу можно получить
через свойство prototype объекта функции-конструктора. Прототип
объекта, созданного с помощью функции-конструктора, отличается от прототипа самой функции-конструктора.



Свойство constructor позволяет получить доступ к функции-конструктору, с помощью которой создавался объект.



Для каждого свойства имеется объект, содержащий описание
данного свойства. Такой объект называется дескриптором. У дескриптора имеются следующие основные свойства: value (значение
исходного, описываемого дескриптором свойства), enumerable (принадлежность исходного свойства к перечисляемым), writable (возможность присваивать свойству значение) и configurable (возможность удалить свойство).



Доступ к дескриптору собственного свойства объекта получаем
с помощью метода getOwnPropertyDescriptor() объекта Object. Для создания у объекта свойства на основе явно заданного дескриптора используют метод defineProperty() объекта Object.



Существует возможность создавать свойства со специальным
режимом доступа. Для таких свойств считывание и запись значения реализуются через специальные методы: get-метод определяет процесс считывания значения свойства, а set-метод определяет
процесс присваивания значения свойству. Для свойств с режимом
доступа в объекте-дескрипторе свойства value и writable не определяются. Также нет необходимости описывать оба метода доступа.

Глава 5
ЗНАКОМСТВО С МАССИВАМИ

Натурально как вы играете… И царь у вас такой… типичный! На нашего Буншу похож.
из к/ф «Иван Васильевич меняет
профессию»

Наконец пришло время познакомиться нам с массивами. Несмотря на
существенную важность массивов, мы оттягивали знакомство с ними
по одной простой причине: массивы в JavaScript уж очень специфичны. По большому счету, массив в JavaScript — это объект, но с определенными нестандартными свойствами. Чтобы не потерять ключевые
моменты при упрощенном рассмотрении массивов, мы сначала познакомились с объектами и только теперь приступаем к рассмотрению
массивов.

Создание массива
Что же вы, маэстры, молчите? Ну-ка, гряньте нам что-нибудь.
из к/ф «Иван Васильевич меняет
профессию»

Массив представляет собой упорядоченный набор элементов. Принципиально важно то, что в JavaScript элементы массива могут относиться к разным типам. Доступ к элементам массива получают
по индексу или индексам. Количество индексов, необходимых для
идентификации элемента массива, определяет размерность массива. Мы сначала рассмотрим одномерные массивы, в которых элемент
имеет только один индекс. Количество элементов в массиве называется размером массива.

Явное указание элементов массива
Самый простой способ создания массива подразумевает явное указание его элементов. Элементы массива указываются в виде списка, за-

283

Часть II. JavaScript и ООП

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



Д Е ТА Л И
На самом деле переменная, которой значением присваивается
массив, содержит ссылку на него. Ситуация напоминает работу
с объектами. Здесь нет ничего удивительного, ведь массив является объектом. Как мы увидим далее, такая особенность массивов
в JavaScript имеет важные последствия.

Таким образом, команда создания массива и записи ссылки на него
в некоторую ïåðåìåííóþ может выглядеть следующим образом:
var ïåðåìåííàÿ=[çíà÷åíèå_1,çíà÷åíèå_2,…,çíà÷åíèå_N]
Чтобы получить доступ к элементу одномерного массива, после имени массива (переменной, которая ссылается на массив) в квадратных
скобках указывают индекс элемента. Важно помнить, что по умолчанию индексация элементов начинается с нуля, так что индекс первого
элемента массива равен нулю, а индекс последнего элемента массива на единицу меньше размера массива (или его длины). Длина же
массива определяется свойством length объекта массива: чтобы узнать
длину массива, после имени переменной, ссылающейся на массив, через точку необходимо указать свойство length.
Небольшой пример с созданием массива путем явного указания его
элементов представлен в листинге 5.1.

 Листинг 5.1. Создание массива явным указанием элементов
(файл Listing05_01.js)

document.write("Ñîçäàíèå ìàññèâà")
// Ñîçäàíèå ìàññèâà:
var nums=[10,true,30,"òåêñò",75]
// Îòîáðàæåíèå ñîäåðæèìîãî ìàññèâà:
document.write(nums+"")
for(var k in nums){
document.write(nums[k]+" | ")
}

284

Глава 5. Знакомство с массивами

Проверить результат выполнения сценария нам поможет веб-документ с представленным ниже кодом:



Ëèñòèíã 5.1

Ëèñòèíã 5.1






На рис. 5.1 показано, как будет выглядеть данный веб-документ в окне
браузера.

Рис. 5.1. Результат выполнения сценария, в котором путем явного указания
элементов создается массив

Проанализируем основные фрагменты программного кода сценария.
Он достаточно простой и, скорее всего, понятный. Создается массив
командой var nums=[10,true,30,"òåêñò",75]. В этой команде объявляется пере-

285

Часть II. JavaScript и ООП

менная nums, а значением ей присваивается выражение [10,true,30,"òåêñò",75].
Последнее представляет собой массив, который состоит из пяти элементов, три из которых являются целыми числами, второй (по порядку) является логическим значением, а четвертый (по порядку) элемент представляет собой текстовое значение.
Если имя массива передать аргументом методу write() объекта документа document, то в рабочем документе будет отображено содержимое
массива, но не в очень эстетическом виде. Поэтому для отображения
элементов массива разумнее использовать оператор цикла. В данном
случае мы используем оператор for-in, позволяющий, кроме прочего,
выполнять перебор по элементам массива. В круглых скобках после
ключевого слова for указано выражение var k in nums, что буквально означает следующее: переменная k последовательно принимает значения
индексов элементов, входящих в массив nums. Поэтому в теле оператора цикла переменную k можно интерпретировать как индекс очередного элемента массива. Отсюда инструкция nums[k] представляет
собой обращение к элементу массива с соответствующим индексом.

Добавление элементов в массив
Надо сказать, что массивы в JavaScript исключительно гибкие в том
смысле, что с ними можно выполнять различные интересные операции, не характерные для массивов как таковых (если сравнивать
с другими языками программирования). Например, создать массив
можно следующим образом: сначала создается пустой массив, а затем
в этот массив последовательно добавляются элементы. Проиллюстрируем сказанное с помощью листинга 5.2, в котором представлена
небольшая вариация на тему предыдущего примера.

 Листинг 5.2. Создание пустого массива и добавление элементов
в массив (файл Listing05_02.js)

document.write("Äîáàâëåíèå ýëåìåíòîâ â ìàññèâ")
// Ñîçäàíèå ïóñòîãî ìàññèâà:
var nums=[]
// Äîáàâëåíèå ýëåìåíòîâ â ìàññèâ:
nums[0]=10
nums[1]=true
nums[2]=30

286

Глава 5. Знакомство с массивами

nums[3]="òåêñò"
nums[4]=75
// Îòîáðàæåíèå ñîäåðæèìîãî ìàññèâà:
for(var k=0;k





-3)&&(x

-3)&&(x









; 4 ; òåêñò ; true >
B=
Ñðåäíåå çíà÷åíèå: 3.8
Ìàññèâ < 1 ; 2 ; 3 ; 4 > - ñðåäíåå 2.5

К вопросу об определении методов toString() и valueOf() (в контексте работы с объектами) мы вернемся в следующей главе.

Двумерные массивы
Нет, он сомнителен! Он сомнителен!!! Я бы
ему не доверял.
из к/ф «Покровские ворота»

Многомерный массив — это массив, в котором для идентификации
элемента нужно несколько индексов (больше, чем один). На практике
обычно редко встречаются массивы размерности большей, чем два.
Особенности двумерных массивов рассматриваются далее.

328

Глава 5. Знакомство с массивами

С формальной точки зрения, двумерный массив — это массив, элементами которого являются одномерные массивы. Процесс создания
такого массива качественно не отличается от создания одномерного
массива. Проиллюстрируем это на примере. Рассмотрим программный код, представленный в листинге 5.13.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
В сценарии для отображения нижних индексов использованы дескрипторы и .

 Листинг 5.13. Создание двумерного массива (файл Listing05_13.js)
// Äîáàâëåíèå ìåòîäà toString() â ïðîòîòèï ìàññèâà:
Array.prototype.toString=function(){
return this.join(" ")+""
}
// Äâóìåðíûé ÷èñëîâîé ìàññèâ:
var A=[[1,2,3],[4,5,6],[7,8,9]]
document.write("Ìàññèâ A:")
// Îòîáðàæåíèå äâóìåðíîãî ÷èñëîâîãî ìàññèâà:
document.write(A)
var i,j,m=3,n=4
// Ñîçäàíèå îäíîìåðíîãî ìàññèâà:
var B=new Array(m)
// Çàïîëíåíèå ìàññèâà:
for(i=0;i









353

Часть II. JavaScript и ООП








1 | означает объект с номером 1 на наивысшем уровне
ранга 4. Код | 4 -> 1 | 3 -> 1 | означает объект, который находится на уров-

376

Глава 6. Использование объектов

не ранга 3, а ссылка на него записана первым элементом в массиве
со ссылками в объекте, находящемся на уровне ранга 4. Далее, код
| 4 -> 1 | 3 -> 1 | 2 -> 2 | соответствует объекту, который находится на уровне
с рангом 2. Ссылка на этот объект является вторым (по порядку) элементом в массиве, который «спрятан» в объекте на уровне ранга 3.
На данный объект, в свою очередь, ссылка содержится в первом (по
порядку) элементе в объекте на уровне ранга 4. Наконец, код | 4 -> 1 | 3
-> 1 | 2 -> 2 | 1 -> 2 | будет у объекта, который находится на уровне ранга 1.
Ссылка на объект записана во второй элемент в массиве объекта,
находящегося на уровне ранга 2. На него, в свою очередь, ссылается
второй элемент в массиве объекта, находящегося на уровне ранга 3.
А на этот объект ссылка содержится в первом элементе в массиве
объекта, находящегося на уровне 4.

Рассмотрим и проанализируем программный код, представленный
в листинге 6.8.

 Листинг 6.8. Дерево объектов (файл Listing06_08.js)
// Êîíñòðóêòîð äëÿ ñîçäàíèÿ äåðåâà îáúåêòîâ:
function Tree(rank,refs,number,code){
// Ðàíã óðîâíÿ, íà êîòîðîì íàõîäèòñÿ îáúåêò:
this.rank= rank
// Íîìåð îáúåêòà:
this.number=number || 1
// Ôîðìàëüíûé êîä îáúåêòà:
this.code=code || "| "
this.code+=this.rank+" -> "+this.number+" | "
// Ñîçäàíèå ìàññèâà îáúåêòîâ:
if(this.rank>1){
// Ìàññèâ:
this.next=new Array(refs)
for(var k=0;k1){
for(var k=0;k1 (ранг объекта больше единицы, а значит, объект не находится на самом нижнем уровне). Если условие истинно, то командой this.next=new Array(refs)
создается массив next, являющийся одновременно свойством объекта.
С помощью оператора цикла массив заполняется: каждый его элемент this.next[k] получает значением ссылку на объект. Объект создается командой new Tree(this.rank-1,refs,k+1,this.code). Речь идет о тех объектах, на
которые ссылается текущий объект. Здесь мы в теле конструктора Tree
вызываем этот же конструктор (то есть имеет место рекурсия). Аргументы при вызове конструктору передаются такие:


ранг this.rank-1 на единицу меньше ранга текущего объекта;



количество ссылок refs в новом объекте такое же, как и в исходном
объекте;



номер объекта k+1 на единицу больше индекса элемента в массиве,
который ссылается на создаваемый объект;



начальное this.code значение для кода создаваемого объекта такое
же, как код текущего объекта.

Собственно, на этом описание конструктора Tree заканчивается.
i


НА ЗАМЕТКУ
Свойство-массив next есть у каждого объекта в дереве, за исключением объектов, находящихся на нижнем уровне с рангом 1.

379

Часть II. JavaScript и ООП

Для прототипа конструктора Tree.prototypeопределяется метод toString().
В теле метода командой var t="Îáúåêò: ðàíã - "+this.rank+", íîìåð - "+this.number определяется локальная текстовая переменная, начальное значение которой содержит информацию о ранге и номере объекта, для которого
(явно или неявно) вызывается метод. Далее командой t+=", êîä - "+this.
code+"" в переменную добавляется информация о коде текущего
объекта. После этого в условном операторе при истинности условия
this.rank>1 запускается оператор цикла, в котором индексная переменная k пробегает значения индексов массива next текущего объекта. На
каждом цикле выполняется команда t+=this.next[k].toString(). Командой
в переменную t дописывается результат сведения к текстовому формату объектов из массива next. Здесь мы рекурсивно вызываем метод
toString() для объектов из массива next. Конечное значение переменной t
возвращается результатом метода toString().



Д Е ТА Л И
Чтобы понять принцип работы конструктора Tree или метода toString(),
имеет смысл представить, что соответственно создается объект
верхнего уровня и метод toString() вызывается из этого объекта. Начнем с конструктора.
В конструкторе сначала заполняются свойства создаваемого
объек та, а затем создается массив. При заполнении массива
объек тами вызывается конструктор, который создает каждый
из объек тов второго уровня и, в свою очередь, вызывает конструктор для создания объектов третьего уровня. Все это продолжается до тех пор, пока не будут созданы объекты с единичным рангом.
Примерно по той же схеме действует метод toString(). Сначала формируется строка на основе свойств объекта верхнего уровня, а затем для каждого из объектов, на которые ссылается данный объект,
вызывается метод toString(). При вызове метода снова происходит
вызов toString(), и так вплоть до объектов единичного ранга. Получается своеобразная «цепная реакция». Каждый вызов метода добавляет в текстовую строку, возвращаемую результатом, информацию
об очередном объекте.

Дерево объектов создается в сценарии командой var myTree=new Tree(4,2).
Речь, очевидно, идет о бинарном дереве из четырех уровней. Увидеть содержимое созданной структуры можно с помощью команды
document.write(myTree). Проверка сценария выполняется с помощью такого
HTML-кода:

380

Глава 6. Использование объектов




Ëèñòèíã 6.8

Ëèñòèíã 6.8






Текстовая версия для результата выполнения сценария представлена
ниже.

 Результат выполнения сценария (из листинга 6.8)
Îáúåêò: ðàíã - 4, íîìåð - 1, êîä - | 4 -> 1 |
Îáúåêò: ðàíã - 3, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 1 |
Îáúåêò: ðàíã - 2, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 1 | 1 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 1 | 1 -> 2 |
Îáúåêò: ðàíã - 2, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 2 |
Îáúåêò: ðàíã - 1, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 2 | 1 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 1 | 2 -> 2 | 1 -> 2 |
Îáúåêò: ðàíã - 3, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 2 |
Îáúåêò: ðàíã - 2, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 1 | 1 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 1 | 1 -> 2 |
Îáúåêò: ðàíã - 2, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 2 |
Îáúåêò: ðàíã - 1, íîìåð - 1, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 2 | 1 -> 1 |
Îáúåêò: ðàíã - 1, íîìåð - 2, êîä - | 4 -> 1 | 3 -> 2 | 2 -> 2 | 1 -> 2 |

381

Часть II. JavaScript и ООП

На рис. 6.19 представлен веб-документ со сценарием, открытый в окне
браузера. Желающие могут проанализировать структуру кодов объектов, которые отображаются в рабочем документе, или проверить
результат выполнения сценария для деревьев иной структуры (в команде var myTree=new Tree(4,2) достаточно изменить числовые значения, которые передаются в конструктор).

Рис. 6.19. Результат выполнения сценария, в котором создается дерево
объектов
i


НА ЗАМЕТКУ
Дерево объектов, состоящее из m уровней при условии, что каждый объект ссылается на n объектов, содержит в общей сложности
объекты в количестве

.

Это число может быть очень большим. А для каждого объекта в дереве выводится строчка с информацией. Так что при экспериментах
с деревьями гигантоманией увлекаться не стоит.
Можно проверить уровень своего понимания рассмотренного выше
примера, заменить в сценарии команду document.write(myTree) на document.
write(myTree.next[1]) или document.write(myTree.next[0].next[0]) и попытаться объяснить результат.

382

Глава 6. Использование объектов

Функция как объект
Этот дуэт двух наших форвардов сегодня
ослепителен. От него можно ждать всяких
неожиданностей.
из к/ф «Покровские ворота»

Функции мы уже рассматривали в одной из глав. Здесь мы затронем
некоторые вопросы, связанные в основном с тем, что в JavaScript функции являются объектами. Также мы рассмотрим ряд тем, которые
в силу разных причин не были подняты ранее.
Итак, функция является объектом. Как у любого объекта, у функции
есть свойства. Причем свойства очень полезные.
Для начала отметим один немаловажный момент: помимо тех способов создания функций, которые мы рассматривали (описание функции с ключевым словом function и присваивание переменной значением
анонимной функции), существует еще один способ, подразумевающий использование конструктора Function. Данный метод создания
функции далеко не самый популярный, однако вполне приемлемый.
Формат создания функции с помощью конструктора Function выглядит
так (жирным шрифтом выделена основная часть конструкции):
var èìÿ_ôóíêöèè=new Function(àðãóìåíòû, êîä ôóíêöèè)
Начальные аргументы конструктора Function — текстовые названия аргументов функции, а последний аргумент — текстовая строка с программным кодом, выполняемым при вызове функции. Скажем, если
мы хотим объявить функцию, которая вычисляет сумму двух своих
аргументов, то соответствующий фрагмент кода мог бы выглядеть
следующим образом:
var f=new Function("x","y","return x+y")
После этого переменную f можем вызывать как функцию: например,
значение выражения f(3,4) равно 7 (сумма аргументов).
Нас конструктор Function интересует не как средство создания функций, а по несколько иной причине. Причина эта связана с тем, что любая функция, каким бы способом (из трех означенных) она ни создавалась, имеет один и тот же прототип, и данный прототип — прототип
Function.prototype конструктора Function. У прототипа Function.prototype име-

383

Часть II. JavaScript и ООП

ется ряд интересных свойств и методов. А поскольку Function.prototype
является прототипом и для пользовательских функций, фактически
любая функция имеет доступ к означенным свойствам и методам. Их
и обсудим далее.

Количество аргументов функции
Начнем со свойства length, которое хотя и не критично, но может быть
достаточно полезным на практике. Свойство length значением возвращает количество аргументов, указанных при объявлении функции.
Если после названия функции через точку указать свойство length, то
результатом будет количество аргументов, указанных при описании
этой функции.
i


НА ЗАМЕТКУ
Стоит напомнить, что количество аргументов, передаваемых функции при вызове, может не совпадать с количеством аргументов,
указанных при описании функции.

Небольшой пример использования свойства length для функций представлен в листинге 6.9.

 Листинг 6.9. Свойство length для функций (файл Listing06_09.js)
// Ôóíêöèÿ äëÿ ïðîâåðêè êîëè÷åñòâà àðãóìåíòîâ,
// óêàçàííûõ ïðè îïèñàíèè ôóíêöèè:
function testArgs(func){
// Êîëè÷åñòâî àðãóìåíòîâ ôóíêöèè:
var n=func.length
var words
// Îïðåäåëåíèå çíà÷åíèÿ òåêñòîâîé ïåðåìåííîé:
switch(n){
case 0:
words="íåò àðãóìåíòîâ."
break
case 1:
words="îäèí àðãóìåíò."

384

Глава 6. Использование объектов

break
case 2:
case 3:
case 4:
words=n+" àðãóìåíòà."
break
default:
words=n+" àðãóìåíòîâ."
}
document.write("Ôóíêöèÿ "+func+": "+words+"")
}
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ áåç àðãóìåíòîâ:
function show(){
document.write("Âñåì ïðèâåò!")
}
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ ñ øåñòüþ àðãóìåíòàìè:
function F(a,b,c,d,e,f){
return a*b*c*d*e*f
}
// Ïðîâåðêà êîëè÷åñòâà àðãóìåíòîâ ôóíêöèè:
testArgs(eval)
testArgs(Math.pow)
testArgs(show)
testArgs(F)
// Ôóíêöèÿ âîçâðàùàåò ðåçóëüòàòîì òó èç ôóíêöèé,
// ó êîòîðîé ïðè îïèñàíèè óêàçàíî ìåíüøå àðãóìåíòîâ:
function getFunc(f1,f2){
if(f1.length

0)&&(typeof(arguments[0])=="string")){
s=""
}
else{
s=0
}
// Âû÷èñëåíèå ñóììû:
for(var k=0;k

0)&&(typeof
(arguments[0])=="string"). Условие истинно, если функции передан хотя бы
один аргумент (условие arguments.length>0) и одновременно первый из
переданных аргументов текстовый (условие typeof(arguments [0])=="string").
Если условие в условном операторе истинно, переменная s получает
в качестве начального значения пустую текстовую строку. В противном случае начальное значение переменной нулевое.



Д Е ТА Л И
При проверке условия (arguments.length>0)&&(typeof(arguments[0])=="string") сначала вычисляется значение выражения arguments.length>0. Если данное выражение равно true, то как минимум один аргумент функции
передан и имеет смысл обращение arguments[0] к первому аргументу функции. Если значение выражения arguments.length>0 равно false, то
аргументы функции не передавались. В таком случае обращение
arguments[0] некорректно. Вместе с тем, поскольку оператор логического и && вычисляется по сокращенной схеме, то при значении false
первого операнда второй не вычисляется. Поэтому при значении
false выражения arguments.length>0 до вычисления значения выражения
typeof(arguments[0])=="string" дело не доходит.

Далее в теле функции запускается оператор цикла, в котором в переменную s последовательно добавляются значения элементов (при
индексе k обращение к аргументу функции выполняется в формате
arguments[k]). Помимо описания функции sum(), сценарий содержит примеры вызова функции. Как несложно заметить, при разном количестве аргументов результатом функции возвращается их сумма, в чем
и состояла наша цель.

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

 Листинг 6.1. Использование ключевого слова this в описании функции
// Ôóíêöèÿ ñ óêàçàòåëåì this îïèñàíà âíå îáúåêòà:
function f(text,number){

391

Часть II. JavaScript и ООП

this.text=text
this.number=number
}
// Îáúåêòû:
var A={}
var B={show:function(){
for(var s in this){
document.write(s+": "+this[s]+"")
}
}}
var C={}
C.method=f
// Âûçîâ ôóíêöèè:
f("ôóíêöèÿ",100)
// Âûçîâ èç îáúåêòà ôóíêöèè ìåòîäà call():
f.call(A,"îáúåêò À",200)
// Âûçîâ èç îáúåêòà ôóíêöèè ìåòîäà apply():
f.apply(B,["îáúåêò B",300])
// Âûçîâ ìåòîäà îáúåêòà:
C.method("îáúåêò C",400)
// Ïðîâåðêà çíà÷åíèé ãëîáàëüíûõ
// ïåðåìåííûõ è ñâîéñòâ îáúåêòîâ:
document.write(text+" | "+number+"")
document.write(A.text+" | "+A.number+"")
document.write(B.text+" | "+B.number+"")
document.write(C.text+" | "+C.number+"")
// Ïðîâåðêà íàëè÷èÿ ó îáúåêòîâ ìåòîäà f():
document.write(("f" in A)+"")
document.write(("f" in B)+"")
document.write(("f" in C)+"")
// Ïðîâåðêà ñâîéñòâ è ìåòîäîâ îáúåêòîâ:
document.write("Îáúåêò B:")
B.show()
document.write("Îáúåêò A:")

392

Глава 6. Использование объектов

B.show.call(A)
document.write("Îáúåêò C:")
B["show"].call(C)
Работу сценария проверим с помощью такого HTML-кода:



Ëèñòèíã 6.11

Ëèñòèíã 6.11






На рис. 6.22 показано, как в окне браузера выглядит веб-документ
с представленным выше сценарием.
Проанализируем код сценария и поясним результаты его выполнения. При этом полезной будет текстовая версия результата выполнения сценария, представленная ниже.

 Результат выполнения сценария (из листинга 6.11)
ôóíêöèÿ | 100
îáúåêò À | 200
îáúåêò B | 300
îáúåêò C | 400
false
false
false
Îáúåêò B:

393

Часть II. JavaScript и ООП

show: function (){ for(var s in this){ document.write(s+": "+this[s]+"
") } }
text: îáúåêò B
number: 300
Îáúåêò A:
text: îáúåêò À
number: 200
Îáúåêò C:
method: function f(text,number){ this.text=text this.number=number }
text: îáúåêò C
number: 400

Рис. 6.22. Результат выполнения сценария, в котором используется функция,
описанная с ключевым словом this, а также методы call() и apply()

Вся интрига в сценарии закручена вокруг функции f(), которая описана с двумя аргументами (обозначены как text и number), а в теле данной

394

Глава 6. Использование объектов

функции использовано ключевое слово this. В частности, командами this.text=text и this.number=number выполняется присваивание значений
свойствам text и number, только непонятно какого объекта, поскольку
функция описана вне какого бы то ни было из них.
Другими словами, поскольку функция f() определена сама по себе, то
возникает вопрос: что подразумевать под this? Ответ на вопрос зависит
от способа вызова функции. Если функция вызывается как обычная
функция (не метод), то под this подразумевается объект рабочего окна
window. Поэтому, например, при выполнении команды f("ôóíêöèÿ",100) будут созданы две глобальные переменные text и number, и им будут присвоены соответствующие значения. В последнем легко удостовериться с помощью команды document.write(text+" | "+number+"").
i


НА ЗАМЕТКУ
Напомним, что глобальная переменная представляет собой свойство объекта рабочего окна window.

В сценарии создается три объекта: A, B и C. Объект A пустой, объект B
содержит метод show(), которым отображаются названия и значения
всех собственных свойств и методов объекта, а объекту C командой
C.method=f добавляется свойство method, значением которого является
ссылка на функцию f().
Далее в сценарии используются встроенные методы call() и apply(), которые вызываются из объекта функции или метода. Вообще методы call()
и apply() позволяют вызывать функцию или метод в контексте некоторого объекта. Принципиальное различие между методами call() и apply()
состоит в способе передачи аргументов: методу apply() они передаются
в виде массива (за исключением первой ссылки на объект), а методу
call() аргументы передаются списком. Более конкретно формат вызова
метода call() таков:
ôóíêöèÿ.call(îáúåêò,àðãóìåíòû)
Результат команды такой, как если бы ôóíêöèÿ (из объекта которой вызывается метод call()) вызывалась из îáúåêòà (первый аргумент метода
call()) с àðãóìåíòàìè (все прочие, кроме первого, аргументы метода call()).
Аналогично используется метод apply():
ôóíêöèÿ.apply(îáúåêò,ìàññèâ)

395

Часть II. JavaScript и ООП

Результатом такого выражения является результат вызова ôóíêöèè
(из объекта которой вызывается метод apply()) из îáúåêòà (первый аргумент метода apply()) с аргументами, переданными в виде ìàññèâà (второй аргумент метода apply()).
Поэтому результат выполнения команды f.call(A,"îáúåêò À",200) такой,
как если бы у объекта A был метод f() и он вызывался с аргументами "îáúåêò À" и 200. Аналогично, результат выполнения команды
f.apply(B,["îáúåêò B",300]) эквивалентен вызову из объекта B метода, аналогичного f(), с аргументами "îáúåêò B" и 300. Но здесь важно понимать,
что на самом деле метода f() ни у объекта A, ни у объекта B нет, хотя
при выполнении соответствующих команд под ключевым словом
this имеется в виду ссылка на объект, в контексте которого вызывается функция (объект A для первой команды и объект B для второй
команды). А вот у объекта C есть метод method(), который можно вызвать (команда C.method("îáúåêò C",400)), причем в данном случае реально
выполняется код функции f(), а под ключевым словом this подразумевается объект C.
С помощью команд document.write(A.text+" | "+A.number+""), document.write(B.
text+" | "+B.number+"") и document.write(C.text+" | "+C.number+"") легко убедиться, что у объектов A, B и C появляются свойства text и number. А вот
проверка на предмет наличия у указанных объектов метода f() (команды document.write(("f" in A)+""), document.write(("f" in B)+"") и document.
write(("f" in C)+"") показывает, что такого метода у данных объектов
(в том числе и у объекта C!) нет.
i


НА ЗАМЕТКУ
У объекта C метода f() нет, зато есть метод method(), при вызове которого на самом деле выполняется код функции f().

Для проверки свойств и методов объекта B вызываем из объекта метод
show().
i


НА ЗАМЕТКУ
При отображении «значения» метода в рабочий документ выводится программный код метода. Код метода show() содержит в текстовых литералах дескрипторы перехода к новой строке . Поэтому
в соответствующих местах текста кода в окне браузера выполняется переход к новой строке.

396

Глава 6. Использование объектов

Метод show() есть у объекта B, но его нет у объектов A и C. Тем не менее
метод show() объекта B можно вызвать в контексте объекта A. Другими словами, несмотря на то что show() является методом объекта B, его
можно вызвать так, как если бы он был методом объекта A. Нужный
эффект достигается, например, командой B.show.call(A). В данном случае
из объекта метода B.show вызывается метод call(), которому передается
аргументом объект A, в контексте которого следует вызывать метод.
Если бы у метода show() были аргументы, они бы перечислялись после
ссылки на объект A в списке аргументом метода call().
Команда B["show"].call(C) по своему назначению аналогична предыдущей, с той лишь разницей, что при обращении к методу show() объекта B
мы используем не точечный синтаксис, а индексную нотацию, указав
название метода в квадратных скобках после имени объекта.
Ситуация с передачей контекста при вызове функций и методов может быть достаточно нетривиальной. В качестве иллюстрации рассмотрим еще один пример. Он представлен в листинге 6.12.

 Листинг 6.12. Передача контекста с помощью метода bind()
(файл Listing06_12.js)

// Ôóíêöèÿ ðåçóëüòàòîì âîçâðàùàåò ôóíêöèþ,
// ïåðåäàííóþ àðãóìåíòîì:
function caller(func){
return func
}
// Ôóíêöèÿ îòîáðàæàåò çíà÷åíèå ïåðåäàííîãî åé àðãóìåíòà:
function show(txt){
document.write(txt+"")
}
// Îáúåêò ñî ñâîéñòâîì text, ìåòîäîì hi()
// è ïåðåîïðåäåëåííûì ìåòîäîì toString():
var obj={text:"îáúåêò obj",hi:function(){
document.write(this.text+"")
},
toString:function(){
return this.text
}

397

Часть II. JavaScript и ООП

}
// Âûçîâ ôóíêöèé show() ÷åðåç ôóíêöèþ caller():
caller(show)("ôóíêöèÿ show()")
obj.hi() // Âûçîâ ìåòîäà îáúåêòà
// Íåóäà÷íàÿ ïîïûòêà âûçâàòü
// ìåòîä hi() ÷åðåç ôóíêöèþ caller():
caller(obj.hi)()
// Âûçîâ ìåòîäà hi() ÷åðåç ôóíêöèþ caller():
caller(obj.hi.bind(obj))()
// Îïðåäåëåíèå íîâîé ôóíêöèè íà îñíîâå ìåòîäà:
var powerOfTwo=Math.pow.bind(Math,2)
var n=5
document.write("2"+n+" = "+powerOfTwo(n)+"")
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ ñ êëþ÷åâûì ñëîâîì this â òåëå:
function f(x){
return this+x
}
// Îïðåäåëåíèå íîâûõ ôóíêöèé íà îñíîâå
// âñïîìîãàòåëüíîé ôóíêöèè:
var one=f.bind(10)
var two=f.bind(obj)
// Âûçîâ ôóíêöèé:
document.write(one(5)+"")
document.write(two(" - ýòî îí")+"")
// Íîâîå çíà÷åíèå ñâîéñòâà text îáúåêòà obj:
obj.text="òîò æå îáúåêò"
// Âûçîâ ôóíêöèè:
document.write(two(" - íîâîå çíà÷åíèå")+"")
Тестирование сценария выполняется посредством следующего
HTML-кода:




398

Глава 6. Использование объектов

Ëèñòèíã 6.12

Ëèñòèíã 6.12






Результат выполнения сценария представлен на рис. 6.23.

Рис. 6.23. Результат выполнения сценария, в котором передача контекста
выполняется с помощью метода bind()



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
Для отображения в рабочем документе верхнего индекса (операция возведения в степень) использованы дескрипторы и .

В сценарии описывается функция caller() с одним аргументом, который она и возвращает результатом функции. Мы предполагаем, что
аргументом функции caller() будет передаваться другая функция. Та-

399

Часть II. JavaScript и ООП

ким образом, вызов функции caller() означает вызов той функции, имя
которой передано аргументом (во всяком случае, в таком режиме мы
планируем использовать функцию caller()).
Функция show() вспомогательная. У нее один аргумент, значение которого отображается при вызове функции. Также в сценарии создается
объект obj со свойством text, методом hi() (методом отображается значение свойства text), и еще для объекта явно описан метод toString() (описан так, что результатом приведения объекта к текстовому формату
является значение свойства text).
При вызове функций show() через функцию caller() с помощью команды
caller(show)("ôóíêöèÿ show()") получаем ожидаемый результат — в рабочем
документе отображается текст "ôóíêöèÿ show()". Здесь все понятно: при
выполнении команды caller(show)("ôóíêöèÿ show()") на самом деле вызывается функция show() с аргументом "ôóíêöèÿ show()". Вполне ожидаемый
результат и при выполнении команды obj.hi() — методом hi() при вызове
из объекта obj отображается текст "îáúåêò À".
Неожиданностью, скорее всего, будет результат выполнения команды caller(obj.hi)(). Можно было бы ожидать, что в данном случае вызывается метод hi() объекта obj, но в рабочем документе появляется текст
"undefined". Причина в том, что передача функции caller() аргументом
объекта obj.hi не означает передачу контекста вызова метода hi(). Проще говоря, когда мы передаем аргументом конструкцию obj.hi, то из
формата данной инструкции понятно, о каком методе идет речь, но
при вызове метода параметр this не определен. Исправить ситуацию
позволяет метод bind(), который вызывается из объекта функции или
метода. Первым аргументом методу bind передается ссылка, которая
определяет параметр this в теле метода. Если имеются и другие аргументы, они также указываются аргументом метода bind(). Например,
инструкция obj.hi.bind(obj) означает ссылку на метод hi() объекта, который при вызове получает в качестве this ссылку на объект obj. Поэтому
команда caller(obj.hi.bind(obj))() означает не что иное, как вызов из объекта
obj метода hi() через функцию caller(). Результатом является текст "îáúåêò
obj" в рабочем документе.
Еще один пример использования метода bind() реализуется командой var powerOfTwo=Math.pow.bind(Math,2). Здесь переменной powerOfTwo значением присваивается результат выражения Math.pow.bind(Math,2). Сразу
отметим, что это функция и поэтому переменную powerOfTwo следует
интерпретировать как имя функции. Вопрос только в том, какой

400

Глава 6. Использование объектов

функции? Метод bind() вызывается из объекта Math.pow. Метод pow()
встроенного объекта Math вызывается с двумя аргументами и результатом возвращает значение первого аргумента в степени, определяемой вторым аргументом. Аргументы Math и 2 метода bind() означают,
что в метод pow() следует в качестве ссылки this передать объект Math,
а первым аргументом нужно передать значение 2. Недостающий
второй аргумент для метода pow() передается при вызове функции,
записанной в переменную powerOfTwo. Другими словами, выражением
Math.pow.bind(Math,2) определяется функция от одного аргумента. Вызов
функции с определенным аргументом означает вызов метода pow() из
объекта Math с первым аргументом, равным 2, и вторым аргументом —
собственно аргументом функции. Более конкретно, выражение
powerOfTwo(n) означает вычисление значения 2 в степени n (для справки
5
заметим, что 2 = 32).
i


НА ЗАМЕТКУ
В одном из предыдущих примеров мы передавали аргументом
функции ссылку на метод pow() встроенного объекта Math. Собственно, там проблемы с контекстом вызова метода не наблюдалось.
Дело в том, что, хотя формально метод pow() вызывается из объекта
Math, на самом деле при вычислении результата ссылка на сам объект не используется. Поэтому результат метода не зависит от того,
«наполнена ли реальным смыслом» ссылка this.

Наконец, в сценарии определяется функция f() с аргументом x, которая результатом возвращает выражение this+x. Мы используем данную
функцию для определения (с привлечением метода bind()) двух новых
функций. Функции определяются командами var one=f.bind(10) и var two=f.
bind(obj). Выражение f.bind(10) определяет такую функцию: при выполнении кода функции f() в качестве ссылки this передается значение 10.
Таким образом, функцией one() для некоторого аргумента x возвращается значение 10+x. В этом смысле значение выражения one(5) равно 15.
Выражение f.bind(obj) определяет такую функцию: выполняется код
функции f(), в котором ссылка this означает объект obj. Для аргумента x
функцией two() возвращается результат obj+x. Поскольку для объекта
obj определен метод toString(), которым при приведении объекта к текстовому формату возвращается значение свойства text объекта, то фактически вычисляется значение text+x. Если аргумент x текстовый — то
выполняется конкатенация текстовых значений.

401

Часть II. JavaScript и ООП
i


НА ЗАМЕТКУ
Операция типа прибавления к объекту значения вообще обрабатывается методом valueOf(). При этом данный метод должен возвращать
значение простого (базового) типа. Если не так, то автоматически
вызывается метод toString(). Здесь именно такой случай.

При вызове функции two() она «считывает» значение свойства text объекта obj. Легко заметить, что после изменения значения свойства text
объекта obj изменяется и результат вызова функции two().
Еще один небольшой пример, который рассмотрим далее, иллюстрирует некоторые особенности работы с массивами, элементы которых
являются функциями или методами. Рассмотрим программный код
в листинге 6.13.

 Листинг 6.13. Функции и методы как элементы массива
(файл Listing06_13.js)

// Ôóíêöèÿ ñ îäíèì àðãóìåíòîì:
function zero(txt){
document.write(""+txt+"")
}
// Ìåòîä toString() äëÿ îáúåêòà ôóíêöèè zero():
zero.toString=function(){
var t="Íàçâàíèå ôóíêöèè - zero"
t+="Êîëè÷åñòâî àðãóìåíòîâ - "+this.length+""
return t
}
// Ìåòîä ñ îäíèì àðãóìåíòîì:
function one(txt){
document.write(""+txt+"")
for(var s in this){
document.write(this[s]+"")
}
document.write("")
}
// Ìåòîä toString() äëÿ îáúåêòà ìåòîäà one():

402

Глава 6. Использование объектов

one.toString=function(){
var t="Ìåòîä one()"
t+="Êîëè÷åñòâî àðãóìåíòîâ - "+this.length+""
return t
}
// Ïóñòîé ìàññèâ:
var A=[]
// Ïåðâûé (íà÷àëüíûé) ýëåìåíò ìàññèâà:
A[0]=zero
// Âòîðîé (ñ èíäåêñîì îäèí) ýëåìåíò ìàññèâà:
A[1]=one
// Âûçîâ ôóíêöèè (ïåðâûé ýëåìåíò ìàññèâà):
A[0]("Íà÷àëüíûé ýëåìåíò ìàññèâà")
// Âûçîâ ìåòîäà (âòîðîé ýëåìåíò ìàññèâà):
A[1]("Ýëåìåíò ñ åäèíè÷íûì èíäåêñîì")
Работа программного кода проверяется с помощью такого HTML-кода:



Ëèñòèíã 6.13

Ëèñòèíã 6.13






Как выглядит окно браузера с открытым в нем веб-документом, содержащим данный сценарий, показано на рис. 6.24.

403

Часть II. JavaScript и ООП

Рис. 6.24. Результат выполнения сценария, создающего массив,
элементы которого являются функциями

В сценарии описывается функция zero() с одним аргументом и метод
one() тоже с одним аргументом. И для функции, и для метода определяется метод toString(). Благодаря этому попытка «отобразить» название
метода в рабочем документе имеет более приемлемые последствия:
вместо отображения кода функции/метода выводится информация
о названии функции/метода и количестве аргументов (то количество
аргументов, что указано при описании функции/метода). Метод one()
принципиально отличается от функции zero() тем, что содержит в своем коде ключевое слово this.



Д Е ТА Л И
Описание метода toString() как для функции zero(), так и для метода one()
содержит ключевое слово this. Но в теле метода toString() данная ссылка означает объект, для которого вызывается функция или метод, —
то есть объект функции и объект метода соответственно. Ключевое
слово this в теле метода toString(), определяемого для объекта метода
one(), — далеко не то же самое, что ключевое слово this в теле метода
one(). Это разные ссылки, хотя и называются одинаково.

В сценарии создается массив A (сначала пустой), и затем первому его
элементу значением присваивается функция zero() (команда A[0]=zero),
а второму элементу массива значением присваивается метод one()
(коман да A[1]=one). После этого команда A[0]("Íà÷àëüíûé ýëåìåíò ìàññèâà") оз-

404

Глава 6. Использование объектов

начает вызов функции zero() с соответствующим аргументом, а команда A[1]("Ýëåìåíò ñ åäèíè÷íûì èíäåêñîì") означает вызов метода one(). Причем
несложно заметить, что объектом, из которого вызывается метод,
является весь массив. Проще говоря, в метод one() при таком вызове
в качестве this передается ссылка на объект массива A.

Встроенные объекты
— Ты такую машину сделал?
из к/ф «Иван Васильевич меняет профессию»

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

Объект Math
Со встроенным объектом Math мы сталкивались, когда вызывали методы этого объекта при решении небольших математических задач.
Здесь мы расширим наши познания в плане математических возможностей языка JavaScript, реализуемых через объект Math. Объект Math
имеет ряд полезных свойств, значения которых соответствуют наиболее часто используемым математическим константам и выражениям.
Собственные свойства объекта Math представлены в табл. 6.1.
Таблица 6.1. Свойства объекта Math
Свойство

Описание

E

Значение константы Эйлера e ≈ 2,718281828459045

LN10

Значение натурального логарифма от 10:

LN2

Значение натурального логарифма от 2:

LOG10E

Значение десятичного логарифма (то есть логарифма по основанию 10)
от константы Эйлера e:

LOG2E

Значение двоичного логарифма (то есть логарифма по основанию 2) от константы Эйлера e:

PI

Значение иррационального числа π ≈ 3,141592653589793
Значение выражения, равного единице, деленной на корень квадратный из 2:

SQRT1_2
SQRT2

Значение корня квадратного из 2:

405

Часть II. JavaScript и ООП

Также у объекта Math имеется группа методов, каждый из которых
определяет некоторую математическую функцию. Методы объекта
Math для вычисления значения основных математических функций
перечислены в табл. 6.2.
Таблица 6.2. Методы объекта Math
Метод

Описание

abs()

Метод для вычисления модуля числа

acos()

Метод для вычисления арккосинуса числа

asin()

Метод для вычисления арксинуса числа

atan()

Метод для вычисления арктангенса

atan2()

Методом возвращается угол (в радианах) на точку на плоскости, координаты которой определяются аргументами метода. Так, если метод
вызывается в формате Math.atan2(y,x), то результатом возвращается угол на
точку с координатами x и y

ceil()

Метод для округления действительного значения до целого числа.
Результатом возвращается наименьшее возможное целое число, которое
больше или равно значению аргумента метода (округление вверх)

cos()

Метод для вычисления косинуса

exp()

Метод для вычисления экспоненты

floor()

Метод предназначен для округления действительного значения до целого числа. Результатом возвращается набольшее возможное целое число,
которое меньше или равно значению аргумента метода (округление вниз)

log()

Метод для вычисления натурального логарифма

max()

Методом возвращается значение наибольшего из чисел, переданных
аргументами методу

min()

Результатом метода возвращается наименьшее из чисел, переданных
аргументами методу

pow()

Метод для вычисления степени числа. Результатом возвращается
значение, равное значению первого аргумента, возведенного в степень,
определяемую вторым аргументом

random()

Метод для генерирования случайного числа в диапазоне от 0 до 1

round()

Метод предназначен для округления действительных чисел до целочисленных значений. Округление выполняется до ближайшего целого числа

sin()

Метод для вычисления синуса

sqrt()

Метод для вычисления квадратного корня

tan()

Метод для вычисления тангенса



Д Е ТА Л И
В зависимости от типа используемого браузера в объекте Math могут поддерживаться и некоторые другие методы. Так, браузером
Mozilla Firefox поддерживаются методы sinh() (синус гиперболический), cosh() (косинус гиперболический), tanh() (тангенс гиперболичес-

406

Глава 6. Использование объектов

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

Математические методы объекта Math «перекрывают» основные
математические операции. Но список, разумеется, не является
исчерпывающим. В случае необходимости обычно не составляет
труда описать собственную пользовательскую математическую
функцию.

Объект Number
Встроенный объект Number предназначен для работы с числовыми
значениями и позволяет выполнять некоторые полезные операции,
связанные с числовыми данными. Объект используется не часто, но
эффективно.
Объект Number является конструктором — с помощью инструкции
вида new Number(àðãóìåíò) можно создать числовой объект (хотя, как отмечалось выше, потребность в этом возникает не часто). Аргументом
конструктору обычно передается число или текстовое представление числа (текстовая строка, содержащая число). Если аргументом
конструктору передается некорректное значение (значение, которое
не может быть преобразовано в число), то результатом возвращается
значение NaN (сокращение от Not A Number — не число).



Д Е ТА Л И
Числа можно указывать в экспоненциальной нотации. В таком случае число представляется в виде мантиссы (обычно число большее
или равное 1 и меньшее 10), умноженной на 10 в некоторой целочисленной степени. Например, число 123,5 в экспоненциальной но2
тации представляется как 1,235 × 10 , а число 0,0123 записывается
–2
в виде 1,23 × 10 . В программном коде используется представление чисел с символом E (или e): после мантиссы указывается буква
E (или e) и затем показатель степени (положительное значение для
показателя степени указывается со знаком «плюс» или без знака).
2
Например, число 1,235 × 10 записывается в виде 1.235E2, а число
–2
1,23 × 10 записывается как 1.23E-2.

Среди свойств объекта Number можно выделить MAX_VALUE, определяющее максимальное доступное числовое значение (значение

407

Часть II. JavaScript и ООП

1.7976931348623157e+308), а также MIN_VALUE, определяющее минимально
возможное (по модулю), но отличное от нуля число (значение 5e-324).
Методы, которые упоминаются далее, наследуются из прототипа
Number.prototype. Наибольший интерес представляют методы toExponential(),
toFixed() и toPrecision(). Далее приводится краткое описание данных методов, а затем — небольшой пример их использования.


Метод toExponential()предназначен для преобразования числа к формату в экспоненциальной нотации. Аргументом методу передается число, определяющее количество цифр после десятичной точки в мантиссе числа.



Метод toFixed() предназначен для приведения числа к формату
в обычном (не экспоненциальном) представлении. Аргументом
методу передается число, определяющее количество цифр после
десятичной точки в представлении числа.



Методом toPrecision() возвращается текстовая строка, содержащая
представление числа с указанной точностью. Количество цифр
в представлении числа (всех, а не только после десятичной точки)
указывается аргументом метода.

Небольшой пример с использованием упомянутых выше методов
приведен в листинге 6.14.

 Листинг 6.14. Использование методов объекта Number
(файл Listing06_14.js)

var x=new Number(12.3478)
document.write(x.toExponential(10)+"")
document.write(x.toExponential(2)+"")
document.write(x.toFixed(10)+"")
document.write(x.toFixed(2)+"")
document.write(x.toPrecision(5)+"")
document.write(x.toPrecision(2)+"")
Для тестирования кода используем следующий HTML-код:



Ëèñòèíã 6.14

408

Глава 6. Использование объектов


Ëèñòèíã 6.14






Текстовая версия результата выполнения сценария представлена ниже.

 Результат выполнения сценария (из листинга 6.14)
1.2347800000e+1
1.23e+1
12.3478000000
12.35
12.348
12
Как выглядит окно браузера с открытым в нем веб-документом со
сценарием, показано на рис. 6.25.

Рис. 6.25. Результат выполнения сценария, в котором используются методы
объекта Number

409

Часть II. JavaScript и ООП

Желающие могут самостоятельно поэкспериментировать с методами
объекта Number. Мы же на данном объекте больше останавливаться не
будем.

Объект Boolean
Встроенный объект Boolean используется для представления логических значений. Аргументом конструктору Boolean может передаваться
логическое значение (true или false), равно как и значение другого типа.
Если аргумент конструктора не относится к логическому типу, то
правило вычисления логического объекта на основе нелогического
аргумента сводится к следующему:


при нулевом значении аргумента, пустой текстовой строке "", значении null, false, NaN и undefined (а также при отсутствии аргумента)
создается объект, соответствующий логическому значению false;



во всех прочих случаях создается объект, соответствующий логическому значению true.

Важно понимать разницу между объектом, созданным с помощью
конструктора Boolean, и базовыми логическими значениями true и false.
Объект — это объект. Если объект указывается в месте, где должно
быть логическое значение, то выполняется автоматическое приведение данного объекта к логическому значению. Можно было бы ожидать, что объекты, созданные с помощью конструктора Boolean, приводятся в соответствии с их «внутренним» значением. Но это не так.
Действует общее правило преобразования объектов к логическому
типу: если объект отличен от undefined и null, он интерпретируется как
значение true. В этом смысле показательным является программный
код в листинге 6.15.

 Листинг 6.15. Использование объектов, созданных конструктором
Boolean (файл Listing06_15.js)

var myTrue=new Boolean(true)
var myFalse=new Boolean(false)
if(myTrue){
document.write("Îáúåêò myTrue")
}
if(myTrue==true){

410

Глава 6. Использование объектов

document.write("Ñíîâà îáúåêò myTrue")
}
if(myFalse){
document.write("Îáúåêò myFalse")
}
if(myFalse==false){
document.write("Ñíîâà îáúåêò myFalse")
}
Ниже представлен HTML-код для тестирования сценария:



Ëèñòèíã 6.15

Ëèñòèíã 6.15






Результат выполнения сценария такой.

 Результат выполнения сценария (из листинга 6.15)
Îáúåêò myTrue
Ñíîâà îáúåêò myTrue
Îáúåêò myFalse
Ñíîâà îáúåêò myFalse
На рис. 6.26 показано, как выглядит веб-документ со сценарием, если
его открыть в браузере.

411

Часть II. JavaScript и ООП

Рис. 6.26. Результат выполнения сценария, в котором используется
конструктор Boolean

Несложно заметить, что объект myFalse, созданный на основе значения false, в условном операторе интерпретируется как значение true.
Для «выявления сущности» данного объекта используем выражение
myFalse==false, значение которого равняется true.

Объект String
Встроенный объект String предназначен для работы с текстовыми значениями. С помощью конструктора String создается объект, в который
«упакована» текстовая строка. Создать текстовый объект String-типа
можно с помощью команды следующего вида (жирным шрифтом выделены ключевые элементы кода):
var ïåðåìåííàÿ=new String(àðãóìåíò)
Аргументом конструктору String обычно передается текстовое значение (базового типа).



Д Е ТА Л И
Объект, созданный с помощью конструктора String, отличается от базового текстового значения. Другими словами, текстовый литерал
в двойных (или одинарных) кавычках — не одно и то же, что объект,
созданный посредством конструктора String (даже если в объект
«спрятана» точно такая же строка, что и текстовый литерал).
Вместе с тем со значениями базового текстового типа можно использовать методы объекта String. Причина в том, что в JavaScript
действует автоматическое приведение базовых текстовых значе-

412

Глава 6. Использование объектов

ний к объектам String-типа. Если из базового текста вызывается метод, то соответствующий текст базового типа приводится к объекту
String-типа, из которого и вызывается метод.

Среди свойств объекта String достойно упоминания свойство length.
Оно возвращает количество символов в тексте для объекта, у которого запрашивается свойство. Например, значением выражения "abcdefg".
length является число 7, поскольку в текстовой строке "abcdefg" ровно 7
символов.
Некоторые наиболее интересные методы объекта String представлены в табл. 6.3 (за исключением метода fromCharCode(), который является
собственным методом объекта String, все прочие методы наследуются
из прототипа String.prototype). Многие методы напоминают методы для
работы с массивами, и это в принципе не случайно: текстовая строка
представляет собой упорядоченный набор символов, что можно интерпретировать как частный случай массива.
Таблица 6.3. Методы объекта String
Метод

Описание

anchor()

Метод предназначен для программного размещения якоря в вебстранице. Метод вызывается из текста гиперссылки, а аргумент
метода служит значением атрибута name в дескрипторе

charAt()

Метод для определения символа в текстовой строке по его индексу. Индекс символа в строке передается аргументом методу.
Индексация символов в строке начинается с нуля

charCodeAt()

Методом возвращается код в кодовой таблице для символа с определенным индексом в текстовой строке. Индекс передается
аргументом методу

concat()

Методом возвращается текстовая строка, которая получается
объединением текстовой строки, из которой вызывается метод,
а также строк, переданных аргументами методу

indexOf()

Методом возвращается индекс первого вхождения в текстовую
строку фрагмента, указанного аргументом метода. Если в строке данного фрагмента нет, возвращается значение -1. Вторым
аргументом методу можно передать индекс, начиная с которого
выполняется поиск

fromCharCode()

Метод вызывается из объекта String, и результатом возвращается
строка из символов, коды которых передаются аргументами
методу

lastIndexOf()

Методом возвращается индекс последнего вхождения в текстовую строку фрагмента, указанного аргументом метода. Если
в строке данного фрагмента нет, возвращается значение -1.
Второй необязательный аргумент метода определяет индекс,
начиная с которого выполняется поиск

413

Часть II. JavaScript и ООП

Метод

Описание

link()

Метод для создания гиперссылки. Объект, из которого вызывается метод, становится текстом гиперссылки, а адрес перехода
по гиперссылке определяется аргументом метода

localeCompare()

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

match()

Метод используется для сопоставления строки, из которой он
вызывается, и выражения, переданного аргументом методу. Методом возвращается объект Array-типа, содержащий информацию
о результатах сопоставления

replace()

Результатом возвращается текстовая строка, которая получается заменой в исходной строке (из которой вызывается метод) одних фрагментов на другие. Заменяемый фрагмент указывается
первым аргументом метода, а замещающий фрагмент — вторым
аргументом

search()

Методом выполняется поиск выражения, переданного аргументом методу, в строке, из которой вызывается метод. Результатом
возвращается индекс, определяющий позицию первого вхождения искомого фрагмента в строку. Если строка фрагментне
содержит, то результатом возвращается -1

slice()

Методом возвращается подстрока той строки, из которой вызывается метод (сама строка не изменяется). Первый аргумент метода определяет начальную позицию (индекс символа в исходной строке) для считывания символов. Второй необязательный
аргумент — индекс первого не входящего в подстроку символа.
Если второй аргумент не указан, символы считываются до конца
строки. Если второй аргумент отрицательный, то его модуль
определяет позицию символа, начиная с конца строки

split()

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

substr()

Метод возвращает подстроку символов из строки вызова метода.
Аргументами передается начальный индекс символа и количество считываемых символов

substring()

Методом возвращается подстрока той строки, из которой вызывается метод. Аргументами методу передается индекс элемента,
начиная с которого считывается подстрока. Второй необязательный аргумент определяет индекс первого не включенного в подстроку символа. Если второй аргумент не указан, то считывание
выполняется до конца строки

414

Глава 6. Использование объектов

Метод

Описание

toLocaleLowerCase()

Метод для преобразования символов текстовой строки, из
которой вызван метод, в нижний регистр (строчные символы)
с учетом региональных настроек. Исходная строка не изменяется, результатом возвращается новая строка

toLocaleUpperCase()

Метод для преобразования символов текстовой строки, из
которой вызван метод, в верхний регистр (прописные символы)
с учетом региональных настроек. Исходная строка не изменяется, результатом возвращается новая строка

toLowerCase()

Методом возвращается текстовая строка, которая получается
переводом всех символов в нижний регистр в строке, из которой
вызывается метод (в строке-результате все буквы строчные)

toUpperCase()

Методом возвращается текстовая строка, которая получается из
исходной строки (той, из которой вызывается метод) переводом
всех символов в верхний регистр (в строке-результате все буквы
прописные)

trim()

Методом возвращается строка, которая получается из исходной
строки (той, из которой вызывается метод) удалением пробелов
в начале и в конце строки

i


НА ЗАМЕТКУ
Есть еще, например, методы toString() и valueOf(), которые для текстового объекта возвращают «упакованную» в объект текстовую строку.

Как иллюстрация в листинге 6.16 представлены примеры использования некоторых из перечисленных выше методов.

 Листинг 6.16. Методы для работы с текстовыми значениями
(файл Listing06_16.js)

// Èñõîäíàÿ òåêñòîâàÿ ñòðîêà:
var text="Ìû èçó÷àåì JavaScript"
document.write(""+text+"")
// Îáðàùåíèå ê ñèìâîëàì ñòðîêè ÷åðåç èíäåêñ:
for(var k=11;k





































Ó Âàñ îñòàëîñü














520

Глава 7. Веб-документы и сценарии









Äîêóìåíò çàãðóæåí. Ýòî ïîñëåäíÿÿ ñòðîêà â äîêóìåíòå.




























































Документ содержит -блок вывода и две кнопки. Кнопки создаются с помощью -блоков. Стиль кнопок и блока вывода задается
в -блоке.



ЯЗЫК ГИПЕР ТЕКСТОВОЙ РАЗМЕ ТКИ HTML
В описании стиля для кнопок (элементов типа button) установлена
ширина элемента в 180 пикселей, высота равна 30 пикселям, определен жирный шрифт Courier New размера 13.
Для блока вывода (элемент типа div) ширина равна 360 пикселей,
высота составляет 100 пикселей, используется трехмерная ребристая рамка, жирный шрифт Courier New размера 20. Текст вдоль
горизонтали выравнивается по центру (инструкция text-align:center),

615

Часть III. Использование JavaScript

а инструкцией line-height:100px высота строки установлена равной высоте блока, так что вдоль вертикали текст находится примерно посередине.

Каждый из трех блоков (блок вывода и две кнопки) описан с атрибутом id. Из сценария доступ к элементам осуществляется на основе
значения данного атрибута. Сам сценарий начинается с объявления
массива colors, состоящего из пяти элементов. Массив предназначен
для хранения кодов цвета, который применяется в качестве фонового
для блока вывода. Формирование массива происходит при выполнении оператора цикла. При заданном значении индексной переменной
k элемент массива colors[k] получает значением "rgb(0,"+(215+k*10)+",0)". Это
текстовая строка, определяющая код цвета. Несложно заметить, что
мы используем оттенки зеленого цвета.
В сценарии объявлена переменная selected, в которую предполагается
записывать индекс элемента из массива colors, который соответствует выбранному цвету. Переменная используется в функции setColor(),
вызываемой для применения цвета для фона блока вывода в соответствии с текущим значением переменной selected. В теле функции
командой ref=document.getElementById("mydiv") получаем ссылку на объект
блока вывода, после чего командами ref.style.background=colors[selected] и ref.
innerHTML=colors[selected] соответственно задается цвет фона, и код использованного цвета отображается в блоке вывода.
Кроме функции setColor(), в сценарии используется еще несколько
вспомогательных функций. Так, функция next() позволяет выбрать
следующий цвет в массиве и применить его для фона блока вывода.
Выбор цвета выполняется циклически: если на данный момент выбран цвет, соответствующий последнему элементу в массиве colors, то
следующим будет выбран цвет, соответствующий начальному элементу массива. В теле функции командой selected=(selected+1)%colors.length
вычисляется новое значение для индекса, после чего вызовом функции setColor() применяется выбранный цвет (и отображается его код).



Д Е ТА Л И
Значение выражения (selected+1)%colors.length — это остаток от деления
выражения selected+1 на colors.length. Если текущее значение переменной selected меньше индекса colors.length-1 последнего элемента в массиве, то результат выражения (selected+1)%colors.length совпадает со значением selected+1. Если текущее значение переменой selected совпадает

616

Глава 8. Элементы управления и обработка событий

с индексом colors.length-1 последнего элемента в массиве, то новое
значение переменной selected будет равно 0.

Функция для применения предыдущего цвета из массива называется prev(). В ее теле выполняется условный оператор. Если текущее значение переменной selected больше 0, то командой selected-- значение переменной уменьшается на единицу. Если текущее значение переменной
selected нулевое, то новое значение переменной равно colors.length-1 (индекс последнего элемента в массиве). После вычисления нового значения переменной selected, благодаря команде setColor(), изменения, связанные с цветом фона, вступают в силу.
Но центральное место в сценарии занимает функция handler(), которая
используется в качестве обработчика события, связанного с загрузкой
документа, а также событий, связанных со щелчком по любой из кнопок, расположенных в документе. Другими словами, согласно нашему
стратегическому плану, одна и та же функция должна вызываться при
возникновении разных событий, происходящих на разных элементах.
Очевидно, что в таком случае должна существовать возможность уже
в процессе выполнения функции каким-то образом классифицировать
события по типу и месту. Сделать это можно на основе объекта события, который передается аргументом функции. В данном конкретном
случае функция handler() описана с аргументом, который называется evt.
Поскольку функция будет использоваться как обработчик событий,
то мы можем отождествлять ее аргумент evt с объектом события.
В теле функции командами nxt=document.getElementById("next") и prv=document.
getElementById("prev") вычисляются ссылки на объекты кнопок. Далее
в игру вступает условный оператор, в котором проверяется условие
evt.type=="load". В этом выражении мы используем свойство type объекта события. Значением свойства является текст, определяющий тип
события. Для события, связанного с загрузкой, тип события определяется как "load". Поэтому выражение evt.type=="load" истинно, если событие связно с загрузкой ресурса (в данном случае документа).
i


НА ЗАМЕТКУ
Название для типа события обычно можно угадать по названию
соответствующего свойства, через которое реализуется обработчик — отличие в начальной приставке on. Так, для события, связанного со щелчком мышью, событие имеет тип "click", в то время как
для обработки данного события предназначено свойство onclick.

617

Часть III. Использование JavaScript

Если так, и речь идет об обработке события, связанного с загрузкой
(документа), то командой selected=0 присваивается начальное значение
переменной selected, вызовом функции setColor() к блоку вывода применяется цвет фона (и выводится код цвета), а также командами nxt.
onclick=handler и prv.onclick=handler для объектов кнопок задается обработчик
события, связанного со щелчком по кнопке, и этот обработчик — функция handler(). Проще говоря, при вызове функции handler() для обработки события, связанного с загрузкой документа, она сама себя определяет в качестве обработчика для кнопок.
Инструкцию return мы используем для того, чтобы завершить выполнение функции без выполнения прочих команд в ее теле. Поэтому
второй условный оператор в теле функции handler() выполняется только в том случае, если условие в первом условном операторе оказалось
ложным.
Во втором условном операторе проверяется условие evt.type=="click",
истинное в случае, если обрабатывается событие, связанное со щелчком мышью (по кнопке). Но этой информации мало. Необходимо
еще знать, на какой именно кнопке произошло событие. Поэтому
используются вложенные условные операторы, проверяющие объект (кнопку), на котором произошло событие. Для получения ссылки на объект, на котором произошло событие, используем свойство
target объекта события. Так, условие evt.target==nxt истинно в случае,
если событие произошло на объекте nxt. Если так, то выполняется
команда next(), и процесс завершается инструкцией return. Если приведенное выше условие ложно, то проверяется условие evt.target==prv
в следующем условном операторе, и при его истинности вызывается
функция prev().
i


НА ЗАМЕТКУ
Стоит отметить, что в старых версиях браузера Internet Explorer
ссылку на объект, на котором произошло событие, можно получить
через свойство srcElement элемента события. Последние версии этого
браузера поддерживают свойство target.

После описания функции handler() размещена команда window.onload=handler,
которой функция регистрируется как обработчик события, связанного с загрузкой документа.
Как выглядит загруженный в браузер документ, показано на рис. 8.28.

618

Глава 8. Элементы управления и обработка событий

Рис. 8.28. Документ с блоком вывода и двумя кнопками

Последовательные щелчки по кнопке Следующий приводят к «просветлению» зеленого фона блока вывода. На рис. 8.29 показана ситуация, когда по кнопке Следующий выполнено последовательно три
щелчка.

Рис. 8.29. Документ после трех щелчков по кнопке Следующий

На рис. 8.30 показано, как выглядит документ после двух щелчков по
кнопке Предыдущий.

619

Часть III. Использование JavaScript

Рис. 8.30. Документ после двух щелчков по кнопке Предыдущий

Еще раз подчеркнем, что в данном примере мы использовали одну
функцию handler() для обработки события, связанного с загрузкой
документа, и событиями, связанными со щелчком по кнопкам в документе.
Непосредственно в сценарии функция регистрируется как обработчик для события, связанного с загрузкой документа. Когда функция
вызывается первый раз для обработки данного события, в процессе
выполнения кода функции происходит ее регистрация в качестве
обработчика для событий, связанных со щелчком по кнопкам. При
первом и последующих вызовах функции на основе объекта события
производится идентификация типа произошедшего события и, если
необходимо, — элемента, на котором оно произошло.

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

620

Глава 8. Элементы управления и обработка событий

 Листинг 8.9. Вложенные блоки и обработка событий



Ëèñòèíã 8.9


// Îáðàáîò÷èê ñîáûòèÿ, ñâÿçàííîãî ñ çàãðóçêîé äîêóìåíòà:
window.onload=function(){
// Îáðàáîò÷èê äëÿ òðåòüåãî áëîêà:
document.getElementById("divRed").onclick=function(){
showColor("Êðàñíûé")
}
// Îáðàáîò÷èê äëÿ âòîðîãî áëîêà:
document.getElementById("divYellow").onclick=function(){
showColor("Æåëòûé")
}
// Îáðàáîò÷èê äëÿ ïåðâîãî áîêà:
document.getElementById("divGreen").onclick=function(){
showColor("Çåëåíûé")
}
}
// Ôóíêöèÿ äëÿ îòîáðàæåíèÿ â òåêñòîâîì
// áëîêå íàçâàíèÿ öâåòà:
function showColor(clr){
// Òåêñò äëÿ äîáàâëåíèÿ â òåêñòîâûé áëîê:
var txt=""+clr+""
// Ê òåêóùåìó ñîäåðæèìîìó òåêñòîâîãî áëîêà
// äîïèñûâàåòñÿ íîâûé òåêñò:
document.getElementById("result").innerHTML+=txt
}





621

Часть III. Использование JavaScript

Ëèñòèíã 8.9













У внешнего -блока (будем называть его первым) зеленый фон.
В этом блоке размещен еще один -блок желтого цвета (будем называть этот блок вторым). Второй -блок содержит еще один блок (назовем его третьим) с фоном красного цвета. Блоки разного
размера, поэтому перекрываются только частично. Внизу под первым
(самым большим) блоком расположен текстовый блок, выделенный
рамкой. На рис. 8.31 показано, как выглядит документ при загрузке.
В сценарии содержится описание функции showColor(). При вызове функции ей передается текстовый аргумент. В соответствии с кодом функции значение аргумента отображается в текстовом блоке. Обработчик события, связанного с загрузкой документа, содержит команды,
которыми для каждого из -блоков задаются обработчики события, связанного со щелчком по блоку. Все обработчики однотипные:
вызывается функция showColor(), но для каждого блока ей передается
свой аргумент. Для первого зеленого блока функции передается текст
"Çåëåíûé", для второго желтого блока функции передается тест "Æåëòûé",
для третьего красного блока функции передается текст "Êðàñíûé". Таким образом, можно было бы ожидать, что щелчок мышью в области
внутреннего красного блока приведет к отображению в текстовой области значения Красный. Реальный результат может быть несколько
неожиданным. На рис. 8.32 показано, как будет выглядеть документ,
если навести курсор на внутренний красный блок и щелкнуть по
нему левой кнопкой мыши.

622

Глава 8. Элементы управления и обработка событий

Рис. 8.31. Документ с тремя вложенными блоками

Рис. 8.32. Результат щелчка мышью по внутреннему красному блоку

623

Часть III. Использование JavaScript

Видим, что последовательно появились три сообщения (Красный,
Желтый и Зеленый). Нечто подобное происходит, если продолжить
наши эксперименты. Так, перезагрузим документ и щелкнем по среднему желтому блоку. Результат показан на рис. 8.32.

Рис. 8.33. Результат щелчка мышью по среднему желтому блоку

В данном случае появляется два сообщения: Желтый и Зеленый.
Наконец, что будет, если после перезагрузки документа щелкнуть по
внешнему зеленому блоку, показано на рис. 8.34.
В этом случае появляется только одно сообщение: Зеленый.
i


НА ЗАМЕТКУ
Если перед щелчком мышью по блоку не произвести перезагрузку документа, то новые сообщения в текстовом блоке будут добавляться к уже существующим.

Объяснение происходящего кроется в способе, которым обрабатываются события. Если некоторое событие происходит на вложенном

624

Глава 8. Элементы управления и обработка событий

компоненте (таком, например, как третий красный блок), то после его
обработки обработчиком этого компонента событие, как правило, передается дальше, контейнеру. Если у контейнера имеется обработчик
данного типа событий, событие обрабатывается контейнером и снова
передается к контейнерному элементу. В нашем случае при щелчке
по красному блоку событие обрабатывается обработчиком красного
блока и передается для обработки в желтый блок. При обработке события в желтом блоке оно передается дальше в зеленый блок. Поэтому при щелчке по красному блоку появляется три сообщения, а не
одно. При щелчке по желтому блоку событие обрабатывается обработчиком желтого блока и передается для обработки в зеленый блок.

Рис. 8.34. Результат щелчка мышью по внешнему зеленому блоку

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

625

Часть III. Использование JavaScript

«путешествие» от самого внешнего компонента к вложенному компоненту. Просто на этом «прямом пути» события по умолчанию не обрабатываются. Первая обработка начинается, когда объект события
«достигает дна». На нашем примере это выглядит примерно так:


объект события передается от зеленого блока желтому;



от желтого блока объект события передается красному;



в красном блоке событие обрабатывается и передается в желтый
блок;



в желтом блоке событие обрабатывается, и объект события передается в зеленый блок;



событие обрабатывается в зеленом блоке.

Для нас во всей этой схеме важны два момента. Во-первых, процесс
триумфальной передачи объекта события для обработки от компонента к компоненту можно остановить. Для этого из объекта события
следует вызвать метод stopPropagation(). Во-вторых, существует возможность перехватывать события на «прямом пути», а не на «обратном»,
как происходит по умолчанию. Для этого обработчик события следует добавлять с помощью метода addEventListener(), причем последним
аргументом метода должно быть указано значение true (первый аргумент — тип события, второй аргумент — ссылка на функцию-обработчик).
Небольшая модификация предыдущего документа представлена
в листинге 8.10 (для удобства основные комментарии удалены, а новые важные места кода выделены жирным шрифтом).

 Листинг 8.10. Перехват событий



Ëèñòèíã 8.9

window.onload=function(){
//  îáðàáîò÷èêå èñïîëüçóåòñÿ îáúåêò ñîáûòèÿ:
document.getElementById("divRed").onclick=function(evt){
showColor("Êðàñíûé")

626

Глава 8. Элементы управления и обработка событий

// Áëîêèðîâêà ïåðåäà÷è îáúåêòà ñîáûòèÿ:
evt.stopPropagation()
}
document.getElementById("divYellow").onclick=function(){
showColor("Æåëòûé")
}
// Ïåðåõâàò ñîáûòèÿ:
document.getElementById("divGreen").addEventListener("click",function()
{showColor("Çåëåíûé")},true)
}
function showColor(clr){
var txt=""+clr+""
document.getElementById("result").innerHTML+=txt
}



Ëèñòèíã 8.10









По сравнению с предыдущим документом здесь есть два принципиальных изменения. Во-первых, обработчик для третьего (красного)
блока описан с аргументом (объект события evt), и в тело обработчика
добавлена инструкция evt.stopPropagation(). Поэтому после обработки события дальше оно передаваться не будет.
Во-вторых, добавление обработчика для первого (зеленого) блока выполняется командой document.getElementById("divGreen").addEventListener("click",func

627

Часть III. Использование JavaScript

tion(){showColor("Çåëåíûé")},true). Важно здесь то, что обработчик добавляется с помощью метода addEventListener(). Первым аргументом методу передается тип события "click". Второй аргумент — анонимная функция,
описанная как function(){showColor("Çåëåíûé")}. Она будет вызываться при
обработке события. Третий аргумент true означает, что событие следует перехватывать при «прямой передаче». Последствия от внесенных
изменений такие. Если в документе щелкнуть по красному блоку, появятся сообщения Зеленый и Красный, как показано на рис. 8.35.

Рис. 8.35. При щелчке по красному блоку появляются сообщения
Зеленый и Красный

При щелчке (после перезагрузки документа) по желтому блоку появятся сообщения Зеленый и Желтый, как показано на рис. 8.36.
При щелчке по зеленому блоку появляется сообщение Зеленый. Ситуация проиллюстрирована на рис. 8.37.
Во всех рассмотренных случаях важно не только то, что появляется
сообщение Зеленый, но и то, что оно появляется первым. Объясняется все просто. Например, пользователь выполнил щелчок по красному блоку.

628

Глава 8. Элементы управления и обработка событий

Рис. 8.36. При щелчке по желтому блоку появляются сообщения
Зеленый и Желтый

Рис. 8.37. При щелчке по зеленому блоку появляется сообщение Зеленый

629

Часть III. Использование JavaScript

Соответствующее событие последовательно передается от зеленого
блока через желтый в красный блок. Но обработчик зеленого блока
описан так, что событие обрабатывается уже на этом этапе, то есть
при прямой передаче. Поэтому появляется сообщение Зеленый.
У желтого блока обработчик описан иначе, поэтому желтый блок
при прямой передаче события его не обрабатывает. Событие доходит до красного блока и пытается начать свой обратный путь. Происходит обработка события красным блоком. Появляется сообщение Красный. Но поскольку в обработчике красного блока процесс
дальнейшей передачи события блокируется, то на этом все и заканчивается.
Если пользователь щелкнул по желтому блоку, то, как и в предыдущем случае, сначала событие обрабатывается зеленым блоком, а затем передается в желтый. Происходит обработка, и событие начинает
передаваться в обратном направлении. Но поскольку в зеленом блоке
событие уже обработано, то второй раз сообщение Зеленый не отображается. Ситуация, когда пользователь выполняет щелчок по зеленому блоку, представляется тривиальной.

Резюме
Ничего не видел, все люди братья, все должны помогать друг другу.
из к/ф «Гостья из будущего»

Наиболее важные моменты, рассмотренные в этой главе, можно сформулировать следующим образом.


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



При работе с элементами управления обычно определяют обработчики событий, на которые должны «реагировать» элементы.
Среди наиболее актуальных событий можно выделить связанные
с загрузкой документа, щелчком кнопкой мыши, нажатием и отпусканием клавиш, передачей и потерей фокуса, изменением состояния элементов управления и рядом других.

630

Глава 8. Элементы управления и обработка событий



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



При обработке событий следует учитывать механизм передачи
объекта событий при наличии вложенных компонентов, на которых происходят события.



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

Глава 9
РАЗЛИЧНЫЕ ПРИМЕРЫ

Тихо, кричать не надо. Каждая ваша мысль
нам известна.
из к/ф «Гостья из будущего»

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

Триадная кривая Коха
Как говорит наш дорогой шеф, в нашем деле
главное — этот самый реализм.
из к/ф «Бриллиантовая рука»

Мы рассмотрим задачу о построении триадной кривой Коха. Алгоритм построения кривой проиллюстрируем с помощью документа,
созданного средствами HTML с использованием сценария, написанного на языке JavaScript. После этого проанализируем код документа
и сценария.
Итак, сначала берется единичный отрезок, как, например, на рис. 9.1.
Это нулевая итерация в построении кривой. Следующая, первая итерация выполняется так: исходная прямая условно разделяется на три
равные части, и на основании центральной трети строится равносторонний треугольник, после чего центральная треть отбрасывается.
Что получается в итоге, показано на рис. 9.2.

632

Глава 9. Различные примеры

Рис. 9.1. Вид документа при загрузке: в нижней части графической области
отображена прямая линия

Рис. 9.2. Первая итерация при построении триадной кривой Коха

На следующей итерации каждый из четырех отрезков ломаной кривой разделяется на три равные части. Каждая центральная часть
служит основанием для равностороннего треугольника, а сами центральные части отбрасываются. Результат выполнения второй итерации показан на рис. 9.3.

633

Часть III. Использование JavaScript

Рис. 9.3. Вторая итерация при построении триадной кривой Коха

Далее действуем по той же схеме: каждый отрезок кривой разбивается на три равные части, на основании центральных частей строятся
равносторонние треугольники, а затем центральные части отбрасываются. Результат, который получаем на третьей итерации, показан
на рис. 9.4. Как выглядит кривая после четырех итераций, показано
на рис. 9.5.

Рис. 9.4. Третья итерация при построении триадной кривой Коха

634

Глава 9. Различные примеры

Рис. 9.5. Четвертая итерация при построении триадной кривой Коха

Пятая итерация представлена на рис. 9.6.

Рис. 9.6. Пятая итерация при построении триадной кривой Коха

Итерации можно продолжать и дальше, но с увеличением порядка итерации размер характерных деталей кривой становится очень
маленьким. Вообще же представленный выше документ позволяет

635

Часть III. Использование JavaScript

отображать до девяти итераций. Номер итерации выбирается в раскрывающемся списке, расположенном над графической областью.
Проанализируем код документа, позволяющего проследить процесс
построения триадной кривой Коха. Интересующий нас код представлен в листинге 9.1.

 Листинг 9.1. Триадная кривая Коха



Ëèñòèíã 9.1


// Ïåðåìåííûå äëÿ çàïèñè ññûëîê íà ãðàôè÷åñêóþ îáëàñòü,
// îáúåêò ãðàôè÷åñêîãî êîíòåêñòà è îáúåêò
// ðàñêðûâàþùåãîñÿ ñïèñêà:
var cnv,ctx,sel
// Ôóíêöèÿ äëÿ âû÷èñëåíèÿ ïîñëåäîâàòåëüíîñòè
// òî÷åê äëÿ êðèâîé Êîõà:
function getPoints(a,b){
// Âñïîìîãàòåëüíûå ïåðåìåííûå:
var p,k,i,j,phi
// Ìàññèâ ñ äâóìÿ íà÷àëüíûìè òî÷êàìè:
var pts=new Array(2)
// Ìàññèâû - ýëåìåíòû:
for(i=0;i
















654

Глава 9. Различные примеры







































704

Глава 9. Различные примеры