JavaScript Формы
Цель работы: научиться управлять формой через сценарий.
Форма служит для ввода пользователем через окно браузера данных и передачи их на веб-сервер. Форма состоит из контейнера <FORM> …</form> и заключённых в него тегов (элементов) <INPUT>, <SELECT> и <TEXTAREA>.
Проверка данных перед отправкой на сервер
Для уменьшения нагрузки на сеть и веб-сервер можно проверять введённые пользователем данные на браузере с помощью сценария на JavaScript. Если в данных обнаружится ошибка, то пользователю предоставляется возможность её исправить. Введенные правильно данные отправляются на веб-сервер. Использование сценария для управления формой демонстрируется в примере 3.1.
Пример 3.1
Пример 3.1 состоит из двух страниц.Первая страница служит для ввода пользователем данных и их проверки с помощью скрипта, написанного на JavaScript. Если данные введены правильно, то они отправляются на веб-сервер. На веб-сервере полученные данные обрабатываются скриптом, написанным на языке PHP, формируется и пересылается на браузер пользователя новая страница.
В скрипте,написанном на JavaScript, для доступа к данным, находящимся в форме, используются имена и индексы элементов формы. Для задания адреса (URL) страницы, содержащей PHP-скрипт, используется свойствоaction объекта Form.
Получение данных из всплывающего списка
Иногда можно полностью решать задачу ведения диалога с пользователем средствами JavaScript, не обращаясь к веб-серверу. В примере 3.2 пользователь вводит код цвета в модели RGB и выбирает из списка название цвета. После нажатия кнопки Ввод на экран выводятся окрашенные в выбранные цвета код и название цвета.
Пример 3.2
В примере 3.2 нет необходимости использовать контейнер <FORM> …</form>, так как на веб-сервер ничего не передаётся. Для вывода на экран кода и названия цвета используется свойство innerHTML. Строго говоря, innerHTML следовало бы называть не свойством, а методом. Рассмотрим применение этого свойства на примере вывода на экран введённого пользователем кода цвета. Пусть пользователь ввёл код цвета a=FF0000, т.е. красный.
В результате выполнения оператора
e=»<FONT size=7 color=FF0000>FF0000</font>»
В результате выполнения оператора
в элемент <B ></b> вставится значение переменной e:
и браузер выполнит преобразованный злемент (оператор языка HTML), т.е. выведет на экран
FF0000
Таким образом, с помощью свойства innerHTML можно на стороне браузера программным путём вносить изменения в HTML-документ (страницу сайта).
Задача 3.1. Создайте сайт из двух страниц. Первая страница имеет заголовок Заказ мебели. На ней расположены два поля со списками (теги <SELECT>), поле (<INPUT>) и кнопка (<SUBMIT>). Из первого поля со списком пользователь выбирает изделие (шкаф, стол, сервант и т.д.).Из второго поля со спискомпользователь выбирает материал (дуб,орех, бук). В третье поле нужно ввести количество заказываемых изделий. После ввода данных необходимо проверить, все ли данные введены.Если обнаружена ошибка, то нужно вывести сообщение и предложить её исправить.
Проверка данных сразу после ввода
Если в форме нужно заполнить много полей, то пользователю удобно получать сообщения об ошибках сразу после окончания ввода данных в очередное поле, то есть после нажатия клавиши Tab или клавиши со стрелкой. Для немедленной проверки введённых данных служит событие onchange:
<INPUT TYPE =»text»SIZE=6 onchange=»arg(this)»>
Функция, вызываемая событием onchange, имеет примерно такую структуру:
Методы focus() и select() служат для возвращения курсора мышки в поле ввода и выделения ошибочных данных. Эти методы без использования специальных приёмов правильно работают только в браузере Mozilla.

Задача 3.2. Создайте страницу (рис. 1) для вычисления тригонометрических функций. Вводимые пользователем данные должны проверяться немедленно после ввода и после нажатия кнопки Вычислить.
Указания. Не забудьте перевести градусы в радианы. Название тригонометрической функции можно передавать как параметр тега :
Сформируйте текстовую строку вида
Затем воспользуйтесь функцией eval(строка), которая выполняет выражение, хранящееся в строке.
Основы JavaScript
JavaScript — мультипарадигменный язык программирования. Поддерживает объектно-ориентированный, императивный и функциональный стили. JavaScript обычно используется как встраиваемый язык для программного доступа к объектам приложений. Наиболее широкое применение находит в браузерах как язык сценариев для придания интерактивности web-страницам.
JavaScript является интерпретируемым языком, это означает, что код на языке JavaScript выполняется с помощью интерпретатора. Он получает инструкции языка JavaScript, которые определены на web-странице, выполняет их (или интерпретирует).
2. Основы синтаксиса
2.1. Инструкции
Код JavaScript состоит из инструкций, каждая из которых завершается точкой запятой:
Однако современные браузеры вполне могут различать отдельные инструкции, если они просто располагаются на отдельных строках без точки запятой:
Но чтобы улучшить читабельность кода и снизить число возможных ошибок, рекомендуется определять каждую инструкцию JavaScript на отдельной строчке и завершать ее точкой с запятой.
2.2. Комментарии
В коде JavaScript могут использоваться комментарии, они не обрабатываются интерпретатором JavaScript и никак не учитываются в работе программы. Комментарии предназначен для ориентации по коду, чтобы указать, что делает тот или иной код.
Комментарии могут быть однострочными, для которых используется двойной слэш // :
Кроме однострочных комментариев могут использоваться и многострочные комментарии. Такие комментарии заключаются между символами /* текст комментария */ . Например:
2.3. Переменные
Для хранения данных в программе используются переменные, они предназначены для хранения каких-нибудь временных данных или таких данных, которые в процессе работы могут менять свое значение.
Для создания переменных применяются ключевые слова var и let . Например, объявим переменную myIncome :
Каждая переменная имеет имя, оно представляет собой произвольный набор алфавитно-цифровых символов, знака подчеркивания _ или знака доллара $ , причем названия не должны начинаться с цифровых символов. То есть можно использовать в названии буквы, цифры, подчеркивание. Однако все остальные символы запрещены.
Например, правильные названия переменных:
Следующие названия являются некорректными и не могут использоваться:
Также нельзя давать переменным такие имена, которые совпадают с зарезервированными ключевыми словами. В JavaScript не так много ключевых слов, поэтому данное правило несложно соблюдать. Например, следующее название будет некорректным, так как for — ключевое слово в JavaScript:
Список зарезервированных слов в JavaScript:
При названии переменных надо учитывать, что JavaScript является регистрозависимым языком, то есть в следующем коде объявлены две разные переменные:
Через запятую можно определить сразу несколько переменных:
С помощью знака равно = можно присвоить переменной какое-либо значение:
Процесс присвоения переменной начального значения называется инициализацией. Теперь переменная income будет хранить число 300 , а переменная price — число 76 . Отличительной чертой переменных является то, что можно изменить их значение:
2.4. Константы
С помощью ключевого слова const можно определить константу, которая, как и переменная, хранит значение, однако это значение не может быть изменено.
Если попробовать изменить ее значение, то возникнет ошибка:
Также стоит отметить, что поскольку нельзя изменить значение константы, то константа должна быть инициализирована, то есть при ее определении необходимо предоставить ей начальное значение. Если этого не сделать, возникнет ошибка:
2.5. Типы данных
Все используемые данные в JavaScript имеют определенный тип. В JavaScript имеется пять примитивных типов данных:
String : представляет строку
Number : представляет числовое значение
Boolean : представляет логическое значение true или false
undefined : указывает, что значение не установлено
null : указывает на неопределенное значение
Все данные, которые не попадают под вышеперечисленные пять типов, относятся к типу object .
2.5.1. Числовые данные
Числа в JavaScript могут иметь две формы:
Целые числа, например, 35 . Можно использовать как положительные, так и отрицательные числа. Диапазон используемых чисел: от -2^53 до 2^53 .
Дробные числа (числа с плавающей точкой), например, 3.5575 . Опять же можно использовать как положительные, так и отрицательные числа. Для чисел с плавающей точкой используется тот же диапазон: от -2^53 до 2^53 .
В качестве разделителя между целой и дробной частями, как и в других языках программирования, используется точка.
2.5.2. Строки
Тип string представляет строки, то есть такие данные, которые заключены в кавычки. Причем можно использовать как двойные, так и одинарные кавычки.
Единственно ограничение: тип закрывающей кавычки должен быть тот же, что и тип открывающей, то есть либо обе двойные, либо обе одинарные кавычки.
Если внутри строки встречаются кавычки, то их нужно экранировать слэшем \ . Например, пусть у нас есть текст Бюро «Рога и копыта» . Теперь экранируем кавычки:
Также можно внутри стоки использовать другой тип кавычек:
2.5.3. Тип Boolean
Тип Boolean представляет булевы или логические значения true и false (то есть да или нет):
2.5.4. null и undefined
Нередко возникает путаница между null и undefined . Итак, когда только определяется переменная без присвоения ей начального значения, она представляет тип undefined :
Присвоение значение null означает, что переменная имеет некоторое неопределенное значение (не число, не строка, не логическое значение), но все-таки имеет значение. undefined означает, что переменная не имеет значения.
2.5.5. object
Тип object представляет сложный объект. Простейшее определение объекта представляют фигурные скобки:
Объект может иметь различные свойства и методы:
В данном случае объект называется user , и он имеет два свойства: name и age . Это краткое описание объектов.
2.5.6. Слабая типизация
JavaScript является языком со слабой типизацией. Это значит, что переменные могут динамически менять тип.
Несмотря на то, что во втором и третьем случае консоль выведет число 45 , но во втором случае переменная xNumber будет представлять число, а в третьем случае — строку.
Это важный момент, который надо учитывать. От этого зависит поведение переменной в программе:
Выше в обоих случая к переменной xNumber применяется операция сложения + . Но в первом случае xNumber представляет число, поэтому результатом операции xNumber + 5 будет число 50 .
Во втором случае xNumber представляет строку. Но операция сложения между строкой и числом 5 невозможна. Поэтому число 5 будет преобразовываться к строке, и будет происходить операция объединения строк. И результатом выражения xNumber + 5 будет стока «455» .
2.6. Операторы
2.6.1. Оператор typeof
С помощью оператора typeof можно получить тип переменной:
2.6.2. Математические операторы
JavaScript поддерживает все базовые математические операции:
Сложение:
Вычитание:
Умножение:
Деление:
Деление по модулю (оператор % ) возвращает остаток от деления:
Результатом будет 5 , так как наибольшее целое число, которое меньше или равно 40 и при этом делится на 7 равно 35 , а 40 — 35 = 5 .
Инкремент
Оператор инкремента ++ увеличивает переменную на единицу. Существует префиксный инкремент, который сначала увеличивает переменную на единицу, а затем возвращает ее значение:
И есть постфиксный инкремент, который сначала возвращает значение переменной, а затем увеличивает его на единицу:
Постфиксный инкремент аналогичен операции:
Декремент
Декремент уменьшает значение переменной на единицу. Также есть префиксный и постфиксный декремент:
Как и принято в математике, все операции выполняются слева направо и различаются по приоритетам: сначала операции инкремента и декремента, затем выполняются умножение и деление, а потом сложение и вычитание. Чтобы изменить стандартный ход выполнения операций, часть выражений можно поместить в скобки:
2.6.3. Операции присваивания
=
Приравнивает переменной определенное значение: var x = 5 ;
+=
Сложение с последующим присвоением результата.
-=
Вычитание с последующим присвоением результата.
*=
*Умножение* с последующим присвоением результата:
/=
Деление с последующим присвоением результата:
%=
Получение остатка от деления с последующим присвоением результата:
2.6.4. Операторы сравнения
Как правило, для проверки условия используются операторы сравнения. Операторы сравнения сравнивают два значения и возвращают значение true или false :
==
Оператор равенства сравнивает два значения, и если они равны, возвращает true , иначе возвращает false : x == 5
===
Оператор тождественности также сравнивает два значения и их тип, и если они равны, возвращает true , иначе возвращает false : x === 5
!=
Сравнивает два значения, и если они не равны, возвращает true , иначе возвращает«false`: x != 5
!==
Сравнивает два значения и их типы, и если они не равны, возвращает true , иначе возвращает false : x !== 5
>
Сравнивает два значения, и если первое больше второго, то возвращает true , иначе возвращает false : x > 5
<
Сравнивает два значения, и если первое меньше второго, то возвращает true , иначе возвращает false : x < 5
>=
Сравнивает два значения, и если первое больше или равно второму, то возвращает true , иначе возвращает false : x >= 5
<=
Сравнивает два значения, и если первое меньше или равно второму, то возвращает true , иначе возвращает false : x <= 5
Все операторы довольно просты, наверное, за исключением оператора равенства и оператора тождественности. Они оба сравнивают два значения, но оператор тождественности также принимает во внимание и тип значения.
Переменная result здесь будет равна true , так как фактически и income , и strIncome представляют число 100 .
Но оператор тождественности возвратит в этом случае false , так как данные имеют разные тип:
Аналогично работают операторы неравенства != и !== .
2.6.5. Логические операции
Логические операции применяются для объединения результатов двух операций сравнения. В JavaScript есть следующие логические операции:
&&
Возвращает true , если обе операции сравнения возвращают true , иначе возвращает false :
||
Возвращает true , если хотя бы одна операция сравнения возвращают true , иначе возвращает false :
!
Возвращает true , если операция сравнения возвращает false :
2.6.6. Операции со строками
Строки могут использовать оператор + для объединения.
Если одно из выражений представляет строку, а другое — число, то число преобразуется к строке и выполняется операция объединения строк:
2.6.7. Пример
Программа, которая продемонстрирует работу с операциями над переменными.
В скрипте объявляются три переменных: sum , percent и income . Переменная income вычисляется по остальным двум переменным с помощью операций умножения и деления. И в конце ее значение суммируется со значением переменной sum .
И консоль браузера выведет:

2.7. Преобразование данных
Нередко возникает необходимость преобразовать одни данные в другие. Например:
Обе переменных представляют строки, а точнее строковые представления чисел. И в итоге получим не число 50 , а строку 464 . Но было бы неплохо, если бы их тоже можно было бы складывать, вычитать, в общем работать как с обычными числами.
В этом случае можно использовать операции преобразования. Для преобразования строки в число применяется функция parseInt() :
Для преобразования строк в дробные числа применяется функция parseFloat() :
При этом строка может иметь смешанное содержимое, например, 123hello , то есть в данном случае есть цифры, но есть и обычные символы. Но метод parseInt() все равно попытается выполнить преобразование:
Если методу не удастся выполнить преобразование, то он возвращает значение NaN (Not a Number), которое говорит о том, что строка не представляет число и не может быть преобразована.
С помощью специальной функции isNaN() можно проверить, представляет ли строка число. Если строка не является числом, то функция возвращает true , если это число — то false :
Выше был рассмотрен перевод строк в числа в десятичной системе. Однако можно переводить числа в любую систему. По умолчанию интерпретатор JavaScript сам определяет, в число из какой системы исчисления хотели преобразовать строку, как правило, выбирается десятичная система. Но можно с помощью второго параметра явно указать, что хотим преобразовать строку в число в определенной системе.
Результатом будет 6 , так как 110 в двоичной системе — это число 6 в десятичной.
2.7.1. Пример
Теперь напишем небольшую программу, в которой используем операции с переменными:
С помощью функции prompt() в браузере выводится диалоговое окно с предложением ввести некоторое значение. Второй аргумент в этой функции указывает на значение, которое будет использоваться по умолчанию.
Однако функция prompt() возвращает строку. Поэтому эту строку необходимо преобразовать в число, чтобы выполнить с ней операции.
После открытия страницы в браузере увидим приглашение к вводу суммы вклада:

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

2.8. Массивы
Для работы с наборами данных предназначены массивы. Для создания массива применяется выражение new Array() :
Существует также более короткий способ инициализации массива:
В данном случае создаётся пустой массив. Но можно также добавить в него начальные данные:
В этом случае в массиве myArray будет три элемента. Его можно представить в виде таблицы так:
Для обращения к отдельным элементам массива используются индексы. Отсчет начинается с нуля, то есть первый элемент будет иметь индекс 0, а последний — 2:
Если попробовать обратиться к элементу по индексу больше размера массива, то получим undefined :
Также по индексу осуществляется установка значений для элементов массива:
Причем в отличие от других языков, как Java или C#, можно установить элемент, который изначально не установлен:
Также стоит отметить, что в отличие от ряда языков программирования в JavaScript массивы не являются строго типизированными, один массив может хранить данные разных типов:
2.8.1. spread-оператор
spread-оператор … позволяет взять значения из массива по отдельности:
spread-оператор указывается перед массивом. В результате выражение …numbers возвратит набор чисел, но это будет не массив, а именно отдельные значения.
2.8.2. Многомерные массивы
Массивы могут быть одномерными и многомерными. Каждый элемент в многомерном массиве может представлять собой отдельный массив. Выше рассмотрели одномерный массив, теперь создадим многомерный массив:
Визуально оба массива можно представить следующим образом:
Поскольку массив numbers2 двухмерный, он представляет собой простую таблицу. Каждый его элемент может представлять отдельный массив.
Рассмотрим еще один двумерный массив:
Массив people можно представить в виде следующей таблицы:
Чтобы получить отдельный элемент массива, также используется индекс:
Только теперь переменная tomInfo будет представлять массив. Чтобы получить элемент внутри вложенного массива, необходимо использовать его вторую размерность:
То есть если визуально двумерный массив можно представить в виде таблицы, то элемент people[0][1] будет ссылаться на ячейку таблицы, которая находится на пересечении первой строки и второго столбца (первая размерность — 0 — строка, вторая размерность — 1 — столбец).
Также можно выполнить присвоение:
При создании многомерных массивов не ограничены только двумерными, но также можем использовать массивы больших размерностей:
2.9. Условные конструкции
Условные конструкции позволяют выполнить те или иные действия в зависимости от определенных условий.
2.9.1. Выражение if
Конструкция if проверяет некоторое условие и если это условие верно, то выполняет некоторые действия. Общая форма конструкции if :
Здесь в конструкции if используется следующее условие: income > 50 . Если это условие возвращает true , то есть переменная income имеет значение больше 50 , то браузер отображает сообщение. Если же значение income меньше 50 , то никакого сообщения не отображается.
Если необходимо выполнить по условию набор инструкций, то они помещаются в блок из фигурных скобок:
Причем условия могут быть сложными:
Конструкция if позволяет проверить наличие значения.
Если переменная myVar имеет значение, то в условной конструкции она возвратит значение true .
Но нередко для проверки значения переменной используют альтернативный вариант — проверяют на значение undefined :
В конструкции if также можно использовать блок else . Данный блок содержит инструкции, которые выполняются, если условие после if ложно, то есть равно false :
С помощью конструкции else if можно добавить альтернативное условие к блоку if :
В данном случае выполнится блок else if . При необходимости можно использовать несколько блоков else if с разными условиями:
2.9.2. true или false
В JavaScript любая переменная может применяться в условных выражениях, но не любая переменная представляет тип boolean . Поэтому возникает вопрос, что возвратит та или иная переменная — true или false ? Много зависит от типа данных, который представляет переменная:
undefined
Возвращает false
null
Возвращает false
Boolean
Если переменная равна false , то возвращается false . Соответственно, если переменная равна true , то возвращается true
Number
Возвращает false , если число равно 0 или NaN (Not a Number), в остальных случаях возвращается true
String
Возвращает false , если переменная равна пустой строке, то есть ее длина равна 0, в остальных случаях возвращается true
Object
Всегда возвращает true
2.9.3. Конструкция switch..case
Конструкция switch..case является альтернативой использованию конструкции if..else if..else и также позволяет обработать сразу несколько условий:
После ключевого слова switch в скобках идет сравниваемое выражение. Значение этого выражения последовательно сравнивается со значениями, помещенными после оператора сase . И если совпадение будет найдено, то будет выполняться определенный блок сase .
В конце каждого блока сase ставится оператор break , чтобы избежать выполнения других блоков.
Если есть необходимость обработать ситуацию, когда совпадения не будет найдено, то можно добавить блок default :
2.9.4. Тернарная операция
Тернарная операция состоит из трех операндов и имеет следующее определение:
[первый операнд — условие] ? [второй операнд] : [третий операнд]
В зависимости от условия тернарная операция возвращает второй или третий операнд: если условие равно true , то возвращается второй операнд; если условие равно false , то третий. Например:
Если значение переменной a меньше значения переменной b , то переменная result будет равняться a + b . Иначе значение result будет равняться a — b .
2.10. Циклы
Циклы позволяют в зависимости от определенных условий выполнять некоторое действие множество раз. В JavaScript имеются следующие виды циклов:
2.10.1. Цикл for
Цикл for имеет следующее формальное определение:
Например, используем цикл for для перебора элементов массива:
Первая часть объявления цикла — var i = 0 — создает и инициализирует счетчик — переменную i . И перед выполнением цикла ее значение будет равно 0 . По сути это то же самое, что и объявление переменной.
Вторая часть — условие, при котором будет выполняться цикл. В данном случае цикл будет выполняться, пока значение i не достигнет величины, равной длине массива people . Получить длину массива можно с помощью свойства length: people.length .
Третья часть — приращение счетчика на единицу.
И так как в массиве 4 элемента, то блок цикла сработает 4 раза, пока значение i не станет равным people.length (то есть 4 ). И каждый раз это значение будет увеличиваться на 1. Каждое отдельное повторение цикла называется итерацией. Таким образом, в данном случае сработают 4 итерации.
А с помощью выражения people[i] можно получить элемент массива для его последующего вывода в браузере.
Необязательно увеличивать счетчик на единицу, можно производить с ним другие действия, например, уменьшать на единицу:
В данном случае массив выводится с конца, а перебор массива начинается с i = 3 до i = 0 .
2.10.2. Цикл for..in
Цикл for..in предназначен для перебора массивов и объектов. Его формальное определение:
Например, переберем элементы массива:
2.10.3. Цикл for…of
Цикл for…of похож на цикл for…in и предназначен для перебора коллекций, например, массивов:
Текущий перебираемый элемент коллекции помещается в переменную val , значение которой затем выводится на консоль.
2.10.4. Цикл while
Цикл while выполняется до тех пор, пока некоторое условие истинно. Его формальное определение:
Опять же выведем с помощью while элементы массива:
Цикл while здесь будет выполняться, пока значение index не станет равным длине массива.
2.10.5. Цикл do..while
В цикле do сначала выполняется код цикла, а потом происходит проверка условия в инструкции while . И пока это условие истинно, цикл повторяется. Например:
Здесь код цикла сработает 9 раз, пока x не станет равным 10 . При этом цикл do гарантирует хотя бы однократное выполнение действий, даже если условие в инструкции while не будет истинно.
2.10.6. Операторы continue и break
Иногда бывает необходимо выйти из цикла до его завершения. В этом случае можно воспользоваться оператором break :
Данный цикл перебирает все элементы массива, однако последние четыре элемента не будут выведены в браузере, поскольку поверка if (array[i] > 10) прервет выполнение цикла с помощью оператора break , когда перебор массива дойдет до элемента 12 .
Если необходимо просто пропустить итерацию, но не выходить из цикла, можно применять оператор continue .
В этом случае, если программа встретит в массиве число, больше 10 , то это число не будет выводиться в браузере.
3. Функциональное программирование
3.1. Функции
Функции представляют собой набор инструкций, выполняющих определенное действие или вычисляющих определенное значение.
Синтаксис определения функции:
Определение функции начинается с ключевого слова function , после которого следует имя функции. Наименование функции подчиняется тем же правилам, что и наименование переменной: оно может содержать только цифры, буквы, символы подчеркивания и доллара $ и должно начинаться с буквы, символа подчеркивания или доллара.
После имени функции в скобках идет перечисление параметров. Даже если параметров у функции нет, то просто идут пустые скобки. Затем в фигурных скобках идет тело функции, содержащее набор инструкций.
Определим простейшую функцию:
Данная функция называется display() . Она не принимает никаких параметров и все, что она делает, это пишет на web-страницу строку.
Однако простого определения функции еще недостаточно, чтобы она заработала. На надо еще ее вызвать:
Необязательно давать функциям определенное имя. Можно использовать анонимные функции:
Фактически определяем переменную display и присваиваем ей ссылку на функцию. А затем по имени переменной функция вызывается.
Также можно динамически присваивать функции для переменной:
3.1.1. Параметры функции
Рассмотрим передачу параметров:
Функция display принимает один параметр — x . Поэтому при вызове функции можно передать для него значение, например, число 5 , как в данном случае.
Если функция принимает несколько параметров, то с помощью spread-оператора … можно передать набор значений для этих параметров из массива:
Во втором случае в функцию передается числа из массива nums . Но чтобы передавался не просто массив, как одно значение, а именно числа из этого массива, применяется spread-оператор … .
3.1.2. Необязательные параметры
Функция может принимать множество параметров, но при этом часть или все параметры могут быть необязательными. Если для параметров не передается значение, то по умолчанию они имеют значение undefined .
Здесь функция display принимает два параметра. При вызове функции можно проверить их значения. При этом, вызывая функцию, необязательно передавать для этих параметров значения. Для проверки наличия значения параметров используется сравнение со значением undefined .
Есть и другой способ определения значения для параметров по умолчанию:
Если параметрам x и y не передаются значения, то они получаются в качестве значений числа 5 и 10 соответственно. Такой способ более лаконичен и интуитивен, чем сравнение с undefined .
При этом значение параметра по умолчанию может быть производным, представлять выражение:
В данном случае значение параметра y зависит от значения x .
При необходимости можно получить все переданные параметры через глобально доступный массив arguments :
При этом даже неважно, что при определении функции не указаны параметры, все равно можно их передать и получить значения через массив arguments .
3.1.3. Неопределенное количество параметров
С помощью spread-оператора можно указать, что с помощью параметра можно передать переменное количество значений:
В данном случае второй параметр …temps указывает, что вместо него можно передать разное количество значений. В самой функции temps фактически представляет массив переданных значений, которые можно получить. При этом несмотря на это, при вызове функции в нее передается не массив, а именно отдельные значения.
Но нужно учесть, что каждое значение будет выведено с новой строки.
3.1.4. Результат функции
Функция может возвращать результат. Для этого используется оператор return :
После оператора return идет значение, которое надо возвратить из метода. В данном случае это квадрат числа х .
После получения результата функции можно присвоить его какой-либо другой переменной:
3.1.5. Функции в качестве параметров
Функции могут выступать в качестве параметров других функций:
Функция operation принимает три параметра: x , y и func . func — представляет функцию, причем на момент определения operation не важно, что это будет за функция. Единственное, что известно, что функция func может принимать два параметра и возвращать значение, которое затем отображается в консоли браузера. Поэтому можно определить различные функции (например, функции sum и subtract в данном случае) и передавать их в вызов функции operation .
3.1.6. Возвращение функции из функции
Одна функция может возвращать другую функцию:
В данном случае функция menu в зависимости от переданного в нее значения возвращает одну из трех функций или undefined .
3.2. Область видимости переменных
Все переменные в JavaScript имеют определенную область видимости, в пределах которой они могут действовать.
3.2.1. Глобальные переменные
Все переменные, которые объявлены вне функций, являются глобальными:
Здесь переменные x и d являются глобальными. Они доступны из любого места программы. А вот переменная z глобальной не является, так как она определена внутри функции.
3.2.2. Локальные переменные
Переменная, определенная внутри функции, является локальной:
Переменные z и b являются локальными, они существуют только в пределах функции. Вне функции их нельзя использовать:
Когда функция заканчивает свою работу, то все переменные, определенные в функции, уничтожаются.
3.2.3. Сокрытие переменных
Что если у нас есть две переменных — одна глобальная, а другая локальная, которые имеют одинаковое имя:
В этом случае в функции будет использоваться та переменная z , которая определена непосредственно в функции. То есть локальная переменная скроет глобальную переменную.
3.2.4. var или let
При использовании оператора let каждый блок кода определяет новую область видимости, в которой существует переменная. Например, можно одновременно определить переменную на уровне блока и на уровне функции:
Здесь внутри функции displayZ() определен блок кода, в котором определена переменная z . Она скрывает глобальную переменную и переменную z , определенную на уровне функции. В реальной программе блок мог быть предоставлять вложенную функцию, блок цикла for или конструкции if . Но в любом случае такой блок определяет новую область видимости, вне которого переменная не существует.
И в данном случае получим следующий консольный вывод:
С помощью оператора var можно определить одновременно переменную с одним и тем же именем и в функции, и в блоке кода в этой функции:
Но стоит обратить внимание, что переменная введенная в общем коде и на уровне функции/блока функции — это переменные с разным уровнем видимости (глобальным и на уровни функции соответственно).
3.2.5. Константы
Все, что относится к оператору let , относится и к оператору const , который позволяет определить константы. Блоки кода задают область видимости констант, а константы, определенные на вложенных блоках кода, скрывают внешние константы с тем же именем:
3.2.6. Необъявленные переменные
Если не используется ключевое слово при определении переменной в функции, то такая переменная будет глобальной.
Несмотря на то, что вне функции bar переменная foo нигде не определяется, тем не менее она доступна вне функции во внешнем контексте.
Иначе, если не только присвоить значение переменной, но и переопределить ее:
3.2.7. strict mode
Определение глобальных переменных в функциях может вести к потенциальным ошибкам. Чтобы их избежать используется строгий режим или strict mode:
Установить режим strict mode можно двумя способами:
Добавить выражение «use strict» в начало кода `JavaScript, тогда strict mode будет применяться для всего кода.
Добавить выражение «use strict» в начало тела функции, тогда strict mode будет применяться только для этой функции.
3.3. Замыкания
Замыкание (closure) представляют собой конструкцию, когда функция, созданная в одной области видимости, запоминает свое лексическое окружение даже в том случае, когда она выполняет вне своей области видимости.
Замыкание технически включает три компонента:
Внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные — лексическое окружение
Переменные (лексическое окружение), которые определены во внешней функции
Вложенная функция, которая использует эти переменные
3.3.1. Пример
Рассмотрим замыкания на простейшем примере:
Здесь функция outer задает область видимости, в которой определены внутренняя функция inner и переменная x . Переменная x представляет лексическое окружение для функции inner . В самой функции inner инкрементируем переменную x и выводим ее значение на консоль. В конце функция outer возвращает функцию inner .
Далее вызываем функцию outer :
Поскольку функция outer возвращает функцию inner , то переменная fn будет хранить ссылку на функцию inner . При этом эта функция запомнила свое окружение — то есть внешнюю переменную x .
Далее фактически три раза вызываем функцию Inner , и видно, что переменная x , которая определена вне функции inner , инкрементируется:
То есть несмотря на то, что переменная x определена вне функции inner , эта функция запомнила свое окружение и может его использовать, несмотря на то, что она вызывается вне функции outer , в которой была определена. В этом и суть замыканий.
3.3.2. Пример
Рассмотрим следующий пример:
Итак, здесь вызов функции multiply() приводит к вызову другой внутренней функции. Внутренняя же функция:
Запоминает окружение, в котором она была создана, в частности, значение переменной x .
В итоге при вызове функции multiply определяется переменная fn1 , которая и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. Окружение состоит из любой локальной переменной, которая была в области действия функции multiply во время создания замыкания.
То есть fn1 — это замыкание, которое содержит и внутреннюю функцию function(m) < return x * m;>, и переменную x , которая существовала во время создания замыкания.
При создании двух замыканий: fn1 и fn2 , для каждого из этих замыканий создается свое окружение.
При этом важно не запутаться в параметрах. При определении замыкания:
Число 5 передается для параметра n функции multiply .
При вызове внутренней функции:
Число 6 передается для параметра m во внутреннюю функцию function(m) < return x * m;>; .
Также можно использовать другой вариант для вызова замыкания:
3.4. Функции IIFE (Самовызывающиеся функции)
Обычно определение функции отделяется от ее вызова: сначала определяем функцию, а потом вызываем. Но это необязательно. Также можем создать такие функции, которые будут вызываться сразу при определении. Такие функции еще называют Immediately Invoked Function Expression (IIFE).
Подобные функции заключаются в скобки, и после определения функции идет в скобках передача параметров.
3.5. Паттерн Модуль
Паттерн Модуль базируется на замыканиях и состоит из двух компонентов: внешняя функция, которая определяет лексическое окружение, и возвращаемый набор внутренних функций, которые имеют доступ к этому окружению.
Определим простейший модуль:
Здесь определена переменная foo , которая представляет результат анонимной функции. Внутри подобной функции определен объект obj с некоторыми данными.
Сама анонимная функция возвращает объект, который определяет функцию display() . Возвращаемый объект определяет общедоступный API, через который можно обращаться к данным, определенным внутри модуля.
Такая конструкция позволяет закрыть некоторый набор данных в рамках функции-модуля и опосредовать доступ к ним через определенный API — возвращаемые внутренние функции.
Рассмотрим чуть более сложный пример:
Данный модуль представляет примитивный калькулятор, который выполняет три операции: сложение, вычитание и вывод результата.
Все данные сокрыты в объекте data , который хранит результат операции. Все операции представлены тремя возвращаемыми функциями: sum() , subtract() и display() . Через эти функции можно управлять результатом калькулятора извне.
3.6. Рекурсивные функции
Среди функций отдельно можно выделить рекурсивные функции. Их суть состоит в том, что функция вызывает саму себя.
Например, рассмотрим функцию, определяющую факториал числа:
Функция getFactorial() возвращает значение 1, если параметр n равен 1 , либо возвращает результат опять же функции getFactorial , то в нее передается значение n-1 . Например, при передаче числа 4 , у нас образуется следующая цепочка вызовов:
Рассмотрим другой пример — определение чисел Фибоначчи:
3.7. Переопределение функций
Функции обладают возможностью для переопределения поведения. Переопределение происходит с помощью присвоения анонимной функции переменной, которая называется так же, как и переопределяемая функция:
При первом срабатывании функции действует основной блок операторов функции, в частности, в данном случае выводится сообщение Доброе утро . И при первом срабатывании функции display() также происходит ее переопределение. Поэтому при всех последующих вызовах функции срабатывает ее переопределенная версия, а на консоль будет выводиться сообщение Добрый день .
Но при переопределении функции надо учитывать некоторые нюансы. В частности, попробуем присвоить ссылку на функцию переменной и через эту переменную вызвать функцию:
Здесь переменная displayMessage получает ссылку на функцию display() до ее переопределения. Поэтому при вызове displayMessage() будет вызываться не переопределенная версия функции display .
Но допустим, определили переменную displayMessage уже после вызова функции display() :
В этом случае переменная displayMessage будет указывать на переопределенную версию функции display() .
3.8. Hoisting
Hoisting представляет процесс доступа к переменным до их определения. Возможно, данная концепция выглядит немного странно, но она связана с работой компилятора JavaScript. Компиляция кода происходит в два прохода. При первом проходе компилятор получает все объявления переменных, все идентификаторы. При этом никакой код не выполняется, методы не вызываются. При втором проходе собственно происходит выполнение. И даже если переменная определена после непосредственного использования, ошибки не возникнет, так как при первом проходе компилятору уже известны все переменные.
То есть как будто происходит поднятие кода с определением переменных и функций вверх до их непосредственного использования. Поднятие на английский переводится как hoisting, сообственно поэтому данный процесс так и называется.
Переменные, которые попадают под hoisting, получают значение undefined .
Например, возьмем следующий простейший код:
Его выполнение вызовет ошибку
Добавим определение переменной:
В этом случае консоль выведет значение undefined . При первом проходе компилятор узнает про существование переменной foo . Она получает значение undefined . При втором проходе вызывается метод console.log(foo) .
Возьмем другой пример:
Здесь та же ситуация. Переменные a и b используются до определения. По умолчанию им присваиваются значения undefined . А если умножить undefined на undefined , то получим Not a Number ( NaN ).
Все то же самое относится и к использованию функций. Можно сначала вызвать функцию, а потом уже ее определить:
Здесь функция display благополучно отработает, несмотря на то, что она определена после вызова.
Но от этой ситуации надо отличать тот случай, когда функция определяется в виде переменной:
В данном случае получим ошибку
При первом проходе компилятор также получит переменную display и присвоит ей значение undefined . При втором проходе, когда надо будет вызывать функцию, на которую будет ссылаться эта переменная, компилятор увидит, что вызывать то нечего: переменная display пока еще равна undefined . И будет выброшена ошибка.
Поэтому при определении переменных и функций следует учитывать такой аспект как hoisting.
3.9. Передача параметров по значению и по ссылке
3.9.1. Передача параметров по значению
Строки, числа, логические значения передаются в функцию по значению. Иными словами при передаче значения в функцию, эта функция получает копию данного значения. Рассмотрим, что это значит в практическом плане:
Функция change получает некоторое число и увеличивает его в два раза. При вызове функции change ей передается число n . Однако после вызова функции видно, что число n не изменилось, хотя в самой функции произошло увеличение значения параметра. Потому что при вызове функция change получает копию значения переменной n . И любые изменения с этой копией никак не затрагивают саму переменную n .
3.9.2. Передача по ссылке
Объекты и массивы передаются по ссылке. То есть функция получает сам объект или массив, а не их копию.
В данном случае функция change получает объект и меняет его свойство name . В итоге увидим, что после вызова функции изменился оригинальный объект bob , который передавался в функцию.
Однако если переустановить объект или массив полностью, оригинальное значение не изменится.
То же самое касается массивов:
3.10. Стрелочные функции
Стрелочные функции (arrow functions) представляют сокращенную версию обычных функций. Стрелочные функции образуются с помощью знака стрелки => , перед которым в скобках идут параметры функции, а после — собственно тело функции.
В данном случае функция (x, y) => x + y осуществляет сложение двух чисел и присваивается переменной sum . Функция принимает два параметра — x и y . Ее тело составляет сложение значений этих параметров. И поскольку после стрелки фактически идет конкретное значение, которое представляет сумму чисел, то функция возвращает это значение. И можно через переменную sum вызвать данную функцию и получить ее результат в переменные a и b .
Если после стрелки идет операция или выражение, которое возвращает значение, то это значение фактически возвращается из стрелочной функции. Но также в качестве тела функции может примяться выражение, которое ничего не возвращает и просто выполняет некоторое действие:
В данном случае функция console.log() ничего не возвращает, и соответственно функция sum также не возвращает никакого результата.
Если функция принимает один параметр, то скобки вокруг него можно опустить:
Если тело функции представляет набор выражений, то они облекаются в фигурные скобки:
Для возвращения результата из функции в таком случае применяется стандартный оператор return .
Особо следует остановиться на случае, когда стрелочная функция возвращает объект:
Объект также определяется с помощью фигурных скобок, но при этом он заключается в круглые скобки.
Если стрелочная функция не принимает никаких параметров, то ставятся пустые скобки:
4. Реализация ООП в JavaScript
4.1. Объекты
Для работы с сущностями в JavaScript используются объекты. Каждый объект может хранить свойства, описывающие его состояние, и методы, описывающие его поведение.
4.1.1. Создание нового объекта
Есть несколько способов создания нового объекта.
Первый способ заключается в использовании конструктора Object :
В данном случае объект называется user . Он определяется так же, как и любая обычная переменная с помощью ключевого слова var .
Выражение new Object() представляет вызов конструктора — функции, создающей новый объект. Для вызова конструктора применяется оператор new . Вызов конструктора фактически напоминает вызов обычной функции.
Второй способ создания объекта представляет использование фигурных скобок:
Более распространенным является второй способ создания объекта.
4.1.2. Свойства объекта
После создания объекта можно определить в нем свойства. Чтобы определить свойство, надо после названия объекта через точку указать имя свойства и присвоить ему значение:
В данном случае объявляются два свойства name и age , которым присваиваются соответствующие значения. После этого появляется возможность использовать эти свойства:
Также можно определить свойства при определении объекта:
В этом случае для присвоения значения свойству используется символ двоеточия, а после определения свойства ставится запятая (а не точка с запятой).
Кроме того, доступен сокращенный способ определения свойств:
4.1.3. Методы объекта
Методы объекта определяют его поведение или действия, которые он производит. Методы представляют собой функции. Например, определим метод, который бы выводил имя и возраст человека:
Как и в случае с функциями методы сначала определяются, а потом уже вызываются.
Также методы могут определяться непосредственно при определении объекта:
Как и в случае со свойствами, методу присваивается ссылка на функцию с помощью знака двоеточия.
Чтобы обратиться к свойствам или методам объекта внутри этого объекта, используется ключевое слово this . Оно означает ссылку на текущий объект.
Также можно использовать сокращенный способ определения методов, когда двоеточие и слово function опускаются:
4.1.4. Синтаксис массивов
Существует также альтернативный способ определения свойств и методов с помощью синтаксиса массивов:
Название каждого свойства или метода заключается в кавычки и в квадратные скобки, затем им также присваивается значение. Например, user[«age»] = 26 .
При обращении к этим свойствам и методам можно использовать либо нотацию точки user.name , либо обращаться так: user[«name»] .
4.1.5. Строки в качестве свойств и методов
Названия свойств и методов объекта всегда представляют строки. То есть предыдущее определение объекта можно переписать так:
С одной стороны, разницы никакой нет между двумя определениями. С другой стороны, бывают случаи, где заключение названия в строку могут помочь. Например, если название свойства состоит из двух слов, разделенных пробелом:
В этом случае для обращения к подобным свойствам и методам мы должны использовать синтаксис массивов.
4.1.6. Удаление свойств
Удалять свойства и методы необходимо с помощью оператора delete . Как и в случае с добавлением удалять свойства можно двумя способами. Первый способ — использование нотации точки:
Либо использовать синтаксис массивов:
После удаления свойство будет не определено, поэтому при попытке обращения к нему, программа вернет значение undefined .
4.2. Вложенные объекты и массивы в объектах
Одни объекты могут содержать в качестве свойств другие объекты. Например, есть объект страны, у которой можно выделить ряд свойств. Одно из этих свойств может представлять столицу. Но у столицы мы также можем выделить свои свойства, например, название, численность населения, год основания:
Для доступа к свойствам таких вложенных объектов мы можем использовать стандартную нотацию точки:
Либо обращаться к ним как к элементам массивов:
Также допустим смешанный вид обращения:
В качестве свойств также могут использоваться массивы, в том числе массивы других объектов:
В объекте country имеется свойство languages, содержащее массив строк, а также свойство cities , хранящее массив однотипных объектов.
С этими массивами можно работать так же, как и с любыми другими, например, перебрать с помощью цикла for .
При переборе массива объектов каждый текущий элемент будет представлять отдельный объект, поэтому мы можем обратиться к его свойствам и методам:
4.3. Проверка наличия и перебор методов и свойств
При динамическом определении в объекте новых свойств и методов перед их использованием бывает важно проверить, а есть ли уже такие методы и свойства. Для этого в JavaScript может использоваться оператор in :
Оператор in имеет следующий синтаксис: «свойство|метод» in объект — в кавычках идет название свойства или метода, а после in — название объекта. Если свойство или метод с подобным именем имеется, то оператор возвращает true . Если нет — то возвращается false .
Альтернативный способ заключается на значение undefined . Если свойство или метод равен undefined , то эти свойство или метод не определены:
И так как объекты представляют тип Object , а значит, имеет все его методы и свойства, то объекты также могут использовать метод hasOwnProperty() , который определен в типе Object :
4.3.1. Перебор свойств и методов
С помощью цикла for мы можем перебрать объект как обычный массив и получить все его свойства и методы и их значения:
4.4. Объекты в функциях
Функции могут возвращать значения. Но эти значения необязательно должны представлять примитивные данные — числа, строки, но также могут быть сложными объектами.
Например, вынесем создание объекта user в отдельную функцию:
Здесь функция createUser() получает значения pName и pAge и по ним создает новый объект, который является возвращаемым результатом.
Преимуществом вынесения создания объекта в функцию является то, что далее мы можем создать несколько однотипных объектов с разными значениями.
Кроме того объект может передаваться в качестве параметра в функцию:
4.5. Конструкторы объектов
Кроме создания новых объектов JavaScript предоставляет возможность создавать новые типы объектов с помощью конструкторов. Так, одним из способов создания объекта является применение конструктора типа Object :
После создания переменной tom она будет вести себя как объект типа Object .
Конструктор позволяет определить новый тип объекта. Можно еще провести следующую аналогию.
Определение типа может состоять из функции конструктора, методов и свойств.
Конструктор — это обычная функция за тем исключением, что в ней мы можем установить свойства и методы. Для установки свойств и методов используется ключевое слово this :
Чтобы вызвать конструктор, то есть создать объект типа User , надо использовать ключевое слово new :
4.5.1. Оператор instanceof
Оператор instanceof позволяет проверить, с помощью какого конструктора создан объект. Если объект создан с помощью определенного конструктора, то оператор возвращает true :
4.6. Расширение объектов. Prototype
Кроме непосредственного определения свойств и методов в конструкторе мы также можем использовать свойство prototype . Каждая функция имеет свойство prototype , представляющее прототип функции. То есть свойство User.prototype представляет прототип объектов User . И любые свойства и методы, которые будут определены в User.prototype , будут общими для всех объектов User .
Например, после определения объекта User необходимо добавить к нему метод и свойство:
В то же время можно определить в объекте свойство, которое будет назваться так же, как и свойство прототипа. В этом случае собственное свойство объекта будет иметь приоритет перед свойством прототипа.
4.7. Инкапсуляция
Инкапсуляция является одним из ключевых понятий объектно-ориентированного программирования и представляет сокрытие состояния объекта от прямого доступа извне. По умолчанию все свойства объектов являются публичными, общедоступными, и мы к ним можем обратиться из любого места программы.
Но есть возможность их скрыть от доступа извне, сделав свойства локальными переменными:
В конструкторе User объявляется локальная переменная _age вместо свойства age . Как правило, названия локальных переменных в конструкторах начинаются со знака подчеркивания.
Для того, что бы работать с возрастом пользователя извне, определяются два метода. Метод getAge() предназначен для получения значения переменной _age . Этот метод еще называется геттер (getter). Второй метод — setAge , который еще называется сеттер (setter), предназначен для установки значения переменной _age .
4.8. Функция как объект. Методы call() и apply()
В JavaScript функция тоже является объектом — объектом Function и тоже имеет прототип, свойства, методы. Все функции, которые используются в программе, являются объектами Function и имеют все его свойства и методы.
Например, мы можем создать функцию с помощью конструктора Function :
В конструктор Function может передаваться ряд параметров. Последний параметр представляет собой само тело функции в виде строки. Фактически строка содержит JavaScript код. Предыдущие аргументы содержат названия параметров.
Среди свойств объекта Function можно выделить следующие:
arguments : массив аргументов, передаваемых в функцию
length : определяет количество аргументов, которые ожидает функция
caller : определяет функцию, вызвавшую текущую выполняющуюся функцию
name : имя функции
prototype : прототип функции
С помощью прототипа можно определить дополнительные свойства:
Среди методов надо отметить методы call() и apply() .
Метод call() вызывает функцию с указанным значением this и аргументами:
this указывает на объект, для которого вызывается функция — в данном случае это глобальный объект window . После this передаются значения для параметров.
При передаче объекта через первый параметр, мы можем ссылаться на него через ключевое слово this :
Если нам не важен объект, для которого вызывается функция, то можно передать значение null .
На метод call() похож метод apply() , который так же вызывает функцию. В качестве первого параметра также получает объект, для которого функция вызывается. Только теперь в качестве второго параметра передается массив аргументов:
4.9. Наследование
JavaScript поддерживает наследование, что позволяет при создании новых типов объектов при необходимости унаследовать функционал от уже существующих. Например, у нас может быть объект User , представляющий отдельного пользователя. И также может быть объект Employee , который представляет работника. Но работник также может являться пользователем и поэтому должен иметь все его свойства и методы.
В конструкторе Employee происходит обращение к конструктору User с помощью вызова:
Передача первого параметра this позволяет вызвать функцию конструктора User для объекта, создаваемого конструктором Employee . Благодаря этому все свойства и методы, определенные в конструкторе User , также переходят на объект Employee .
Кроме того, необходимо унаследовать также и прототип User . Для этого служит вызов:
Метод Object.create() позволяет создать объект прототипа User , который затем присваивается прототипу Employee . При этом при необходимости в прототипе Employee мы также можем определить дополнительные свойства и методы.
При наследовании мы можем переопределять наследуемый функционал.
4.10. Ключевое слово this
Поведение ключевого слова this зависит от контекста, в котором оно используется, и от того, в каком режиме оно используется — строгом или нестрогом.
4.10.1. Глобальный контекст
В глобальном контексте this ссылается на глобальный объект. В данном случае поведение не зависит от режима (строгий или нестрогий):
4.10.2. Контекст функции
В пределах функции this ссылается на внешний контекст. Для функций, определенных в глобальном контексте, — это объект Window , который представляет окно браузера.
Если не использовать this , то обращение шло бы к локальной переменной, определенной внутри функции.
Но если использовать строгий режим (strict mode), то this в этом случае имело бы значение undefined :
4.10.3. Контекст объекта
В контексте объекта, в том числе в его методах, ключевое слово this ссылается на этот же объект:
4.10.4. Явная привязка
С помощью методов call() и apply() можно задать явную привязку функции к определенному контексту:
4.10.5. Метод bind
Метод f.bind(o) позволяет создать новую функцию с тем же телом и областью видимости, что и функция f() , но с привязкой к объекту o :
4.10.6. this и стрелочные функции
При работе с несколькими контекстами необходимо учитывать, в каком контексте определяется переменная.
Видно, что значение this.title не определено, так как this как контекст объекта замещается глобальным контекстом. В этом случае нам надо передать подобное значение this.title или весь контекст объекта.
Стрелочные функции также позволяют решить данную проблему:
Контекстом для стрелочной функции в данном случае будет выступать контекст объекта school . Соответственно, не надо определять дополнительные переменные для передачи данных в функцию.
4.11. Декомпозиция
Декомпозиция (destructuring) позволяет извлечь из объекта отдельные значения в переменные:
Для декомпозиции объекта переменные помещаются в фигурные скобки и им присваивается объект. Сопоставление между свойствами объекта и переменными идет по имени.
Так же можно указать, что необходимо получить значения свойств объекта в переменные с другим именем:
4.11.1. Декомпозиция массивов
Также можно декомпозировать массивы:
Для декомпозиции массива переменные помещаются в квадратные скобки и последовательно получают значения элементов массива.
При этом можно пропустить ряд элементов массива, оставив вместо имен переменных пропуски:
4.11.2. Декомпозиция параметров
Если в функцию в качестве параметра передается массив или объект, то его также можно подобным образом декомпозировать:
4.12. Классы
С внедрением стандарта ES2015 (ES6) в JavaScript появился новый способ определения объектов — с помощью классов. Класс представляет описание объекта, его состояния и поведения, а объект является конкретным воплощением или экземпляром класса.
Для определения класса используется ключевое слово class :
Также можно определить анонимный класс и присвоить его переменной:
После этого можно создать объекты класса с помощью конструктора:
Для создания объекта с помощью конструктора сначала ставится ключевое слово new . Затем собственно идет вызов конструктора — по сути вызов функции по имени класса. По умолчанию классы имеют один конструктор без параметров. Поэтому в данном случае при вызове конструктора в него не передается никаких аргументов.
Также можно определить в классе свои конструкторы. Также класс может содержать свойства и методы:
Конструктор определяется с помощью метода с именем constructor . По сути это обычный метод, который может принимать параметры. Основная цель конструктора — инициализировать объект начальными данными. И в данном случае в конструктор передаются два значения — для имени и возраста пользователя.
Для хранения состояния в классе определяются свойства. Для их определения используется ключевое слово this . В данном случае в классе два свойства: name и age .
Поведение класса определяют методы. В данном случае определен метод display() , который выводит значения свойств на консоль.
4.12.1. Наследование
Одни классы могут наследоваться от других. Наследование позволяет сократить объем кода в классах-наследниках:
Для наследования одного класса от другого в определении класса применяется оператор extends , после которого идет название базового класса. То есть в данном случае класс Employee наследуется от класса Person . Класс Person еще называется базовым классом, классом-родителем, суперклассом, а класс Employee — классом-наследником, подклассом, производным классом.
Производный класс, как и базовый, может определять конструкторы, свойства, методы. Вместе с тем с помощью слова super производный класс может ссылаться на функционал, определенный в базовом.
4.12.2. Статические методы
Статические методы вызываются для всего класса в целом, а не для отдельного объекта. Для их определения применяется оператор static .
5. Встроенные объекты
Кроме возможности создавать свои объекты JavaScript также предоставляет набор встроенных типов объектов, которые можно применять в различных ситуациях.
5.1. Объект Date . Работа с датами
Объект Date позволяет работать с датами и временем в JavaScript.
Существуют различные способы создания объекта Date . Первый способ заключается в использовании пустого конструктора без параметров:
В этом случае объект будет указывать на текущую дату компьютера.
Второй способ заключается в передаче в (конструктор) Date количества миллисекунд, которые прошли с начала эпохи Unix, то есть с 1 января 1970 года 00:00:00 GMT:
Третий способ состоит в передаче в конструктор Date дня, месяца и года:
Если используется полное название месяца, то оно пишется в по-английски, если используем сокращенный вариант, тогда используется формат месяц/день/год.
Четвертый способ состоит в передаче в конструктор Date всех параметров даты и времени:
При этом надо учитывать, что отсчет месяцев начинается с нуля, то есть январь — 0, а декабрь — 11.
5.1.1. Получение даты и времени
Для получения различных компонентов даты применяется ряд методов:
getDate() : возвращает день месяца
getDay() : возвращает день недели (отсчет начинается с 0 — воскресенье, и последний день — 6 — суббота)
getMonth() : возвращает номер месяца (отсчет начинается с нуля, то есть месяц с номер 0 — январь)
getFullYear() : возвращает год
toDateString() : возвращает полную дату в виде строки
getHours() : возвращает час (от 0 до 23 )
getMinutes() : возвращает минуты (от 0 до 59 )
getSeconds() : возвращает секунды (от 0 до 59 )
getMilliseconds() : возвращает миллисекунды (от 0 до 999 )
toTimeString() : возвращает полное время в виде строки
5.1.2. Установка даты и времени
Коме задания параметров даты в конструкторе для установки мы также можем использовать дополнительные методы объекта Date :
setDate() : установка дня в дате
setMonth() : уставовка месяца (отсчет начинается с нуля, то есть месяц с номер 0 — январь)
setFullYear() : устанавливает год
setHours() : установка часа
setMinutes() : установка минут
setSeconds() : установка секунд
setMilliseconds() : установка миллисекунд
При установке значений есть возможность передать величину, большую, чем максимальное допустимое значение. В данном случае из значения будет высчитано количество целых величин большего порядка.
5.2. Объект Math
5.2.1. Математические операции
Объект Math предоставляет ряд математических функций, которые можно использовать при вычислениях. Рассмотрим основные математические функции.
abs() : возвращает абсолютное значение числа.
min() и max() : возвращают соответственно минимальное и максимальное значение из набора чисел.
ceil() : округляет число до следующего наибольшего целого числа.
floor() : округляет число до следующего наименьшего целого числа.
round() : округляет число до следующего наименьшего целого числа, если его десятичная часть меньше 0.5. Если же десятичная часть равна или больше 0.5, то округление идет до ближайшего наибольшего целого числа.
random() : возвращает случайное число с плавающей точкой из диапазона от 0 до 1.
pow() : возвращает число в определенной степени.
sqrt() : возвращает квадратный корень числа.
log() : возвращает натуральный логарифм числа.
5.2.2. Тригонометрические функции
Целый ряд функций представляют тригонометрические функции:
sin() — вычисляет синус угла
cos() — вычисляет косинус угла
tan() — вычисляет тангенс угла
В качестве значения они принимают значение в радианах.
5.2.3. Константы
Кроме методов объект Math также определяет набор встроенных констант, которые можно использовать в различных вычислениях:
Math.PI — число PI: 3.141592653589793
Math.SQRT2 — квадратный корень из двух: 1.4142135623730951
Math.SQRT1_2 — половина от квадратного корня из двух: 0.7071067811865476
Math.E — число e или число Эйлера: 2.718281828459045
Math.LN2 — натуральный логарифм числа 2: 0.6931471805599453
Math.LN10 — натуральный логарифм числа 10: 2.302585092994046
Math.LOG2E — двоичный логарифм числа e: 1.4426950408889634
Math.LOG10E — десятичный логарифм числа e: 0.4342944819032518
5.3. Объект Array . Работа с массивами
Объект Array представляет массив и предоставляет ряд свойств и методов, с помощью которых мы можем управлять массивом.
5.3.1. Инициализация массива
Можно создать пустой массив, используя квадратные скобки или конструктор Array :
Можно сразу же инициализировать массив некоторым количеством элементов:
Можно определить массив и по ходу определять в него новые элементы:
При этом не важно, что по умолчанию массив создается с нулевой длиной. С помощью индексов мы можем подставить на конкретный индекс в массиве тот или иной элемент.
5.3.2. length
Чтобы узнать длину массива, используется свойство length :
5.3.3. Копирование массива. slice()
Копирование массива может быть поверхностным или неглубоким (shallow copy) и глубоким (deep copy).
При неглубоком копировании достаточно присвоить переменной значение другой переменной, которая хранит массив:
Такое поведение не всегда является желательным. Например, необходимо, чтобы после копирования переменные указывали на отдельные массивы. И в этом случае можно использовать глубокое копирование с помощью метода slice() :
Также метод slice() позволяет скопировать часть массива:
5.3.4. push()
Метод push() добавляет элемент в конец массива.
5.3.5. pop()
Метод pop() удаляет последний элемент из массива.
5.3.6. shift()
Метод shift() извлекает и удаляет первый элемент из массива.
5.3.7. unshift()
Метод unshift() добавляет новый элемент в начало массива.
5.3.8. Удаление элемента по индексу. splice()
Метод splice() удаляет элементы с определенного индекса. Например, удаление элементов с третьего индекса:
Метод splice() возвращает удаленные элементы.
Если в метод передать отрицательный индекс, то удаление будет производиться с конца массива.
Дополнительная версия метода позволяет задать количество элементов для удаления.
Еще одна версия метода splice() позволяет вставить вместо удаляемых элементов новые элементы:
5.3.9. concat()
Метод concat() служит для объединения массивов:
Также можно объединять разнотипные массивы.
5.3.10. join()
Метод join() объединяет все элементы массива в одну строку:
В метод join() передается разделитель между элементами массива. В данном случае в качестве разделителя будет использоваться запятая , и пробел ` `).
5.3.11. sort()
Метод sort() сортирует массив по возрастанию.
5.3.12. reverse()
Метод reverse() переворачивает массив задом наперед. В сочетании с методом sort() можно отсортировать массив по убыванию.
5.3.13. Поиск индекса элемента
indexOf() и lastIndexOf()
Методы indexOf() и lastIndexOf() возвращают индекс первого и последнего включения элемента в массиве.
every()
Метод every() проверяет, все ли элементы соответствуют определенному условию:
В метод every() в качестве параметра передается функция, представляющая условие.
Параметр value представляет текущий перебираемый элемент массива, параметр index представляет индекс этого элемента, а параметр array передает ссылку на массив.
Метод some() похож на метод every() , только он проверяет, соответствует ли хотя бы один элемент условию. И в этом случае метод some() возвращает true . Если элементов, соответствующих условию, в массиве нет, то возвращается значение false .
filter()
Метод filter() , как some() и every() , принимает функцию условия. Но при этом возвращает массив* тех элементов, которые соответствуют этому условию.
forEach() и map()
Методы forEach() и map() осуществляют перебор элементов и выполняют с ними определенный операции. Например, для вычисления квадратов чисел в массиве можно использовать следующий код:
Но с помощью метода forEach() можно упростить эту конструкцию:
Метод forEach() в качестве параметра принимает все ту же функцию, в которую при переборе элементов передается текущий перебираемый элемент и над ним выполняются операции.
Метод map() похож на метод forEach() , он также в качестве параметра принимает функцию, с помощью которой выполняются операции над перебираемыми элементами массива, но при этом метод map() возвращает новый массив с результатами операций над элементами массива.
5.4. Объект Number
Объект Number представляет числа. Чтобы создать число, надо передать в конструктор Number число или стоку, представляющую число:
Однако создавать объект Number можно и просто присвоив переменной определенное число:
Объект Number предоставляет ряд свойств и методов. Некоторые его свойства:
Number.MAX_VALUE : наибольшее возможное число. Приблизительно равно 1.79E+308. Числа, которые больше этого значения, рассматриваются как Infinity
Number.MIN_VALUE : наименьшее возможное положительное число. Приблизительно равно 5e-324 (где-то около нуля)
Number.NaN : специальное значение, которое указывает, что объект не является числом
Number.NEGATIVE_INFINITY : значение, которое обозначает отрицательную неопределенность и которое возникает при переполнении. Например, если складывать два отрицательных числа, которые по модулю равны Number.MAX_VALUE
Number.POSITIVE_INFINITY : положительная неопределенность. Также, как и отрицательная неопределенность, возникает при переполнении, только теперь в положительную сторону
parseFloat() : преобразует строку в число с плавающей точкой.
parseInt(): преобразует строку в целое число.
toFixed() : оставляет в числе с плавающей точкой определенное количество знаков в дробной части.
isNaN() : определяет, является ли объект числом. Если объект не является числом, то возвращается значение true .
Но следующее выражение вернет false , хотя значение не является числом:
Чтобы избежать подобных ситуаций, лучше применять глобальную функцию isNaN() :
6. Строки
6.1. Строки и объект String
Для создания строк можно как напрямую присваивать переменной строку:
Для работы со строками предназначен объект String , поэтому также можно использовать конструктор String :
Но, как правило, используется первый более краткий способ. В первом случае JavaScript при необходимости автоматически преобразует переменную примитивного типа в объект String .
Объект String имеет большой набор свойств и методов, с помощью которых мы можем манипулировать строками.
Свойство length указывает на длину строки:
6.1.1. Шаблоны строк
Шаблоны строк позволяют вставлять в строку различные значения. Для этого строки заключаются в косые кавычки ` :
Для вставки значения в строку оно заключается в фигурные скобки, перед которыми ставится знак доллара $ .
Также вместо скалярных значений могут добавляться свойства сложных объектов или результаты выражений.
6.1.2. Поиск в строке
Для поиска в строке некоторой подстроки используются методы indexOf() (индекс первого вхождения подстроки) и lastIndexOf() (индекс последнего вхождения подстроки*. Эти методы принимают два параметра:
Подстроку, которую надо найти
Необязательный параметр, который указывает, с какого символа следует проводить поиск подстроки в строке
Оба метода возвращают индекс символа, с которого в строке*начинается подстрока. Если подстрока не найдена, то возвращается число -1 .
Еще один метод — includes() возвращает true , если строка содержит определенную подстроку. С помощью второго дополнительного параметра можно определить индекс, с которого будет начинаться поиск подстроки.
6.1.3. Выбор подстроки
Для того, что бы вырезать из строки подстроку, применяются методы substr() и substring() .
Метод substring() принимает два параметра:
индекс символа в строке, начиная с которого надо проводить обрезку строки
индекс, до которого надо обрезать строку
Метод substr() также в качестве первого параметра принимает начальный индекс подстроки, а в качестве второго — длину вырезаемой подстроки. Если второй параметр не указывается, то обрезается вся остальная часть строки.
6.1.4. Управление регистром
Для изменения регистра имеются методы toLowerCase() (для перевода в нижний регистр) и toUpperCase() (для перевода в верхний регистр).
6.1.5. Получение символа по индексу
Чтобы получить определенный символ в строке по индексу, можно применять методы charAt() и charCodeAt() . Оба метода в качестве параметра принимают индекс символа.
Но если в качестве результата метод charAt() возвращает сам символ, то метод charCodeAt() возвращает числовой код этого символа.
6.1.6. Удаление пробелов
Для удаления начальных и концевых пробелов в стоке используется метод trim() .
6.1.7. Объединение строк
Метод concat() объединяет две строки.
6.1.8. Замена подстроки
Метод replace() заменяет первое вхождение одной подстроки на другую.
6.1.9. Разделение строки
Метод split() разбивает строку на массив подстрок по определенному разделителю. В качестве разделителя используется строка, которая передается в метод.
6.1.10. Проверка начала и окончания строки
Метод startsWith() возвращает true , если строка начинается с определенной подстроки. А метод endsWith() возвращает true , если строка оканчивается на определенную подстроку. При этом играет роль регистр символов.
Дополнительный второй параметр позволяет указать индекс относительно которого будет проводиться сравнение.
6.2. Объект RegExp
6.2.1. Регулярные выражения
Регулярные выражения представляют шаблон, который используется для поиска или модификации строки. Для работы с регулярными выражениями в JavaScript определен объект RegExp .
Определить регулярное выражение можно двумя способами:
6.2.2. Методы RegExp
Чтобы определить, соответствует ли регулярное выражение строке, в объекте RegExp определен метод test() . Этот метод возвращает true , если строка соответствует регулярному выражению, и false , если не соответствует.
Аналогично работает метод exec() — он также проверяет, соответствует ли строка регулярному выражению, только теперь данный метод возвращает ту часть строки, которая соответствует выражению. Если соответствий нет, то возвращается значение null .
6.2.3. Группы символов
Регулярное выражение необязательно состоит из обычных строк, но также может включать специальные элементы синтаксиса регулярных выражений. Один из таких элементов представляют группы символов, заключенные в квадратные скобки.
Если нам надо определить наличие в строке буквенных символов из определенного диапазона, то можно разу задать этот диапазон:
Если, наоборот, не надо, чтобы строка имела только определенные символы, то необходимо в квадратных скобках перед перечислением символов ставить знак ^ :
6.2.4. Свойства выражений
Свойство global позволяет найти все подстроки, которые соответствуют регулярному выражению. По умолчанию при поиске подстрок регулярное выражение выбирает первую попавшуюся подстроку из строки, которая соответствует выражению. Хотя в строке может быть множество подстрок, которые также соответствуют выражению. Для этого применяется данное свойство в виде символа g в выражениях
Свойство ignoreCase позволяет найти подстроки, которые соответствуют регулярному выражению, вне зависимости от регистра символов в строке. Для этого в регулярных выражениях применяется символ i
Свойство multiline позволяет найти подстроки, которые соответствуют регулярному выражению, в многострочном тексте. Для этого в регулярных выражениях применяется символ m
6.3. Регулярные выражения в методах String
Ряд методов объекта String могут использовать регулярные выражения в качестве параметра.
6.3.1. Разделение строки. Метод split()
Метод split() может использовать регулярные выражения для разделения строк:
6.3.2. Метод match()
Для поиска всех соответствий в строке применяется метод match() :
6.3.3. Поиск в строке. Метод search()
Метод search() находит индекс первого включения соответствия в строке:
6.3.4. Замена. Метод replace()
Метод replace() позволяет заменить все соответствия регулярному выражению определенной строкой:
7. Работа с браузером и BOM
7.1. Browser Object Model и объект window
Большое значение в JavaScript имеет работа с web-браузером и теми объектами, которые он предоставляет. Например, использование объектов браузера позволяет манипулировать элементами html, которые имеются на странице, или взаимодействовать с пользователем.
Все объекты, через которые JavaScript взаимодействует с браузером, описываются таким понятием как Browser Object Model (Объектная Модель Браузера).
Browser Object Model можно представить в виде следующей схемы:
В вершине находится главный объект — объект window , который представляет собой браузер. Этот объект в свою очередь включает ряд других объектов, в частности, объект document , который представляет отдельную web-страницу, отображаемую в браузере.
7.1.1. Объект window
Объект window представляет собой окно web-браузера, в котором размещаются web-страницы. window является глобальным объектом, поэтому при доступе к его свойствам и методам необязательно использовать его имя. Например, window имеет метод alert() , который отображает окно сообщения. Но нам необязательно писать:
window можно не использовать:
Но так как данный объект глобальный, то это накладывает некоторые ограничения. Например:
Все объявляемые в программе глобальные переменные или функции автоматически добавляются к объекту window . И поскольку название новой функции будет совпадать с названием метода alert() , то произойдет переопределение этого метода в объекте window новой функцией.
И если мы объявим в программе какую-нибудь глобальную переменную, то она нам доступна как свойство в объекте window :
7.2. Управление окнами
7.2.1. Диалоговые окна
Для взаимодействия с пользователем в объекте window определен ряд методов, которые позволяют создавать диалоговые окна.
Метод alert()
Метод alert() выводит окно с сообщением:
Метод confirm()
Метод confirm() отображает окно с сообщением, в котором пользователь должен подтвердить действие двух кнопок OK и Отмена. В зависимости от выбора пользователя метод возвращает:
true если пользователь нажал OK
false если пользователь нажал кнопку Отмены

Метод prompt()
Метод prompt() позволяет с помощью диалогового окна запрашивать у пользователя какие-либо данные. Данный метод возвращает введенное пользователем значение:
Если пользователь откажется вводить значение и нажмет на кнопку отмены, то метод возвратит значение null .

7.2.2. Открытие, закрытие и позиционирование окон
Объект window также предоставляет ряд методов для управления окнами браузера.
Метод open()
Метод open() открывает определенный ресурс в новом окне браузера:
Метод open() принимает ряд параметров: путь к ресурсу, описательное название для окна и в качестве третьего параметра набор стилевых значений окна. Метод возвращает ссылку на объект нового окна.
Можно установить следующие стилевые характеристики:
width : ширина окна в пикселях. Например, width=640
height : высота окна в пикселях. Например, height=480
left : координата x относительно начала экрана в пикселях. Например, left=0
top : координата y относительно начала экрана в пикселях. Например, top=0
titlebar : будет ли окно иметь строку с заголовком. Например, titlebar=no
menubar : будет ли окно иметь панель меню. Например, menubar=yes
toolbar : будет ли окно иметь панели инструментов. Например, toolbar=yes
location : будет ли окно иметь адресную строку. Например, location=no
scrollbars : допускается ли наличие полос прокрутки. Например, scrollbars=yes
status : наличие статусной строки. Например, status=yes
resizable : может ли окно изменять размеры. Например, resizable=no
Метод close()
С помощью метода close() можно закрыть окно. Например, откроем новое окно и через 10 секунд закроем его:
Метод moveTo()
Метод moveTo() позволяет переместить окно на новую позицию:
В данном случае окно перемещается на позицию с координатами x равные 50 , y равные 50 относительно левого верхнего угла экрана.
Метод resizeTo()
Метод resizeTo() позволяет изменить размеры окна:
7.3. История браузера
Объект history предназначен для хранения истории посещений web-страниц в браузере. Этот объект доступен через объект window .
Все сведения о посещении пользователя хранятся в специальном стеке (history stack). С помощью свойства length можно узнать, как много web-станиц хранится в стеке:
Для перемещения по страницам в истории в объекте history определены методы:
back() : перемещение к прошлой посмотренной странице
forward() : перемещение к следующей просмотренной странице
Также в объекте history определен специальный метод go() , который позволяет перемещаться вперед и назад по истории на определенное число страниц. Например, переместимся на 2 страницы назад:
Соответственно если надо переместиться на несколько страниц вперед, то в метод передается положительное значение. Например, переместимся вперед на три страницы:
7.4. Расположение web-страницы
Объект location содержит информацию о расположении текущей web-страницы: URL, информацию о сервере, номер порта, протокол. С помощью свойств объекта мы можем получить эту информацию:
href : полная строка запроса к ресурсу
pathname : путь к ресурсу
origin : общая схема запроса
port : порт, используемый ресурсом
hostname : название хоста
hash : если строка запроса содержит символ решетки ( # ), то данное свойство возвращает ту часть строки, которая идет после этого символа
search : если строка запроса содержит знак вопроса ( ? ), например, то данное свойство возвращает ту часть строки, которая идет после знака вопроса
Например, пусть есть следующая web-страница test.html , которая лежит на локальном web-сервере:

Также объект location предоставляет ряд методов, которые можно использовать для управления путем запроса:
assign(url) : загружает ресурс, который находится по пути url
reload(forcedReload) : перезагружает текущую web-страницу. Параметр forcedReload указывает, надо ли использовать кэш браузера. Если параметр равен true , то кэш не используется
replace(url) : заменяет текущую web-станицу другим ресурсом, который находится по пути url . В отличие от метода assign , который также загружает web-станицу с другого ресурса, метод replace не сохраняет предыдущую web-страницу в стеке истории переходов history , поэтому мы не сможем вызвать метод history.back() для перехода к ней.
Для перенаправления на другой ресурс мы можем использовать как свойства, так и методы location :
Переход на другой локальный ресурс:
7.5. Информация о браузере и операционной системе
Объект navigator содержит информацию о браузере и операционной системе, в которой браузер запущен. Он определяет ряд свойств и методов, основным из которых является свойство userAgent , представляющее браузер пользователя:
Данное свойство хранит полную стоку юзер-агента, например, Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0
Чтобы вычленить из этой информации непосредственно браузер, можно попробовать найти в этой информации название браузера:
7.5.1. Географическое положение пользователя
Объект navigator хранит свойство geolocation , с помощью которого можно получить географическое положение пользователя. Для получения положения используется метод getCurrentPosition() . Этот метод принимает два параметра: функцию, которая срабатывает при удачном запуске, и функцию, которая срабатывает при ошибке запроса геоданных:
В функцию, которая выполняется при удачном определении геоданных, передается позиция пользователя в виде параметра position . Передаваемый объект имеет вложенный объект coords , с помощью свойство которого можно получить непосредственные координаты пользователя:
latitude : географическая широта
longitude : географическая долгота
speed : скорость, с которой перемещается пользователь (например, если он идет или перемещается на транспорте)
При этом надо учитывать, что в браузерах действует политика безопасности, которая при обращении к методу geolocation.getCurrentPosition() отображает пользователю сообщение, в котором пользователь может подтвердить отправку географических координат. Если же пользователь откажется, то будет срабатывать функция error() .

7.6. Таймеры
Для выполнения действий через определенные промежутки времени в объекте window предусмотрены функции таймеров. Есть два типа таймеров:
срабатывающие только один раз
срабатывающие постоянно через промежуток времени
7.6.1. Функция setTimeout()
Для одноразового выполнения действий через промежуток времени предназначена функция setTimeout() . Она может принимать два параметра:
Параметр period указывает на промежуток, через который будет выполняться функция из параметра someFunction . А в качестве результата функция возвращает id таймера.
В данном случае через 3 секунды после загрузки страницы произойдет срабатывание функции timerFunction .
Для остановки таймера применяется функция clearTimeout() .
7.6.2. Функция setInterval()
Функции setInterval() и clearInterval() работают аналогично функциям setTimeout() и clearTimeout() с той лишь разницей, что setInterval() постоянно выполняет определенную функцию через промежуток времени.
Например, напишем небольшую программу для вывода текущего времени:
Здесь через каждую секунду ( 1000 миллисекунд) вызывается функция updateTime() , которая обновляет содержимое элемента <div > , устанавливая в качестве его значения текущее время.
7.6.3. Функция requestAnimationFrame()
Метод requestAnimationFrame() действует аналогично setInterval() за тем исключением, что он больше заточен под анимацию, работу с графикой и имеет ряд оптимизаций, которые улучшают его производительность.
В метод window.requestAnimationFrame() передается функция, которая будет вызываться определенное количество раз в секунду (обычно 60 кадров в секунду или же FPS). В данном случае в этот метод передается функция rotate , которая изменяет угол поворота блока на странице и затем обращается опять же к методу window.requestAnimationFrame(rotate) .
В качестве возвращаемого результата метод window.requestAnimationFrame() возвращает уникальный id , который может потом использоваться для остановки анимации:
8. Работа с DOM
Одой из ключевых задач JavaScript является взаимодействие с пользователем и манипуляция элементами web-страницы. Для JavaScript web-страница доступна в виде объектной модели документа (document object model) или сокращенно DOM. DOM описывает структуру web-страницы в виде древовидного представления и предоставляет разработчикам способ получить доступ к отдельным элементам web-страницы.
Важно не путать понятия BOM (Browser Object Model — объектная модель браузера) и DOM (объектная модель документа). Если BOM предоставляет доступ к браузеру и его свойствам в целом, то DOM предоставляет доступ к отдельной web-странице или HTML-документу и его элементам.
Рассмотрим простейшую web-страницу:
Дерево DOM для этой страницы будет выглядеть следующим образом:

Все компоненты упорядочены в DOM иерархическим образом, где каждый компонент представляет отдельный узел.
Существует следующие виды узлов:
Element: HTML-элемент
Attr: атрибут HTML-элемента
Document: корневой узел HTML-документа
DocumentType: DTD или тип схемы XML-документа
DocumentFragment: место для временного хранения частей документа
EntityReference: ссылка на сущность XML-документа
ProcessingInstruction: инструкция обработки web-страницы
Comment: элемент комментария
Text: текст элемента
CDATASection: секция CDATA в документе XML
Entity: необработанная сущность DTD
Notation: нотация, объявленная в DTD
8.1. Объект document
Для работы со структурой DOM в JavaScript предназначен объект document , который определен в глобальном объекте window . Объект document предоставляет ряд свойств и методов для управления элементами страницы.
8.1.1. Поиск элементов
Для поиска элементов на странице применяются следующие методы:
getElementById(value) : выбирает элемент, у которого атрибут id равен value
getElementsByTagName(value) : выбирает все элементы, у которых тег равен value
getElementsByClassName(value) : выбирает все элементы, которые имеют класс value
querySelector(value) : выбирает первый элемент, который соответствует css-селектору value
querySelectorAll(value) : выбирает все элементы, которые соответствуют css-селектору value
Например, найдем элемент по id :
С помощью вызова document.getElementById(«header») находим элемент, у которого id=»header» . А с помощью свойства innerText можно получить текст найденного элемента.
Поиск по определенному тегу:
С помощью вызова document.getElementsByTagName(«p») находим все элементы параграфов. Этот вызов возвращает массив найденных элементов. Поэтому, чтобы получить отдельные элементы массива, необходимо пробежаться по ним в цикле.
Если нам надо получить только первый элемент, то можно к первому элементу найденной коллекции объектов:
Получение элемента по классу:
Выбор по селектору CSS:
Выражение document.querySelector(«.annotation p») находит элемент, который соответствует селектору .annotation p . Если на странице несколько элементов, соответствующих селектору, то метод выберет первый из них. В итоге браузер выведет:
Чтобы получить все элементы по селектору, можно подобным образом использовать метод document.querySelectorAll() , который возвращает массив найденных элементов:
8.2. Свойства объекта document
Кроме ранее рассмотренных методов объект document позволяет обратиться к определенным элементам web-страницы через свойства:
documentElement : предоставляет доступ к корневому элементу <html>
body : предоставляет доступ к элементу <body> на web-странице
images : содержит коллекцию всех объектов изображений (элементов img )
links : содержит коллекцию ссылок — элементов <a> и <area> , у которых определен атрибут href
anchors : предоставляет доступ к коллекции элементов <a> , у которых определен атрибут name
forms : содержит коллекцию всех форм на web-странице
Эти свойства не предоставляют доступ ко всем элементам, однако позволяют получить наиболее часто используемые элементы на web-странице.
Получение всех изображений на странице:
Подобно тому, как в коде HTML можно установить атрибуты у элемента img , так и в коде JavaScript можно через свойства src и alt получить и установить значения этих атрибутов.
Рассмотрим получение всех ссылок на странице:
Подобно тому, как в коде HTML можно установить атрибуты у элемента img , так и в коде javascript можно через свойства src и alt получить и установить значения этих атрибутов.
8.3. Объект Node
Каждый отдельный узел, будь то HTML-элемент, его атрибут или текст, в структуре DOM представлен объектом Node . Этот объект предоставляет ряд свойств, с помощью которых мы можем получить информацию о данном узле:
childNodes : содержит коллекцию дочерних узлов
firstChild : возвращает первый дочерний узел текущего узла
lastChild : возвращает последний дочерний узел текущего узла
previousSibling : возвращает предыдущий элемент, который находится на одном уровне с текущим
nextSibling : возвращает следующий элемент, который находится на одном уровне с текущим
ownerDocument : возвращает корневой узел документа
parentNode : возвращает элемент, который содержит текущий узел
nodeName : возвращает имя узла
nodeType : возвращает тип узла в виде числа. 1 — элемент, 2 — атрибут, 3 — текст
nodeValue : возвращает или устанавливает значение узла в виде простого текста
8.4. Создание, добавление элементов web-страницы
Для создания элементов объект document имеет следующие методы:
createElement(elementName) : создает HTML-элемент, тег которого передается в качестве параметра. Возвращает созданный элемент
createTextNode(text) : создает и возвращает текстовый узел. В качестве параметра передается текст узла.
Переменная elem будет хранить ссылку на элемент div . Однако одного создания элементов недостаточно, их еще надо добавить на web-страницу.
Для добавления элементов мы можем использовать один из методов объекта Node :
appendChild(newNode) : добавляет новый узел newNode в конец коллекции дочерних узлов
insertBefore(newNode, referenceNode) : добавляет новый узел newNode перед узлом referenceNode
Однако необязательно для определения текста внутри элемента создавать дополнительный текстовый узел, так как мы можем воспользоваться свойством textContent и напрямую ему присвоить текст:
В этом случае текстовый узел будет создан неявно при установке текста.
8.4.1. Копирование элементов
Иногда элементы бывают довольно сложными по составу, и гораздо проще их скопировать, чем с помощью отдельных вызовов создавать из содержимое. Для копирования уже имеющихся узлов у объекта Node можно использовать метод cloneNode() .
В метод cloneNode() в качестве параметра передается логическое значение: если передается true , то элемент будет копироваться со всеми дочерними узлами; если передается false — то копируется без дочерних узлов.
8.4.2. Удаление элемента
Для удаления элемента вызывается метод removeChild(removalNode) объекта Node . Этот метод удаляет один из дочерних узлов.
8.4.3. Замена элемента
Для замены элемента применяется метод replaceChild(newNode, oldNode) объекта Node .
8.5. Объект Element
Кроме методов и свойств объекта Node в JavaScript мы можем использовать свойства и методы объектов Element . Важно не путать эти два объекта: Node и Element . Node представляет все узлы web-страницы, в то время как объект Element представляет непосредственно только HTML-элементы. То есть объекты Element — это фактически те же самые узлы — объекты Node , у которых тип узла (свойство nodeType ) равно 1.
Одним из ключевых свойств объекта Element является свойство tagName , которое возвращает тег элемента. Например, получим все элементы, которые есть на странице:
8.5.1. Свойства innerText и innerHTML
Для получения или установки текстового содержимого элемента можно использовать свойство innerText , а для получения или установки HTML-кода — свойство innerHTML .
Надо отметить, что свойство innerText во многом аналогично свойству textContent . То есть следующие вызовы будут равноценны:
Установка HTML-кода у элемента:
8.5.2. Методы объекта Element
Среди методов объекта Element можно отметить методы управления атрибутами:
getAttribute(attr) : возвращает значение атрибута attr
setAttribute(attr, value) : устанавливает для атрибута attr значение value . Если атрибута нет, то он добавляется
removeAttribute(attr) : удаляет атрибут attr и его значение
Работа с атрибутами:
8.5.3. Размеры и позиция элементов
Элементы имеют ряд свойств, которые позволяют определить размер элемента. Но важно понимать разницу между всеми этими свойствами.
Свойства offsetWidth и offsetHeight определяют соответственно ширину и высоту элемента в пикселях. В ширину и высоту включается граница элемента.
Свойства clientWidth и clientHeight также определяют ширину и высоту элемента в пикселях, но уже без учета границы.
Для определения позиции элемента наиболее эффективным способом является метод getBoundingClientRect() .
Этот метод возвращает объект со свойствами top , bottom , left , right , которые указывают на смещение элемента относительно верхнего левого угла браузера.
8.5.4. Изменение стиля элементов
Для работы со стилевыми свойствами элементов в JavaScript применяются, главным образом, два подхода:
Изменение свойства style
Изменение значения атрибута class
Свойство style
Свойство style представляет сложный объект для управления стилем и напрямую сопоставляется с атрибутом style HTML-элемента. Этот объект содержит набор свойств CSS: element.style.свойствоCSS .
Однако ряд свойств CSS в названиях имеют дефис, например, font-family . В JavaScript для этих свойств дефис не употребляется. Только первая буква, которая идет после дефиса, переводится в верхний регистр.
Свойство className
С помощью свойства className можно установить атрибут class HTML-элемента:
Благодаря использованию классов не придется настраивать каждое отдельное свойство css с помощью свойства style .
Но при этом надо учитывать, что прежнее значение атрибута class удаляется. Поэтому, если нам надо добавить класс, надо объединить его название со старым классом.
И если надо вовсе удалить все классы, то можно присвоить свойству пустую строку.
Свойство classList
Выше было рассмотрено, как добавлять классы к элементу, однако для управления множеством классов гораздо удобнее использовать свойство classList . Это свойство представляет объект, реализующий следующие методы:
add(className) : добавляет класс className
remove(className) : удаляет класс className
toggle(className) : переключает у элемента класс на className . Если класса нет, то он добавляется, если есть, то удаляется
8.6. Создание своего элемента HTML
По умолчанию HTML предоставляет ряд встроенных элементов, из которых можно составить структуру web-страницы. Однако нет ограничений на использование встроенных HTML-элементов и можно создать и использовать свои HTML-элементы.
В JavaScript HTML-элемент представлен интерфейсом HTMLElement . Соответственно, реализуя данный интерфейс в JavaScript, мы можем создать свои классы, которые будут представлять HTML-элементы, и потом их использовать.
Чтобы определить класс, который будет представлять HTML-элемент, достаточно создать класс, который реализует интерфейс HTMLElement :
Второй важный момент — нужно зарегистрировать разработанный HTML-элемент, что бы браузер знал, что есть такой элемент. Для этого применяется встроенная функция:
Она принимает три параметра:
name : имя разработанного элемента HTML, который будет представлять класс JavaScript. Важно: имя должно содержать дефис.
constructor : конструктор (по сути класс JavaScript), который представляет разработанный элемент HTML.
options : необязательный параметр — объект, который настраивает разработанный HTML-элемент.
Как правило, классы кастомных элементов применяют конструктор. Причем в самом начале конструктора должен идти вызов функции super() , который гарантирует, что класс унаследовал все методы, атрибуты и свойства интерфейса HTMlElement .
Но кроме того, в конструкторе мно определить некоторую базовую логику элемента.
8.6.1. Добавление методов
Как и в обычных классах, мы можем определять в классах элементов методы и затем вызывать их.
8.6.2. События жизненного цикла
Кастомный элемент HTML имеет свой жизненный цикл, который описывается следующими методами:
connectedCallback : вызывается каждый раз, когда разработанный HTML-элемент добавляется в DOM.
disconnectedCallback : вызывается каждый раз, когда разработанный HTML-элемент удаляется из DOM.
adoptedCallback : вызывается каждый раз, когда разработанный HTML-элемент перемещается в новый элемент.
attributeChangedCallback : вызывается при каждом изменении (добавлении, изменении значения или удаления) атрибута разработанного HTML-элемента.
8.6.3. Добавление атрибутов
Также мы можем определить у элемента свои атрибуты и затем использовать их.
8.6.4. Стилизация CSS
Стилизация элемента через CSS производится так же, как и стилизация любого другого элемента.
9. События
9.1. Введение в обработку событий
Для взаимодействия с пользователем в JavaScript определен механизм событий. Например, когда пользователь нажимает кнопку, то возникает событие нажатия кнопки. В коде JavaScript можно определить возникновение события и как-то его обработать.
В JavaScript есть следующие типы событий:
События мыши (перемещение курсора, нажатие мыши и т.д.)
События клавиатуры (нажатие или отпускание клавиши клавиатуры)
События жизненного цикла элементов (например, событие загрузки web-страницы)
События элементов форм (нажатие кнопки на форме, выбор элемента в выпадающем списке и т.д.)
События, возникающие при изменении элементов DOM
События, возникающие при касании на сенсорных экранах
События, возникающие при возникновении ошибок
Рассмотрим простейшую обработку событий. Например, на web-странице у нас есть следующий элемент div :
Здесь определен обычный блок div , который имеет атрибут onclick задающий обработчик события _нажатия на блок div . То есть, чтобы обработать какое-либо событие, нам надо определить для него обработчик. Обработчик представляет собой код на языке JavaScript. В данном случае обработчик выглядит довольно просто:
И при нажатии на кнопку будет появляться всплывающее окно:

Также можно вынести все действия по обработке события в отдельную функцию:
Теперь обработчиком события будет выступать функция displayMessage() .
9.1.1. Передача параметров обработчику события
Обработчику события можно передавать параметры. Например, можно передать текущий объект, на котором возникает событие:
Ключевое слово this указывает на текущий объект ссылки, на которую производится нажатие и в коде обработчика можно получить этот объект и обратиться к его свойствам, например, к свойству href .
Кроме того, надо отметить, что здесь обработчик возвращает результат. Хотя в первом примере с блоком div от обработчика не требовалось возвращения результата. Дело в том, что для некоторых обработчиков можно подтвердить или остановить обработку события. Например, нажатие на ссылку должно привести к переадресации. Но возвращая из обработчика false , можно остановить стандартный путь обработки события, и переадресации не будет. Если же возвращать значение true , то событие обрабатывается в стандартном порядке.
Если же убрать возвращение результата, то событие будет обрабатываться, как будто возвращается значение true :
Кроме непосредственно элемента-источника события, обработчику можно передавать объект event . Этот объект не определяется разработчиком, это просто аргумент функции обработчика, который хранит всю информацию о событии. Например:
В данном случае с помощью свойства type объекта event получаем тип события (в данном случае тип click ).
9.2. Обработчики событий
9.2.1. Встроенные обработчики
Пример встроенного обработчика, который определяется в коде элемента с помощью атрибутов:
Хотя этот подход прекрасно работает, но он имеет кучу недостатков:
Код HTML смешивается с кодом JavaScript, в связи с чем становится труднее разрабатывать, отлаживать и поддерживать приложение.
Обработчики событий можно задать только для уже созданных на web-странице элементов. Динамически создаваемые элементы в этом случае лишаются возможности обработки событий.
К элементу для одного события может быть прикреплен только один обработчик.
Нельзя удалить обработчик без изменения кода
9.2.2. Свойства обработчиков событий
Проблемы, которые возникают при использовании встроенных обработчиков, были призваны решить свойства обработчиков. Подобно тому, как у HTML-элементов есть атрибуты для обработчиков, так и в коде JavaScript у элементов DOM можно получить свойства обработчиков, которые соответствуют атрибутам:
В итоге достаточно взять свойство onclick и присвоить ему функцию, используемую в качестве обработчика. За счет этого код HTML отделяется от кода JavaScript.
Стоит также отметить, что обработчику события браузер автоматически передает объект Event , хранящий всю информацию о событии. Поэтому также можно получить этот объект в функции обработчика в качестве параметра.
9.2.3. Слушатели событий
Несмотря на то, что свойства обработчиков решают ряд проблем, которые связаны с использованием атрибутов, в то же время это также не оптимальный подход. Еще один способ установки обработчиков событий представляет использование слушателей.
Для работы со слушателями событий в JavaScript есть объект EventTarget , который определяет методы
addEventListener() для добавления слушателя
removeEventListener() для удаления слушателя
Поскольку HTML-элементы DOM тоже являются объектами EventTarget , то они также имеют эти методы. Фактически слушатели представляют те же функции обработчиков.
Метод addEventListener() принимает два параметра: название события без префикса on и функцию обработчика этого события.
То есть в данном случае опять же обрабатывается событие click . И также можно было бы в качестве второго параметра название функции:
Удаление слушателя аналогично добавлению:
Преимуществом использования слушателей является и то, что можно установить для одного события несколько функций:
9.3. Объект Event
При обработке события браузер автоматически передает в функцию обработчика в качестве параметра объект Event , который содержит в себе всю информацию о событии. И с помощью его свойств можно получить эту информацию:
bubbles : возвращает true , если событие является восходящим. Например, если событие возникло на вложенном элементе, то оно может быть обработано на родительском элементе.
cancelable : возвращает true , если можно отменить стандартную обработку события.
currentTarget : определяет элемент, к которому прикреплен обработчик события.
defaultPrevented : возвращает true , если был вызван у объекта Event метод preventDefault() .
eventPhase : определяет стадию обработки события.
target : указывает на элемент, на котором было вызвано событие.
timeStamp : хранит время возникновения события.
type : указывает на имя события.
Причем в данном случае свойство target представляет собой элемент, поэтому что можно манипулировать им, как и любыми другими узлами и элементами DOM. Например, изменим фоновый цвет:
9.3.1. Остановка выполнения события
С помощью метода preventDefault() объекта Event что можно остановить дальнейшее выполнение события. В ряде случаев этот метод не играет большой роли. Однако может быть полезен, например, при нажатии на ссылку можно с помощью дополнительной обработки определить, надо ли переходить по ссылке или надо запретить переход. Или другой пример: пользователь отправляет данные формы, но в ходе обработки в обработчике события определили, что поля формы заполнены неправильно, и в этом случае также можно запретить отправку.
Например, запретим переход по ссылке после 12 часов:
9.4. Распространение событий
Когда нажимают на какой-либо элемент на станице и генерируется событие нажатия, то это событие может распространяться от элемента к элементу. Например, если нажать на блок div , то также нажимаем и на элемент body , в котором блок div находится. То есть происходит распространение события.
Есть две форм распространения событий:
Восходящие: событие распространяется вверх по дереву DOM от дочерних узлов к родительским.
Нисходящие: событие распространяется вниз по дереву DOM от родительских узлов к дочерним, пока не достигнет того элемента, на котором это событие и возникло.
9.4.1. Восходящие события
Рассмотрим восходящие события, которые распространяются в верх по дереву DOM. Допустим, у нас есть следующая web-страница:
Если нажать на вложенный div , то событие пойдет к родительскому элементу div и далее к элементу body :

Надо сказать, что подобное поведение не всегда является желательным. И в этом случае можно остановить распространение событие с помощью метода stopPropagation() объекта Event :
И в результате нажатия событие будет обработано только обработчиком для redRect .
9.4.2. Нисходящие события
События также могут быть нисходящими. Для их использования в метод addEventListener() в качестве третьего необязательного параметра передается логическое значение true или false , которое указывает, будет ли событие нисходящим. По умолчанию все события восходящие.
Возьмем ту же web-станицу, только изменим ее код JavaScript:
Теперь события будут распространяться в обратном порядке:

9.5. События мыши
Одну из наиболее часто используемых событий составляют события мыши:
click : возникает при нажатии указателем мыши на элемент
mousedown : возникает при нахождении указателя мыши на элементе, когда кнопка мыши находится в нажатом состоянии
mouseup : возникает при нахождении указателя мыши на элементе во время отпускания кнопки мыши
mouseover : возникает при вхождении указателя мыши в границы элемента
mousemove : возникает при прохождении указателя мыши над элементом
mouseout : возникает, когда указатель мыши выходит за пределы элемента
Например, обработаем события mouseover и mouseout :
Теперь при наведении указателя мыши на блок blueRect он будет окрашиваться в красный цвет, а при уходе указателя мыши — блок будет обратно окрашиваться в синий цвет.
Объект Event является общим для всех событий. Однако для разных типов событий существуют также свои объекты событий, которые добавляют ряд своих свойств. Так, для работы с событиями указателя мыши определен объект MouseEvent , который добавляет следующие свойства:
altKey : возвращает true , если была нажата клавиша Alt во время генерации события.
button : указывает, какая кнопка мыши была нажата.
clientX : определяет координату Х окна браузера, на которой находился указатель мыши во время генерации события.
clientY : определяет координату Y окна браузера, на которой находился указатель мыши во время генерации события.
ctrlKey : возвращает true , если была нажата клавиша Ctrl во время генерации события.
metaKey : возвращает true , если была нажата во время генерации события метаклавиша клавиатуры.
relatedTarget : определяет вторичный источник возникновения события.
screenX : определяет координату Х относительно верхнего левого угла экрана монитора, на которой находился указатель мыши во время генерации события.
screenY : определяет координату Y относительно верхнего левого угла экрана монитора, на которой находился указатель мыши во время генерации события.
shiftKey : возвращает true , если была нажата клавиша Shift во время генерации события.
Определим координаты клика:
9.6. События клавиатуры
Другим распространенным типом событий являются события клавиатуры.
keydown : возникает при нажатии клавиши клавиатуры и длится, пока нажата клавиша.
keyup : возникает при отпускании клавиши клавиатуры.
keypress : возникает при нажатии клавиши клавиатуры, но после события keydown и до события keyup . Надо учитывать, что данное событие генерируется только для тех клавиш, которые формируют вывод в виде символов, например, при печати символов. Нажатия на остальные клавиши, например, на Alt, не учитываются.
Для работы с событиями клавиатуры определен объект KeyboardEvent , который добавляет к свойствам объекта Event ряд специфичных для клавиатуры свойств:
altKey : возвращает true , если была нажата клавиша Alt во время генерации события.
key : возвращает символ нажатой клавиши, например, при нажатии на клавишу T это свойство будет содержать T . А если нажата клавиша Я, то это свойство будет содержать Я .
code : возвращает строковое представление нажатой клавиши физической клавиатуры QWERTY, например, при нажатии на клавишу T это свойство будет содержать KeyT , а при нажатии на клавишу ; (точка запятой), то свойство возвратит Semicolon .
При использовании этого свойства следует учитывать ряд момент. Прежде всего используется клавиатура QWERTY. То есть переключая раскладку, к примеру, на русскоязычную и нажмем на клавишу Я, то значением будет KeyZ — на клавиатуре QWERTY клавиша Z представляет ту же клавишу, что и на русскоязычной раскладке Я.
Другой момент — учитывается именно физическая клавиатура. Если нажата клавиша на виртуальной клавиатуре, то возвращаемое значение будет устанавливаться браузером исходя из того, какой клавише на физической клавиатуре соответствовало нажатие.
ctrlKey : возвращает true , если была нажата клавиша Ctrl во время генерации события.
metaKey : возвращает true , если была нажата во время генерации события метаклавиша клавиатуры.
shiftKey : возвращает true , если была нажата клавиша Shift во время генерации события.
Например, можно с помощью клавиш клавиатуры перемещать элемент на web-странице:
В данном случае обрабатывается событие keydown . В обработчике moveRect с помощью метода window.getComputedStyle() получаем стиль элемента blueRect . А затем из этого стиля выбираем значения свойств marginLeft и marginTop .
С помощью свойства e.key получаем нажатую клавишу. Список кодов клавиш клавиатуры можно посмотреть на странице https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values.
Здесь нас интересуют четыре клавиши: вверх, вниз, влево, вправо. Им соответственно будут соответствовать названия ArrowTop , ArrowDown , ArrowLeft и ArrowRight . Если одна из них нажата, производим действия: увеличение или уменьшение отступа элемента от верхней или левой границы. Ну и чтобы элемент не выходил за границы окна, проверяем предельные значения с помощью document.documentElement.clientWidth (ширина корневого элемента) и document.documentElement.clientHeight .
10. Работа с формами
10.1. Формы и их элементы
Один из способов взаимодействия с пользователями представляют HTML-формы. Например, если необходимо получить от пользователя некоторую информацию, можно определить на web-странице формы, которая будет содержать текстовые поля для ввода информации и кнопку для отправки. И после ввода данных можно обработать введенную информацию.
Для создания формы используется элемент <form> :
В JavaScript форма представлена объектом HtmlFormElement . И после создания формы можно к ней обратиться различными способами.
Первый способ заключается в прямом обращении по имени формы:
Второй способ состоит в обращении к коллекции форм документа и поиске в ней нужной формы:
С помощью свойства name объекта формы можно получить значение атрибута name у соответствующего элемента формы в коде HTML.
Еще один способ сочетает оба подхода:
И также можно применять стандартные способы для поиска элемента формы, например, по идентификатору, по тегу или по селектору.
Форма имеет ряд свойств, из которых наиболее важными являются, рассмотренное выше, свойство name . Так же имеется свойство elements , которое содержит коллекцию элементов формы.

Среди методов формы надо отметить метод submit() , который отправляет данные формы на сервер. Так же есть метод reset() , который очищает поля формы.
10.1.1. Элементы форм
Форма может содержать различные элементы ввода HTML: input , textarea , button , select и т.д. Но все они имеют ряд общих свойств и методов.
Так же, как и форма, элементы форм имеют свойство name , с помощью которого можно получить значение атрибута name :
Другим важным свойством является свойство value , которое позволяет получить или изменить значение поля:
С помощью свойства form можно получить родительский объект формы:
Данное свойство может быть полезно, например, при отправке формы, когда перед непосредственной отправкой формы необходимо провести валидацию всех полей формы.
Свойство type позволяет получить тип поля ввода. Это либо название тега элемента, либо значение атрибута type у элементов input .
Из методов можно выделить методы:
focus() : устанавливает фокус на элемент
blur() : убирает фокус с элемента
10.2. Кнопки
Для отправки введенных данных на форме используются кнопки. Для создания кнопки используется либо элемент button :
Либо элемент input :
С точки зрения функциональности в HTML эти элементы не совсем равноценны, но в данном случае они нас интересуют с точки зрения взаимодействия с кодом JavaScript.
При нажатии на любой из этих двух вариантов кнопки происходит отправка формы по адресу, который указан у формы в атрибуте action , либо по адресу web-страницы, если атрибут action не указан. Однако в коде JavaScript можно перехватить отправку, обрабатывая событие click .
При нажатии на кнопку происходит событие click , и для его обработки к кнопке прикрепляем обработчик sendForm . В этом обработчике проверяем введенный в текстовое поле текст. Если его длина больше 5 символов, то выводим сообщение о недопустимой длине и прерываем обычный ход события с помощью вызова e.preventDefault() . В итоге форма не отправляется.
Если же длина текста меньше шести символов, то также выводится сообщение, и затем форма отправляется.
Также можно при необходимости при отправке изменить адрес, на который отправляются данные:
В данном случае, если длина текста больше 5 символов, то текст отправляется, только теперь он отправляется по адресу PostForm , поскольку задано свойство action :
Для очистки формы предназначены следующие равноценные по функциональности кнопки:
При нажатии на кнопки произойдет очистка форм. Но также функциональность по очистке полей формы можно реализовать с помощью метода reset() :
Кроме специальных кнопок отправки и очистки на форме также может использоваться обычная кнопка:
При нажатии на подобную кнопку отправки данных не происходит, хотя также генерируется событие click :
При нажатии на кнопку получаем введенный в текстовое поле текст, создаем новый элемент параграфа для этого текста и добавляем параграф в элемент printBlock .

10.3. Текстовые поля
Для ввода простейшей текстовой информации предназначены элементы <input type=»text» :
Данный элемент поддерживает ряд событий, в частности:
focus : происходит при получении фокуса.
blur : происходит при потере фокуса.
change : происходит при изменении значения поля.
select : происходит при выделении текста в текстовом поле.
keydown : происходит при нажатии клавиши клавиатуры.
keypress : происходит при нажатии клавиши клавиатуры для печатаемых символов.
keyup : происходит при отпускании ранее нажатой клавиши клавиатуры.
Здесь к текстовому полю прикрепляется три обработчика для событий blur , focus и change . Обработка события change позволяет сформировать что-то вроде привязки: при изменении текста весь текст отображается в блоке printBlock . Но надо учитывать, что событие change возникает не сразу после изменения текста, а после потери им фокуса.
Обработка события потери фокуса blur позволяет провести валидацию введенного значения. Например, в данном случае если текст состоит из пробелов или не был введен, то окрашиваем границу поля в красный цвет.

Кроме данного текстового поля есть еще специальные поля ввода. Так, поле <input type=»password» предназначено для ввода пароля. По функциональности оно во многом аналогично обычному текстовому полю за тем исключением, что для вводимых символов используется маска:
Если необходимо чтобы на форме было некоторое значение, но чтобы оно было скрыто от пользователя, то для этого могут использоваться скрытые поля:
Для скрытого поля обычно не используется обработка событий, но так же, как и для других элементов, можно в JavaScript получить его значение или изменить его.
10.3.1. Элемент textarea
Для создания многострочных текстовых полей используется элемент textarea :
Данные элемент генерирует все те же самые события, что и обычное текстовое поле:
Здесь к текстовому полю прикрепляются обработчики для событий keypress и keydown . В обработчике keypress получаем введенный символ с помощью конвертации числового кода клавиши в строку:
Затем символ добавляется к содержимому блока printBlock .
Событие keypress возникает при нажатии на клавиши для печатаемых символов, то такие символы отображаются в текстовом поле. Однако есть и другие клавиши, которые оказывают влияние на текстовое поле, но они не дают отображаемого символа, поэтому не отслеживаются событием keypress . К таким клавишам относится клавиша Backspace, которая удаляет последний символ. И для ее отслеживания также обрабатываем событие keydown . В обработчике keydown удаляем из строки в блоке printBlock последний символ.

10.4. Флажки и переключатели
Особую группу элементов ввода составляют флажки и переключатели.
10.4.1. Флажки
Флажки представляют поле, создаваемое с помощью элемента <input type=»checkbox» и в которое можно поставить отметки. Отличительную особенность флажка составляет свойство checked , которое в отмеченном состоянии принимает значение true :
Нажатие на флажок генерирует событие click . В данном случае при обработке данного события просто выводится информация, отмечен ли данный флажок, в блок div .

10.4.2. Переключатели
Переключатели представляют группы кнопок, из которых можно выбрать только одну. Переключатели создаются элементом <input type=»radio» .
Выбор или нажатие на одну из них также представляет событие click :
При создании группы переключателей их атрибут name должен иметь одно и то же значение. В данном случае это — languages . То есть переключатели образуют группу languages .
Поскольку переключателей может быть много, то при прикреплении к ним обработчика события необходимо пробежаться по всему массиву переключателей, который можно получить по имени группы:
Значение выбранного переключателя также можно получить через объект Event : e.target.value

Каждый переключатель также, как и флажок, имеет свойство checked , которое возвращает значение true , если переключатель выбран. Например, отметим последний переключатель:
10.5. Список
Для создания списка используется HTML-элемент select . Причем с его помощью можно создавать как выпадающие списки, так и обычные с одинарным или множественным выбором.
Атрибут size позволяет установить, сколько элементов будут отображаться одномоментно в списке. Значение size=»1″ отображает только один элемент списка, а сам список становится выпадающим. Если установить у элемента select атрибут multiple , то в списке можно выбрать сразу несколько значений.
Каждый элемент списка представлен HTML-элементом option , у которого есть отображаемая метка и есть значения в виде атрибута value .
В JavaScript элементу select соответствует объект HTMLSelectElement , а элементу option — объект HtmlOptionElement или просто Option .
Все элементы списка в JavaScript доступны через коллекцию options . А каждый объект HtmlOptionElement имеет свойства: index , text (отображаемый текст) и value (значение элемента). Например, получим первый элемент списка и выведем о нем через его свойства всю информацию:

В JavaScript можно не только получать элементы, но и динамически управлять списком.
Для добавления на форме предназначены два текстовых поля (для текстовой метки и значения элемента option ) и кнопка. Для удаления выделенного элемента предназначена еще одна кнопка.
За добавление в коде JavaScript отвечает функция addOption() , в которой получаем введенные в текстовые поля значения, создаем новый объект Option и добавляем его в массив options объекта списка.
За удаление отвечает функция removeOption() , в которой просто получаем индекс выделенного элемента с помощью свойства selectedIndex и в коллекции options приравниваем по этому индексу значение null .

Для добавления/удаления также в качестве альтернативы можно использовать методы элемента select :
10.5.1. События элемента select
Элемент select поддерживает три события:
blur потеря фокуса
focus получение фокуса
change изменение выделенного элемента в списке.
Рассмотрим применение события select :
11. Хранение данных
11.1. Куки
Одну из возможностей сохранения данных в JavaScript представляет использование куки. Для работы с куками в объекте document предназначено свойство cookie .
Для установки куков достаточно свойству document.cookie присвоить строку с куками:
В данном случае устанавливается кука, которая называется login и которая имеет значение tom32 . И в большинстве браузеров мы можем посмотреть ее, узнать всю информацию о ней и в дальнейшем ее можно использовать в приложении:

Строка куки принимает до шести различных параметров:
имя куки
значение
срок окончания действия ( expires )
путь ( path )
домен ( domain )
Выше использовались только два параметра: имя куки и значение. То есть в случае со строкой «login=tom32;» куки имеет имя login и значение tom32 .
Но, подобная куки имеет очень ограниченный срок жизни: если явным образом не установить срок действия, то кука будет удалена с закрытием браузера. Подобная ситуация, возможно, идеальна для тех случаев, когда необходимо удалять всю информацию после завершения работы с web-приложением и закрытия браузера. Однако данное поведение не всегда подходит.
И в этом случае нам надо установить параметр expires , то есть срок действия куков:
То есть срок действия куки login истекает в понедельник 30 августа 2021 года в 00:00. Формат параметра expires очень важен. Однако его можно сгенерировать программно. Для этого мы можем использовать метод toUTCString() объекта Date :
В данном случае срок действия куки будет составлять 4 часа.
Если вдруг нам надо установить куки для какого-то определенного пути на сайте, то мы можем использовать параметр path . Например, мы хотим установить куки только для пути www.mysite.com/home :
В этом случае для других путей на сайте, например, www.mysite.com/shop , эти куки будут недоступны.
Если на нашем сайте есть несколько доменов, и мы хотим установить куки непосредственно для определенного домена, тогда можно использовать параметр domain . Например, у нас на сайте есть поддомен blog.mysite.com :
Параметр path=/ указывает, что куки будут доступны для всех директорий и путей поддомена blog.mysite.com .
Последний параметр — secure задает использование SSL (SecureSockets Layer) и подходит для сайтов, использующих протокол HTTPS. Если значение этого параметра равно true , то куки будут использоваться только при установке защищенного соединения ssl. По умолчанию данный параметр равен false .
11.1.1. Получение куки
Для простейшего извлечения куки из браузера достаточно обратиться к свойству document.cookie :
Здесь были установлены три куки, и браузер выведет нам все эти куки:

Извлеченные куки не включают параметры expires , path , domain и secure . Кроме того, сами куки разделяются точкой с запятой, поэтому нужно еще провести некоторые преобразования, чтобы получить их имя и значение:
11.2. Web Storage
Хотя куки позволяют сохранять информацию, они имеют ряд ограничений. Например, браузер имеет ограничения на размер куков — каждая кука не может превышать 4 кб. Куки имеют срок действия, после которого удаляются. Куки являются неотъемлемой чертой протокола HTTP и при каждом запросе к серверу передаются вместе с запросом на сервер. Однако для работы с куками на стороне клиента в коде JavaScript не имеет значения передача куков на сервер. Кроме того, для извлечения сохраненных куков надо написать некоторую порцию кода.
Поэтому в HTML5 была внедрена новая концепция для хранения данных — web storage. Web storage состоит из двух компонентов:
session storage
local storage
Session storage представляет временное хранилище информации, которая удаляется после закрытия браузера.
Local storage представляет хранилище для данных на постоянной основе. Данные из local storage автоматически не удаляются и не имеют срока действия. Эти данные не передаются на сервер в запросе HTTP. Кроме того, объем local storage составляет в Chrome и Firefox 5 Мб для домена, а в IE — 10 Мб.
Все данные в web storage представляют набор пар ключ-значение. То есть каждый объект имеет уникальное имя-ключ и определенное значение.
Для работы с local storage в JavaScript используется объект localStorage , а для работы с session storage — объект sessionStorage .
Для сохранения данных надо передать в метод setItem() объекта localStorage :
В этот метод передаются два значения: ключ и значение сохраняемого объекта.
Если в localStorage уже есть объект с ключом login , то его значение заменяется новым.
Для получения сохраненных данных надо вызвать метод getItem() :
В этот метод передается ключ объекта.
Чтобы удалить объект, применяется метод removeItem() , который принимает ключ удаляемого объекта:
И для полного удаления всех объектов из localStorage можно использовать метод clear() :
С сохранением простых объектов все просто, однако при этом надо учитывать, что данные в localStorage сохраняются в виде строки:
Если в данном случае не преобразовать значение к числу с помощью parseInt() , то age будет действовать как строка.
Трудности могут возникнуть с сохранением сложных объектов:
В этом случае нам надо использовать сериализацию в формат JSON:
И в завершении надо сказать, что в некоторых браузерах с помощью специальных инструментов мы можем увидеть сохраненные объекты в local storage. Например, в Google Chrome:

12. Коллекции и итераторы
12.1. Итераторы
Итераторы применяются для организации последовательного доступа к элементам коллекции — массивам, объектам Set и Map . Итераторы предоставляют метод next() , который возвращает два значения: value и done . value хранит собственно значение текущего перебираемого элемента. А свойство done указывает, есть ли еще в коллекции объекты, доступные для перебора.
Некоторые методы коллекций возвращают итераторы. Например, метод entries() , который есть у коллекций Array , Set , Map :
Метод next() возвратит следующий объект на консоль:
Здесь мы видим, что свойство done имеет значение false , так как мы перебрали только один элемент в множестве, и там еще есть два элемента.
Свойство value представляет массив из двух значений. Первое значение представляет ключ или индекс элемента массива, а второй элемент — значение по этому индексу имеет ключ и значение.
Соответственно мы можем организовать и перебор всей коллекции:
Но в этом нет смысла, поскольку все коллекции, возвращающие итераторы, поддерживают перебор с помощью цикла for … of , который как раз и использует итератор для получения элементов:
Если мы хотим извлечь еще и индекс элемента в массиве, то мы можем использовать для перебора итератор из entries() :
12.2. Генераторы
Генераторы представляют особый тип функции, которые используются для генерации значений. Для определения генераторов применяется символ звездочки * , который ставится после ключевого слова function . Например, определим простейший генератор:
Функция getNumber() представляет генератор. Функция генератора возвращает итератор. Для получения значения из генератора применяется оператор yield . То есть фактически в данном случае генератор генерирует число 5 .
Далее с помощью вызова этой функции создается объект итератора в виде переменной numberGenerator . Используя этот объект, мы можем получать из генератора значения.
Для перехода к следующему значению применяется метод next() . Если мы посмотрим на консольный вывод, то мы увидим, что данный метод возвращает следующие данные:
То есть по сути возвращается объект, свойство value которого содержит собственно сгенерированное значение. А свойство done указывает, достигли ли мы конца генератора.
Теперь изменим код:
Здесь обращение к методу next() происходит два раза:
Но функция генератора getNumber() генерирует только одно значение — число 5 . Поэтому при повторном вызове свойство value будет иметь значение undefined , а свойство done — true , то есть работа генератора завершена.
Генератор может создавать множество значений:
То есть при первом вызове метода next() из итератора извлекается значение, которое идет после первого оператора yield , при втором вызове метода next() — значение после второго оператора yield и так далее.
Поскольку для получения значений применяется итератор, то мы можем использовать цикл for … of :
Генератор необязательно содержит только определение операторов yield . Он также может содержать более сложную логику.
С помощью генераторов удобно создавать бесконечные последовательности:
12.2.1. Передача данных в генератор
С помощью next() можно передать в генератор данные.
При втором вызове метода next() :
Мы можем получить переданные через него данные, присвоив результат первого оператора yield :
То есть здесь переменная n будет равна 2 , так как в метод next() передается число 2 .
Далее мы можем использовать это значение, например, для генерации нового значения:
Соответственно, переменная m получить значение, переданное через третий вызов метода next() , то есть число 3 .
12.2.2. Инициализация генератора
Есть также другой способ передачи данных в генератор, когда мы передаем некоторые данные в саму функцию генератора, то есть фактически инициализируем генератор некоторыми начальными данными:
В данном случае в генератор передается массив, который используется для генерации значений в таймере.
12.3. Множества Set
Множества (sets) представляют структуру данных, которая может хранить только уникальные значения. В JavaScript функционал множества определяет объект Set . Для создания множества применяется конструктор этого объекта:
Также можно передать в конструктор массив значений, которыми будет инициализировано множество:
В данном случае в множество передаются данные из массива. Однако поскольку множество может хранить только уникальные значения, то при его создании повторяющиеся значения, которые есть в массиве, удаляются.
Для проверки количества элементов можно использовать свойство size .
12.3.1. Добавление
Для добавления применяется метод add() . Его результатом является измененное множество:
При этом, поскольку множество хранит только уникальные значения, то добавление элементов, которые уже в нем есть, не имеет смысла.
Так как метод add() возвращает ссылку на это же множество, то мы можем вызывать методы по цепочке:
12.3.2. Удаление
Для удаления элементов применяется метод delete() :
Причем данный метод возвращает булево значение: true — если элемент удален и false — если удаление не произошло (например, когда удаляемого элемента нет в множестве):
Если необходимо удалить вообще все элементы из множества, то применяется метод clear() :
12.3.3. Проверка наличия элемента
Если нужно проверить, если ли элемент в множестве, то используется метод has() . Если элемент есть, то метод возвращает true , иначе возвращает false :
12.3.4. Перебор множества
Для перебора элементов множества применяется метод forEach() :
Для совместимости с массивами, которые тоже имеют метод forEach() , в данный метод передается функция обратного вызова, которая принимает три параметра. Непосредственно для множества первый и второй параметры представляют текущий перебираемый элемент, а третий параметр — перебираемое множество.
Также для перебора множества можно использовать цикл for … of :
12.4. Map
Map или карта (отображение, словарь) представляет структуру данных, где каждый элемент имеет ключ и значение. Ключи в рамках карты являются уникальными, то есть с одним ключом может быть сопоставлен только один элемент. Для создания карты применяется конструктор объекта Map :
Также можно инициализировать карту начальными значениями. Для этого в конструктор передается массив, элементы которого представляют массивы из двух элементов — первый элемент будет выступать в качестве ключа, а второй — в качестве значения:
В данном случае числа 1 , 2 , 3 являются ключами, а строки a , b , c — значениями.
При этом ключи и значения необязательно должны быть одного типа:
12.4.1. Добавление и изменение элементов
Для добавления или изменения значения применяется метод set() :
Первый параметр метода set() представляет ключ, а второй параметр — значение элемента. Если по такому ключу нет элементов, то добавляется новый элемент. Если ключ уже есть, то уже имеющийся элемент изменяет свое значение.
12.4.2. Получение элементов
Для получения элемента по ключу применяется метод get() , в который передается ключ элемента:
Если map не содержит элемента по заданному ключу, то метод возвращает undefined .
Чтобы избежать возвращения undefined , мы можем проверить наличие элемента по ключу с помощью метода has() . Если элемент по ключу имеется, то метод возвращает true , иначе возвращается false :
12.4.3. Удаление элементов
Для удаления одного элемента по ключу применяется метод delete() :
Для удаления всех элементов используется метод clear() :
12.4.4. Перебор элементов
Для перебора элементов используется метод forEach() :
Метод forEach в качестве параметра получает функцию обратного вызова, которая имеет три параметра. Первый и второй параметры — это соответственно значение и ключ текущего перебираемого элемента, а третий параметр — перебираемый объект Map.
Также для перебора объекта Map можно использовать цикл for … of :
Каждый элемент из Map помещается в переменную item , которая в свою очередь представляет массив. Первый элемент этого массива — ключ, а второй элемент — значение элемента.
Также объект Map имеет два дополнительных метода: keys() позволяет перебрать только ключи и values() позволяет перебирать значения элементов. Оба метода возвращают итераторы, поэтому для перебора ключей и значений по отдельности также можно использовать цикл for…of :
12.5. WeakSet и WeakMap
12.5.1. WeakSet
Объект WeakSet во многом похож на обычное множество. Он также может хранить только уникальные значения, но каждый его элемент должен представлять объект.
Для создания объекта WeakSet используется его конструктор, в который можно передать начальные значения:
Для инициализации как в случае с объектом Set в конструктор передается массив, но данный массив содержит именно объекты, а не скалярные значения, типа чисел или строк.
Для добавления данных в WeakSet применяется метод add() :
Причем опять же добавить мы можем только объект, а не скалярные значения типа чисел или строк.
Для удаления применяется метод delete() , в который передается ссылка на удаляемый объект:
Если надо проверить, имеется ли объект в WeakSet , то можно использовать метод has() , который возвращает true при наличии объекта:
12.5.2. WeakMap
WeakMap представляет развитие коллекции Map . Особенностью WeakMap является то, что все ее элементы должны представлять объекты. При этом объектами должны быть как ключи, так и значения.
Для получения объектов по ключу из WeakMap применяется метод get() :
Для добавления новых объектов или изменения старых применяется метод set() :
Чтобы проверить наличие элемента по определенному ключу, применяется метод has() , который возвращает true при наличии элемента:
Для удаления элемента по ключу применяется метод delete() :
13. AJAX
Современные web-приложение, как правило, разделяются на две части: клиент и сервер. Клиент представляет собой web-страницу с кодом JavaScript. К серверным технологиям относятся Java, PHP, Ruby, Node.js, ASP.NET и т.д., которые получают запрос от клиента, обрабатывают и отправляют в ответ результат обработки.
AJAX представляет технологию для отправки запросов к серверу из клиентского кода JavaScript без перезагрузки страницы. Сам термин расшифровывается как Asynchronous JavaScript And XML. То есть изначально AJAX предполагал асинхронное взаимодействие клиента и сервера посредством данных в формате XML. Хотя сейчас XML во многом вытеснил формат JSON. В любом случае AJAX революционизировал web-среду, позволив создавать динамичные отзывчивые web-приложения.
Поскольку AJAX предполагает взаимодействие клиента и сервера, то для работы с AJAX и в частности этой главы необходим локальный web-сервер. Это может быть любой web-сервер: nginx, Apache, IIS и т.д.
13.1. Объект XMLHttpRequest
Для создания приложений, использующих AJAX, применяются различные способы. Но самым распространенным способом является использование объекта XMLHttpRequest :
После создания объекта XMLHttpRequest можно отправлять запросы к серверу. Но для начала надо вызвать метод open() для инициализации:
Метод open() принимает три параметра: тип запроса ( GET , POST , HEAD , PUT ), адрес запроса и третий необязательный параметр — логическое значение true или false , указывающее, будет ли запрос осуществляться в асинхронном режиме. То есть в данном случае запрос будет иметь тип GET , он будет направляться по адресу http://localhost/hello.txt в синхронном режиме, так как стоит значение false (для асинхронного режима указывается значение true ).
Синхронный и асинхронный режим отличаются тем, что запрос в синхронном режиме пока запрос не выполнится, остальной код JavaScript не может выполняться. По умолчанию, если третий параметр не используется, то запрос отправляется в асинхронном режиме, что позволяет параллельно с выполнением запроса выполнять также и другой код JavaScript. И в большинстве случаев, как правило, используется именно асинхронный режим.
Кроме того, метод open() может принимать еще два параметра: логин и пароль пользователя, если для выполнения запроса нужна аутентификация.
После инициализации запроса методом open() необходимо отправить запрос с помощью метода send() :
13.1.1. Свойства XMLHttpRequest
Объект XMLHttpRequest имеет ряд свойств, которые позволяют проконтролировать выполнение запроса:
status : содержит статусный код ответа HTTP , который пришел от сервера. С помощью статусного кода можно судить об успешности запроса или об ошибках, которые могли бы возникнуть при его выполнении. Например, статусный код 200 указывает на то, что запрос прошел успешно. Код 403 говорит о необходимости авторизации для выполнения запроса, а код 404 сообщает, что ресурс не найден и так далее.
statusText : возвращает текст статуса ответа, например, «200 OK»
responseType : возвращает тип ответа. Есть следующие типы:
response : возвращает ответ сервера
responseText : возвращает текст ответа сервера
responseXML : возвращает xml , если ответ от сервера в формате xml
Например, выполним запрос к текстовому файлу, который находится на локальном web-сервере. Для выполнения AJAX-запросов потребуется запущенный локальный web-сервер, на котором будет лежать файл hello.txt , в котором будет содержаться одна строка: Привет мир .
Код web-страницы (пусть она называется test.html ) будет следующим:
И после загрузки страницы выполнится ajax-запрос к ресурсу http://localhost:8080/hello.txt . Но важно отметить, что получение статуса сразу после вызова метода request.send() будет работать только для синхронного запроса.

13.1.2. Асинхронные запросы
Хотя синхронные запросы вполне работают и их можно использовать, но в то же время их рекомендуется избегать. Поскольку нередко запрос может занять продолжительное время, то это может заблокировать выполнение остального кода и работу с HTML-страницей до окончания выполнения запроса. Поэтому рекомендуется использовать преимущественно асинхронные запросы.
Работа с асинхронными запросами чуть более сложна, чем с синхронными, поскольку нам надо еще обработать событие readystatechange объекта XMLHttpRequest .
При асинхронном запросе объект XMLHttpRequest использует свойство readyState для хранения состояния запроса. Состояние запроса представляет собой число:
0 : объект XMLHttpRequest создан, но метод open() еще не был вызван для инициализации объекта
1 : метод open() был вызван, но запрос еще не был отправлен методом send()
2 : запрос был отправлен, заголовки и статус ответа получены и готовы к использованию
3 : ответ получен от сервера
4 : выполнение запроса завершено (даже если получен код ошибки, например, 404 )
Событие readystatechange возникает каждый раз, когда изменяется значение свойства readyState . Например, выполним асинхронный запрос:
Кроме обработки события readystatechange для получения ответа сервера можно также обрабатывать событие load , которое возникает после выполнения запроса. Его использование аналогично:
13.2. Отправка данных
Принцип отправки данных может отличаться в различных ситуациях. Рассмотрим эти ситуации.
13.2.1. Отправка GET-запроса
GET-запрос характеризуется тем, что данные могут отправляться в строке запроса:
Для отправки берем свойства объекта user и формируем из их значений сроку с параметрами:
Затем эта строка добавляется к строке запроса в методе open(«GET», «http://localhost:8080/postdata.php?»+body)
Предполагается, что данные отправляются скрипту на языке php postdata.php , который может иметь, например, следующее содержание:
Конкретная технология стороны сервера тут не важна. И в качестве тестирования можно взять любую другую технологию. Например, ASP.NET MVC . Метод контроллера в ASP.NET MVC , который принимает данные, мог бы выглядеть следующим образом:
13.2.2. Кодирование параметров
Все отправляемые в GET-запросе параметры разделяются знаком амперсанда ( & ). Но что, если какой-нибудь параметр имеет знак амперсанда. Например,
В этом случае при получении параметров скрипт на стороне сервера может неправильно обработать данные и неправильно извлечь параметры. Поэтому, чтобы кодировать все передаваемые данные, нужно применять функцию encodeURIComponent() :
При этом строка Tom&Tim будет кодирована в следующую строку: Tom%26Tim .
При необходимости мы можем выполнить обратное декодирование с помощью функции decodeURIComponent() :
13.2.3. POST-запросы
Отправка данных в POST-запросах будет немного отличаться:
Для отправки данных методом POST надо установить заголовок Content-Type с помощью метода setRequestHeader() . В данном случае заголовок имеет значение application/x-www-form-urlencoded .
13.2.4. Отправка форм. FormData
Начиная со спецификации XMLHttpRequest2 в JavaScript появился новый объект — FormData , который позволяет сериализовать данные формы для ее последующей отправки. При этом нам даже необязательно создавать форму в коде HTML, мы можем создать ее динамически в JavaScript:
Для добавления данных у объекта FormData используется метод append(‘имя_параметра’, значение) . При этом никакие заголовки указывать не надо.
Также мы можем определить форму в HTML и использовать ее для отправки:
Для сериализации всех полей формы нам достаточно передать объект формы в конструктор FormData: var formData = new FormData(form) .
13.2.5. Отправка данных в формате json
Для отправки данных в формате json нам необходимо установить соответствующий заголовок и сериализовать данные с помощью метода JSON.stringify() :
13.3. Promise в AJAX-запросах
Как видно из примеров прошлых тем для создания AJAX-запросов используются фактически повторяющиеся вызовы, отличающиеся лишь деталями — строкой запроса, функциями обработки ответа. И вполне было бы неплохо создать для всех действий, связанных с асинхронным AJAX-запросом, создать какую-то общую абстракцию и затем использовать ее при следующих обращениях к серверу.
Для создания дополнительного уровня абстракции в данном случае удобно применять объект Promise , который обертывает асинхронную операцию в один объект, который позволяет определить действия, выполняющиеся при успешном или неудачном выполнении этой операции.
Инкапсулируем асинхронный запрос в объект Promise :
Метод get получает в качестве параметра адрес ресурса сервера и возвращает объект Promise . Конструктор Promise в качестве параметра принимает функцию обратного вызова, которая в свою очередь принимает два параметра — две функции: одна выполняется при успешной обработке запроса, а вторая — при неудачной.
Допустим, на сервере будет размещен файл users.json со следующим содержимым:
Теперь вызовем метод get() для осуществления запроса к серверу:
Для обработки результата объекта Promise вызывается метод then() , который принимает два параметра:
функцию, вызываемую при успешном выполнении запроса
функцию, которая вызывается при неудачном выполнении запроса.
Метод then() также возвращает объект Promise . Поэтому при необходимости мы можем применить к его результату цепочки вызовов метода then: get().then().then()… . Например:
В данном случае функция в первом вызове метода then получает ответ сервера и возвращает разобранные данные в виде массива с помощью функции JSON.parse() .
Функция во втором вызове then() получает эти разобранные данные, то есть массив, в виде параметра (возвращаемое значение предыдущего then является параметром для последующего then ). Затем первый элемент массива выводится на консоль.
Для обработки ошибок мы можем использовать метод catch() , в который передается функция обработки ошибок:
Подобным образом через Promise можно было бы отправлять данные на сервер:
Формы ввода данных в JavaScript
О формах в JavaScript можно рассказывать очень долго. В книгах по этому языку данному вопросу посвящены целые разделы. Поэтому данная статья — лишь краткий обзор. Хотя возможно, что я ещё буду возвращаться к этой теме.
Ну а если хотите всё и сразу, то вам сюда:
В сценариях JavaScript большее внимание уделяется не передаче и обработке данных формы, а обработке событий. Форма и все расположенные на ней элементы ввода имеют обработчики событий, позволяющие программировать действия, выполняемые сценарием в ответ на взаимодействие пользователя с формой. Если пользователь, например, щелкает на кнопке или вводит текст в поле, то программа JavaScript может получить уведомление через обработчик события и отреагировать на него, изменив значение, отображаемое в каком-либо другом элементе формы.
При этом вовсе не обязательно отправлять данные на сервер. Например, программой может быть калькулятор, который обрабатывает данные, введённые пользователем, и сразу выдаёт итоги вычислений пользователю.
Объект Form
Объект Form в JavaScript представляет HTML-форму. Формы доступны в виде элементов массива forms[] , который является свойством объекта Document .
Формы расположены в этом массиве в том же порядке, что и в документе. То есть элемент document.forms[0] ссылается на самую первую форму документа. На последнюю форму документа можно сослаться, например, так:
Программисту будет весьма полезно свойство объекта Form – массив elements[] , который содержит объекты JavaScript различных типов, представляющие различные элементы ввода формы. Элементы этого массива также располагаются в той же последовательности, в которой они расположены в документе. Например, на второй элемент третьей формы документа в текущем окне можно сослаться так:
document.forms[2].elements[1]
Определение элементов формы
Элементы HTML-формы позволяют создавать простые пользовательские интерфейсы для JavaScript-программ.
Пример такой формы с несколькими элементами я уже приводил здесь, поэтому повторяться не буду. А в этой статье приведу список основных элементов форм с их свойствами и обработчиками событий.
| Объект | HTML-тег | Свойство “type” | Событие | Описание |
| Button | <input type=»button»> или <button type=»button»> | «button» | onclick | Кнопка |
| Checkbox | <input type=»checkbox»> | «checkbox» | onclick | Флажок |
| File | <input type=»file»> | «file» | onchange | Поле для ввода имени файла, загружаемого на веб-сервер |
| Hidden | <input type=»hidden»> | «hidden» | Нет | Данные, сохраняемые вместе с формой, но невидимые пользователю |
| Option | <option> | Нет | Обработчики событий подключаются к объекту Select, а не к отдельным объектам Option | Один элемент объекта Select |
| Password | <input type=»password»> | «password» | onchange | Поле для ввода пароля (набранные символы невидимы) |
| Radio | <input type=»radio»> | «radio» | onclick | Переключатель. Одновременно может быть установлен только один. |
| Reset | <input type=»reset»> или <button type=»reset»> | «reset» | onclick | Кнопка, очищающая значения формы |
| Select | <Select> | «select-one» | onchange | Список или выпадающее меню, в котором может быть выбран один элемент (см. также объект Option) |
| Select | <select multiple> | «select-multiple» | onchange | Список, в котором может быть выбрано несколько элементов (см. также объект Option). |
| Submit | <input type=»submit»> или <button type=»submit»> | «submit» | onclick | Кнопка для передачи данных формы |
| Text | <input type=»text»> | «text» | onchange | Однострочное поле ввода |
| Textarea | <textarea> | «textarea» | onchange | Многострочное поле ввода |
Пример использования форм в JavaScript
Ну а теперь ещё простой пример использования форм ввода-вывода и на этом первое знакомство закончим.
Здесь у нас форма с четырьмя элементами: поле ввода текста ( text ), поле ввода пароля ( password ), кнопка очистки полей ввода ( reset ) и просто кнопка ( button ).
Для обычной кнопки имеется обработчик ShowPassword() события onClick , который выводит окно с паролем (знаки, введённые в поле password , невидимы пользователю).
Для кнопки reset обработчик события создавать необязательно, потому что нажатие на эту кнопку автоматически очищает все поля формы.
Ну а в начале сценария мы использовали способ доступа к элементам формы через массивы форм и элементов:
document.write(document.forms[0].elements[2].value);
Этот код выведет слово “Очистить”, поскольку третьим элементом формы является кнопка с надписью “Очистить”.
А вот так примерно всё это будет выглядеть:

Эта статья — лишь капля в море знаний о JavaScript. Если хотите испить эту чашу до дна, то изучить этот язык, а также jQuery и Ajax можно здесь: