# JavaScript: От Основ до Full-Stack Разработки

Александр Ольшевски

Практическое руководство для современной веб-разработки на JavaScript. Книга предназначена для начинающих , а также желающих углубить свои знания до уровня профессионального Full-Stack разработчика.

Вы начнете с самых азов: погрузитесь в синтаксис, освоите типы данных, функции, объекты и современные возможности ES6+. Перейдете к созданию динамичных пользовательских интерфейсов с помощью мощного React-фреймворка, научитесь управлять состоянием, использовать хуки и строить сложные UI-компоненты.

Вас ждет погружение в мир Backend-разработки с Node.js. Вы освоите создание RESTful API на Express, работу с базами данных (SQL и NoSQL), научитесь строить масштабируемые серверные приложения. Особое внимание уделено тестированию кода (Unit-тесты с Jest), повышению надежности с помощью TypeScript – языка.

Книга также охватывает темы, такие как архитектурные паттерны (SOLID, MVC), чистый код, DevOps и автоматизация (CI/CD, Docker), основы облачных технологий (AWS, Azure, GCP) и многое другое.

> Преждевременная оптимизация – корень всех зол. Сначала сделайте так, чтобы это работало, затем сделайте так, чтобы это работало правильно, и только потом делайте так, чтобы это работало быстро.

## Вступление

Добро пожаловать в мир JavaScript!

JavaScript – ключевая технология современной веб-разработки, обеспечивающая интерактивность и динамическое поведение клиентской части приложений.

Освоение этого языка открывает возможности для создания полнофункциональных веб-платформ – от адаптивных пользовательских интерфейсов до сложных одностраничных приложений (SPA).


Методология и структура руководства

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

JavaScript уникален своей универсальностью: он является стандартом для клиентской разработки (frontend) и активно применяется на серверной стороне (backend) благодаря платформе Node.js. Такой охват делает его одним из наиболее востребованных инструментов в индустрии.

Ключевые принципы обучения:

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

Практика: Теоретические концепции подкрепляются реальными примерами кода и проектными заданиями.

Глубина понимания: Особое внимание уделяется не только тому, «как» работает язык, но и «почему» он работает именно так.

Рекомендации по работе с материалом:

Выполняйте все практические задания и экспериментируйте с кодом.

Анализируйте ошибки – они являются critical частью процесса обучения.

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

Структура программы:

Основы веб-технологий и введение в JavaScript

Фундаментальные конструкции языка и работа с DOM

Асинхронное программирование и сетевое взаимодействие

Инструменты разработки и современные фреймворки

Продвинутые паттерны и промышленная разработка

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

## Глава 1: Основы Веб-Технологий: Где Живет JavaScript.

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

Представьте интернет как огромную библиотеку, а веб-сайты – как книги на полках.

1.1. Как работает Интернет: Клиент и Сервер

Когда вы набираете адрес сайта в браузере (например, google.com), происходит следующее:

Ваш браузер (например, Chrome, Firefox, Safari) выступает в роли клиента. Он отправляет запрос на сервер, где хранится информация о сайте.

Сервер – это мощный компьютер, который, как бы “слушает” ваши запросы. Получив такой запрос, он находит нужные файлы (текст, картинки, код) и отправляет их обратно вашему браузеру.

Браузер получает эти файлы и рендерит (отображает) их так, чтобы вы увидели знакомую страницу.

Процесс выглядит примерно так: Вы (Браузер/Клиент) <– Запрос –> Сервер <– Ответ –> Вы (Браузер/Клиент)

1.2. Три кита веб-разработки: HTML, CSS, JavaScript

Чтобы страница выглядела и работала так, как мы привыкли, используются три основные технологии:

HTML (HyperText Markup Language): Это “скелет” веб-страницы. HTML определяет структуру контента: заголовки, абзацы, списки, изображения, ссылки. Он говорит браузеру, что находится на странице.

Пример:

htmlЗаголовок страницы

Это абзац текста.

CSS (Cascading Style Sheets): Это “внешний вид” или “одежда” веб-страницы. CSS отвечает за оформление: цвета, шрифты, размеры, отступы, расположение элементов. Он говорит браузеру, как должен выглядеть контент.Пример:cssh1 {

color: blue; /* Заголовок будет синим */

font-size: 24px; /* Размер шрифта 24 пикселя */

}

p {

line-height: 1.5; /* Межстрочный интервал 1.5 */

}

JavaScript (JS): Это “мозг” или “двигатель” веб-страницы. JavaScript добавляет интерактивность и динамику. Он позволяет реагировать на действия пользователя, изменять контент “на лету”, загружать новые данные без перезагрузки страницы, создавать анимации и многое другое. Он говорит браузеру, что делать.

Пример:

javascript// При нажатии на кнопку, изменить заголовок

document.querySelector('button').addEventListener('click', function() {

document.querySelector('h1').textContent = 'Кнопка нажата!';

});

1.3. Роль JavaScript: Оживление веб-страниц

Без JavaScript веб-сайты были бы статичными. Вы могли бы читать текст, смотреть картинки, переходить по ссылкам. Но вы не могли бы:

Заполнять формы и отправлять данные.

Просматривать галереи изображений с переключением.

Видеть уведомления или всплывающие окна.

Играть в игры прямо в браузере.

Следить за обновлениями ленты новостей в реальном времени.

JavaScript делает веб интерактивным и динамичным.

1.4. Где еще используется JavaScript?

Хотя JavaScript изначально создавался для браузеров, его возможности расширились:

Node.js: Позволяет запускать JavaScript на сервере. Это значит, что вы можете использовать один язык для всей веб-разработки (full-stack development).

Мобильная разработка: С помощью фреймворков вроде React Native можно создавать нативные мобильные приложения для iOS и Android, используя JavaScript.

Десктопные приложения: С помощью Electron можно создавать приложения для Windows, macOS и Linux.

Это показывает, насколько мощным и универсальным инструментом стал JavaScript.

## Глава 2: Ваш Первый Код: Инструменты и Запуск.

Настало время сделать первые шаги и написать свой собственный JavaScript-код. Для этого нам понадобятся два основных инструмента: браузер и его инструменты разработчика.

2.1. Что нам понадобится: Браузеры и их инструменты

Любой современный веб-браузер (Google Chrome, Mozilla Firefox, Microsoft Edge, Safari) обладает встроенными инструментами для разработчиков. Они позволяют нам:

Просматривать HTML-структуру страницы.

Редактировать CSS-стили “на лету”.

Выполнять JavaScript-код.

Отлаживать ошибки.

Анализировать сетевую активность.

Как открыть инструменты разработчика:

Google Chrome / Mozilla Firefox / Microsoft Edge: Нажмите F12 на клавиатуре, или щелкните правой кнопкой мыши в любом месте страницы и выберите “Просмотреть код” (Inspect) или “Исследовать элемент”.

Safari: Сначала нужно включить меню “Разработка” в настройках (Safari -> Настройки -> Дополнения -> Поставить галочку “Показывать меню Разработка в строке меню”). Затем, когда меню “Разработка” появится, выберите “Показать веб-инспектор”.

Когда вы откроете инструменты разработчика, вы увидите несколько вкладок. Нам особенно важна вкладка “Console” (Консоль) – это место, где мы будем писать и выполнять наш JavaScript-код.

2.2. Написание и выполнение JavaScript: В браузере и в файле

Есть два основных способа запустить JavaScript:

А) В Консоли Браузера (для быстрых тестов и экспериментов)

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

Пример 1: Ваша первая команда

Откройте любой веб-сайт (можно даже пустую вкладку, если хотите).

Откройте инструменты разработчика (F12).

Перейдите на вкладку “Console”.

Введите следующую строку и нажмите Enter:javascriptconsole.log("Привет, мир!");

Вы должны увидеть текст Привет, мир! прямо под вашей командой в консоли.

Что здесь произошло?

console – это специальный объект, который предоставляет доступ к консоли браузера.

.log() – это метод (функция) объекта console, который выводит переданные ему данные в консоль.

"Привет, мир!" – это строка. Мы передали ее как аргумент методу log(). Строки в JavaScript заключаются в одинарные (') или двойные (") кавычки.

Пример 2: Простые вычисления

Вы можете использовать консоль как калькулятор:

javascript

2 + 2

Нажмите Enter. Результат: 4

javascript

10 * 5

Нажмите Enter. Результат: 50

javascript

25 / 5

Нажмите Enter. Результат: 5

Б) В HTML-файле (для создания реальных веб-страниц)

Чтобы JavaScript стал частью вашей веб-страницы, его нужно встроить в HTML-документ. Это делается с помощью тега <script>.

Пример 3: JavaScript внутри HTML

Откройте ваш любимый текстовый редактор (например, VS Code, Sublime Text, Notepad++ или даже Блокнот).

Создайте новый файл и сохраните его с расширением .html (например, index.html).

Скопируйте и вставьте следующий код в этот файл:html<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Моя первая JS страница</title>

</head>

<body>

Приветствую!

Это моя первая веб-страница с JavaScript.

<script>

// Этот код будет выполнен браузером

console.log("JavaScript загружен и работает!");

// Найдем заголовок и изменим его текст

let heading = document.querySelector('h1'); // Находим элемент h1

heading.textContent = "JavaScript изменил заголовок!"; // Меняем его текст

</script>

</body>

</html>

Сохраните файл.

Откройте этот файл в браузере (дважды щелкните по нему).

Что вы увидите:

На странице появится текст “JavaScript изменил заголовок!”.

Если вы откроете инструменты разработчика (F12) и перейдете на вкладку “Console”, вы увидите сообщение JavaScript загружен и работает!.

Где размещать тег <script>:

Внутри <head>: Если скрипт размещен в <head>, он будет загружен и выполнен до того, как браузер начнет обрабатывать содержимое <body>. Это может вызвать проблему, если ваш скрипт пытается работать с элементами, которые еще не существуют на странице (например, найти <h1> до того, как он был создан).

Перед закрывающим тегом </body>: Это рекомендуемый способ для большинства случаев. Скрипт будет выполнен после того, как весь HTML-контент страницы будет загружен и готов. Это гарантирует, что элементы, с которыми ваш скрипт должен работать (например, <h1>), уже существуют.

Использование внешних файлов JavaScript:

Когда ваш JavaScript-код становится большим, лучше вынести его в отдельный файл. Это улучшает читаемость, позволяет кэшировать скрипт браузером и упрощает его повторное использование.

Пример 4: Внешний файл JavaScript

Создайте в той же папке, где лежит index.html, новый файл и сохраните его как script.js.

В файл script.js скопируйте JavaScript-код из предыдущего примера:javascript// script.js

console.log("JavaScript из внешнего файла загружен!");

let heading = document.querySelector('h1');

heading.textContent = "JavaScript из внешнего файла изменил заголовок!";

Измените ваш index.html следующим образом:html<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Моя первая JS страница</title>

</head>

<body>

Приветствую!

Это моя первая веб-страница с JavaScript.

<!– Подключаем внешний файл скрипта –>

<script src="script.js"></script>

</body>

</html>

Сохраните оба файла.

Откройте index.html в браузере.

Теперь JavaScript-код выполняется из отдельного файла script.js. Тег <script src="script.js"></script> сообщает браузеру, где найти код.

2.3. Консоль разработчика: Ваш основной инструмент

Консоль – это не просто место для вывода сообщений. Это мощный инструмент для отладки и исследования.

Вывод информации: console.log() – самый частый помощник. Вы можете выводить значения переменных, результаты вычислений, сообщения о ходе выполнения программы.javascriptlet count = 10;

console.log("Текущее значение count:", count); // Выведет: Текущее значение count: 10

Вывод ошибок: console.error() – для сообщений об ошибках.javascriptconsole.error("Что-то пошло не так!");

Предупреждения: console.warn() – для предупреждений.javascriptconsole.warn("Это предупреждение, обратите внимание!");

Таблицы: console.table() – очень удобно для отображения массивов объектов в виде таблицы.javascriptlet users = [

{ id: 1, name: "Алиса" },

{ id: 2, name: "Боб" }

];

console.table(users);

Группировка сообщений: console.group() и console.groupEnd().javascriptconsole.group("Логи операции");

console.log("Шаг 1 выполнен.");

console.log("Шаг 2 выполнен.");

console.groupEnd();

Измерение времени: console.time() и console.timeEnd().javascriptconsole.time("Время выполнения задачи");

// … какой-то код, который занимает время …

for (let i = 0; i < 1000000; i++) {

// Делаем что-то

}

console.timeEnd("Время выполнения задачи"); // Покажет, сколько времени занял цикл

2.4. Комментарии: Как делать код понятным

Комментарии – это текст в вашем коде, который игнорируется браузером. Они нужны для того, чтобы объяснить самому себе (или другим разработчикам), что делает тот или иной участок кода.

Однострочные комментарии: Начинаются с двух слешей (//). Все, что идет после // до конца строки, считается комментарием.javascript// Это однострочный комментарий. Он объясняет следующую строку.

let x = 10; // Можно комментировать и строки кода.

Многострочные комментарии: Начинаются с /* и заканчиваются */. Полезны для больших пояснений.javascript/*

Этот комментарий

занимает несколько строк

и объясняет более сложный участок кода.

*/

let y = 20;

Зачем нужны комментарии:

Объяснение логики: Помогает понять сложные алгоритмы.

Пометка “TODO”: Оставить напоминание себе о том, что нужно доделать.javascript// TODO: Добавить обработку ошибок

Временное отключение кода: Можно закомментировать строки, чтобы они не выполнялись.javascript// console.log("Эта строка сейчас не будет выполнена.");

Практическое задание:

Создайте HTML-файл с тремя разными сообщениями в консоли, используя console.log, console.warn, console.error.

Создайте массив из 3-4 объектов (например, информация о пользователях: id, name, age) и выведите его в консоли в виде таблицы с помощью console.table().

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

## Глава 3: Основы Языка: Переменные, Типы и Операторы.

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

3.1. Объявление переменных: var, let, const – когда что использовать

Переменная – это именованная область в памяти, где мы можем хранить информацию (данные). Представьте ее как контейнер с ярлычком (именем).

Исторически в JavaScript использовалось ключевое слово var. Однако с появлением современных стандартов ECMAScript 2015 (ES6) появились let и const, которые предлагают более предсказуемое и безопасное поведение.

let: Объявляет переменную, которая может быть изменена. Это наш основной инструмент для переменных, чьи значения будут меняться в процессе выполнения программы.javascriptlet message = "Привет"; // Объявили переменную message и присвоили ей значение "Привет"

console.log(message); // Выведет: Привет

message = "Привет, мир!"; // Теперь мы можем изменить значение переменной

console.log(message); // Выведет: Привет, мир!

Область видимости let: Переменные, объявленные с помощью let, имеют блочную область видимости. Это означает, что они существуют только внутри того блока кода (например, внутри фигурных скобок {} для циклов или условий), где были объявлены.javascriptif (true) {

let blockVar = "Я внутри блока";

console.log(blockVar); // Работает

}

// console.log(blockVar); // Ошибка! blockVar не существует вне блока if

const: Объявляет переменную, значение которой не может быть изменено после первого присваивания. Используйте const для значений, которые должны оставаться постоянными. Это помогает избежать случайных изменений и делает код более читаемым.javascriptconst PI = 3.14159;

console.log(PI); // Выведет: 3.14159

// PI = 3.14; // Ошибка! Нельзя переприсвоить значение константе.

Важно: const означает, что переменной нельзя присвоить новое значение. Если переменная хранит объект или массив, сами объекты/массивы можно изменять (например, добавлять элементы в массив), но нельзя присвоить этой переменной другой массив или объект.javascriptconst person = { name: "Алиса" };

person.name = "Боб"; // Можно изменять свойства объекта

console.log(person.name); // Выведет: Боб

// person = { name: "Чарли" }; // Ошибка! Нельзя переприсвоить новый объект.

var: Объявляет переменную, которая имеет функциональную область видимости (а не блочную, как let). В современном JavaScript let и const предпочтительнее из-за более предсказуемого поведения. Тем не менее, вы можете встретить var в старом коде.javascript// Пример с var, чтобы показать разницу

if (true) {

var oldVar = "Я существую везде";

console.log(oldVar); // Работает

}

console.log(oldVar); // Работает! oldVar видна вне блока if.

Из-за такого поведения var может приводить к трудноуловимым ошибкам, поэтому старайтесь использовать let и const.

Правило: Всегда старайтесь использовать const по умолчанию. Если вам нужно изменить значение переменной, тогда используйте let. Избегайте var.

3.2. Типы данных: Строки, числа, булевы значения и другие

JavaScript – это язык с динамической типизацией. Это значит, что тип переменной определяется в момент присваивания значения, и вы можете менять его позже.

Существуют примитивные типы данных (неизменяемые) и объекты (изменяемые).

Примитивные типы:

String (Строка): Последовательность символов. Используется для представления текста.

Примеры: "Привет", 'JavaScript', `Шаблонная строка`

Заметьте, что строки могут заключаться в одинарные ('), двойные (") или обратные (`) кавычки. Обратные кавычки (шаблонные строки) позволяют встраивать выражения с помощью ${выражение}.javascriptlet firstName = "Алексей";

let lastName = 'Петров';

let greeting = `Привет, ${firstName} ${lastName}!`; // Шаблонная строка

console.log(greeting); // Выведет: Привет, Алексей Петров!

Number (Число): Используется для целых и дробных чисел.Примеры: 10, 3.14, -5, 1000

JavaScript также имеет специальные числовые значения:Infinity: Бесконечность (например, 1 / 0).

–Infinity: Отрицательная бесконечность.

NaN (Not a Number): Результат некорректных числовых операций (например, 'abc' / 2).

Boolean (Булево значение): Имеет только два возможных значения: true (истина) или false (ложь). Часто используется в условиях.Примеры: true, false

Undefined (Неопределенное): Переменная, которая была объявлена, но ей не было присвоено никакого значения.Пример: let someVariable; console.log(someVariable); // Выведет: undefined

Null (Пустое значение): Явно указывает на отсутствие какого-либо объекта или значения. В отличие от undefined, null присваивается намеренно.Пример: let emptyValue = null;

Symbol (Символ): Уникальное и неизменяемое примитивное значение, часто используется как ключ свойства объекта, чтобы избежать конфликтов имен. (Более продвинутая тема, для начала можно не углубляться).

BigInt (Большое целое число): Для работы с целыми числами, которые превышают максимальное безопасное целочисленное значение для типа Number.

Объекты (Object):

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

Примеры: { name: "Алиса", age: 30 }, [1, 2, 3] (массивы – это тоже объекты).

Как узнать тип переменной:

Используйте оператор typeof.

javascript

let str = "Текст";

console.log(typeof str); // Выведет: string

let num = 100;

console.log(typeof num); // Выведет: number

let isTrue = true;

console.log(typeof isTrue); // Выведет: boolean

let emptyVar;

console.log(typeof emptyVar); // Выведет: undefined

let nullValue = null;

console.log(typeof nullValue); // Внимание! Выведет: object (это историческая особенность JavaScript)

let person = { name: "Иван" };

console.log(typeof person); // Выведет: object

let arr = [1, 2, 3];

console.log(typeof arr); // Выведет: object (массивы – это объекты)

3.3. Основные операторы: Арифметические, сравнения, логические

Операторы – это символы, которые выполняют операции над значениями (операндами).

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

+ : Сложение (также конкатенация строк)javascriptlet sum = 10 + 5; // 15

let greeting = "Привет" + " " + "мир!"; // "Привет мир!"

– : Вычитаниеjavascriptlet difference = 10 – 5; // 5

* : Умножениеjavascriptlet product = 10 * 5; // 50

/ : Делениеjavascriptlet quotient = 10 / 5; // 2

% : Остаток от деления (оператор модуля)javascriptlet remainder = 10 % 3; // 1 (10 = 3*3 + 1)

let evenOrOdd = 7 % 2; // 1 (нечетное), 6 % 2 // 0 (четное)

++ : Инкремент (увеличение на 1)javascriptlet counter = 5;

counter++; // counter теперь 6

console.log(counter); // 6

Важно: ++ и – могут использоваться как перед переменной (префиксная форма), так и после (постфиксная). Их поведение немного отличается, когда они используются внутри выражения (об этом позже).– : Декремент (уменьшение на 1)javascriptlet counter = 5;

counter–; // counter теперь 4

console.log(counter); // 4

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

= : Простое присваивание (мы уже видели его)javascriptlet a = 10;

+=, -=, *=, /=, %= : Сокращенные операторы.javascriptlet x = 10;

x += 5; // То же, что x = x + 5; x теперь 15

x -= 2; // То же, что x = x – 2; x теперь 13

x *= 3; // То же, что x = x * 3; x теперь 39

Операторы сравнения: Возвращают true или false.

== : Равенство (с учетом преобразования типов)javascriptconsole.log(5 == "5"); // true (строка "5" преобразуется в число 5)

console.log(0 == false); // true (false преобразуется в 0)

=== : Строгое равенство (без преобразования типов) – предпочтительный вариант!javascriptconsole.log(5 === "5"); // false (тип Number не равен типу String)

console.log(0 === false); // false

console.log(5 === 5); // true

!= : Неравенство (с учетом преобразования типов)javascriptconsole.log(5 != "5"); // false

!== : Строгое неравенство (без преобразования типов) – предпочтительный вариант!javascriptconsole.log(5 !== "5"); // true

> : Больше

< : Меньше

>= : Больше или равно

<= : Меньше или равно

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

&& (Логическое И): Возвращает true, если оба операнда true.javascriptlet isLogged = true;

let isAdmin = true;

console.log(isLogged && isAdmin); // true

let isUser = true;

let isGuest = false;

console.log(isUser && isGuest); // false

|| (Логическое ИЛИ): Возвращает true, если хотя бы один из операндов true.javascriptlet hasPermission = true;

let hasAccess = false;

console.log(hasPermission || hasAccess); // true

let isWeekend = false;

let isHoliday = false;

console.log(isWeekend || isHoliday); // false

! (Логическое НЕ): Инвертирует булево значение.javascriptlet isOnline = false;

console.log(!isOnline); // true

let isEmpty = true;

console.log(!isEmpty); // false

3.4. Преобразование типов: Явное и неявное

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

Неявное преобразование (когда JavaScript делает сам):

При использовании + со строкой: Любое число или булево значение будет преобразовано в строку.javascriptconsole.log("Значение: " + 5); // "Значение: 5"

console.log("Статус: " + true); // "Статус: true"

При сравнении с ==: Один операнд может быть преобразован к типу другого.javascriptconsole.log(5 == "5"); // true (строка "5" стала числом 5)

console.log(true == 1); // true (true стало числом 1)

console.log(null == undefined); // true (особый случай)

Явное преобразование (когда мы говорим JavaScript, что делать):

В строку:

String(value): Преобразует любое значение в строку.javascriptconsole.log(String(123)); // "123"

console.log(String(true)); // "true"

console.log(String(null)); // "null"

console.log(String(undefined)); // "undefined"

value.toString(): Метод объектов (включая числа, но не null и undefined).javascriptconsole.log((123).toString()); // "123"

console.log(true.toString()); // "true"

// console.log(null.toString()); // Ошибка!

В число:

Number(value): Преобразует любое значение в число.javascriptconsole.log(Number("123")); // 123

console.log(Number("Привет")); // NaN (не число)

console.log(Number(true)); // 1

console.log(Number(false)); // 0

console.log(Number(null)); // 0

console.log(Number(undefined)); // NaN

parseInt(string, radix): Парсит строку и возвращает целое число. radix – основание системы счисления (обычно 10).javascriptconsole.log(parseInt("123px", 10)); // 123 (игнорирует "px")

console.log(parseInt("0xFF", 16)); // 255 (шестнадцатеричная система)

console.log(parseInt("abc", 10)); // NaN

parseFloat(string): Парсит строку и возвращает число с плавающей точкой.javascriptconsole.log(parseFloat("3.14м")); // 3.14

Унарный плюс (+): Быстрый способ преобразовать в число.javascriptconsole.log(+"123"); // 123

console.log(+"Привет"); // NaN

console.log(+true); // 1

В булево значение:Boolean(value): Преобразует любое значение в true или false.

“Falsy” значения (преобразуются в false):false

0 (ноль)

"" (пустая строка)

null

undefined

NaN

Все остальные значения преобразуются в true (их называют “Truthy” значениями).

javascriptconsole.log(Boolean("Привет")); // true

console.log(Boolean("")); // false

console.log(Boolean(100)); // true

console.log(Boolean(0)); // false

console.log(Boolean(null)); // false

console.log(Boolean(undefined)); // false

console.log(Boolean({})); // true (пустой объект – Truthy!)

console.log(Boolean([])); // true (пустой массив – Truthy!)


Практические задания:

Задание 1: Система учета товаров

javascript

// 1. Создайте константы для базовой информации о магазине:

// – Название магазина (строка)

// – Курс валюты (число)

// – Признак активности (булево значение)

// 2. Создайте переменные для:

// – Количества товара на складе

// – Цены товара

// – Названия товара

// 3. Проверьте типы всех созданных переменных

// 4. Попробуйте изменить константу с названием магазина

// и обработайте ошибку с помощью try/catch

Задание 2: Калькулятор скидок

javascript

// 1. Создайте переменные для:

// – Исходной цены товара

// – Процента скидки

// – Порога для дополнительной скидки

// 2. Рассчитайте:

// – Цену со скидкой

// – Разницу между исходной и конечной ценой

// – Итоговый процент скидки с точностью до 2 знаков

// 3. Используйте операторы сравнения для проверки:

// – Превышает ли скидка пороговое значение

// – Является ли цена положительным числом

Задание 3: Валидация данных формы

javascript

// Напишите функцию validateForm(data), которая принимает объект с полями:

// – name (строка)

// – age (число)

// – email (строка)

// – subscription (булево значение)

// Функция должна проверять типы данных и возвращать объект с результатами:

// – isValid (булево значение)

// – errors (массив строк с описанием ошибок)


Ответы и решения

Решение задания 1:

javascript

// 1. Создаем константы

const STORE_NAME = "TechMarket";

const CURRENCY_RATE = 97.5;

const IS_ACTIVE = true;

// 2. Создаем переменные

let stockQuantity = 150;

let itemPrice = 2990;

let itemName = "Смартфон";

// 3. Проверяем типы

console.log(typeof STORE_NAME); // "string"

console.log(typeof CURRENCY_RATE); // "number"

console.log(typeof IS_ACTIVE); // "boolean"

console.log(typeof stockQuantity); // "number"

// 4. Пытаемся изменить константу

try {

STORE_NAME = "NewMarket"; // Вызовет ошибку

} catch (error) {

console.log("Ошибка:", error.message);

// "Assignment to constant variable"

}

Решение задания 2:

javascript

// 1. Исходные данные

let originalPrice = 5000;

let discountPercent = 15;

let threshold = 10;

// 2. Расчеты

let discountAmount = originalPrice * (discountPercent / 100);

let finalPrice = originalPrice – discountAmount;

let priceDifference = originalPrice – finalPrice;

let actualDiscountPercent = (priceDifference / originalPrice) * 100;

// Округляем до 2 знаков

actualDiscountPercent = Number(actualDiscountPercent.toFixed(2));

// 3. Проверки

let isAboveThreshold = discountPercent > threshold;

let isPriceValid = originalPrice > 0;

console.log("Цена со скидкой:", finalPrice);

console.log("Реальная скидка:", actualDiscountPercent + "%");

console.log("Скидка выше порога:", isAboveThreshold);

console.log("Цена валидна:", isPriceValid);

Решение задания 3:

javascript

function validateForm(data) {

let errors = [];

// Проверка имени

if (typeof data.name !== 'string' || data.name.trim().length === 0) {

errors.push("Имя должно быть непустой строкой");

}


// Проверка возраста

if (typeof data.age !== 'number' || data.age < 0 || data.age > 150) {

errors.push("Возраст должен быть числом от 0 до 150");

}


// Проверка email

if (typeof data.email !== 'string' || !data.email.includes('@')) {

errors.push("Email должен быть строкой с символом @");

}


// Проверка подписки

if (typeof data.subscription !== 'boolean') {

errors.push("Подписка должна быть булевым значением");

}

return {

isValid: errors.length === 0,

errors: errors

};

}

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

const userData = {

name: "Иван Иванов",

age: 25,

email: "ivan@example.com",

subscription: true

};

console.log(validateForm(userData));

// { isValid: true, errors: [] }


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

## Глава 4: Логика Программы: Условия и Циклы.

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

4.1. Принятие решений: if, else if, else

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

Термин: Условие (Condition) – это выражение, которое может быть оценено как true (истина) или false (ложь). Чаще всего в качестве условий используются операторы сравнения (>, <, ===, !== и т.д.) или булевы переменные.

Конструкция if:

Самая простая форма условного оператора – if. Он выполняет блок кода, только если условие истинно.

javascript

// Пример: Проверка возраста для доступа

let age = 18;

if (age >= 18) {

console.log("Доступ разрешен.");

}

// Если условие (age >= 18) истинно, выполнится console.log.

// Если условие ложно, код внутри фигурных скобок {} будет проигнорирован.

Фигурные скобки {}: Они определяют блок кода, который будет выполнен, если условие истинно. Если внутри блока только одна строка кода, фигурные скобки можно опустить, но для читаемости и безопасности (чтобы случайно не пропустить строку) их лучше всегда использовать.

Конструкция if…else:

Что, если нам нужно выполнить один код, если условие истинно, и другой код, если оно ложно? Здесь на помощь приходит else.

javascript

let temperature = 15;

if (temperature > 25) {

console.log("Жарко! Идем купаться.");

} else {

console.log("Прохладно. Лучше прогуляться.");

}

// Выполнится "Прохладно. Лучше прогуляться."

Конструкция if…else if…else:

А что, если у нас несколько возможных условий? Например, классификация оценок: отличная, хорошая, удовлетворительная, неудовлетворительная. Для таких случаев используется цепочка else if.

javascript

let score = 75; // Оценка студента

if (score >= 90) {

console.log("Отлично!");

} else if (score >= 75) { // Если первое условие ложно, проверяем это

console.log("Хорошо.");

} else if (score >= 50) { // Если и предыдущее ложно, проверяем это

console.log("Удовлетворительно.");

} else { // Если ни одно из вышеперечисленных условий не истинно

console.log("Неудовлетворительно. Нужно подтянуться.");

}

// Выполнится "Хорошо."

Важные моменты:

Условия оцениваются строго по порядку, сверху вниз. Как только находится первое истинное условие, выполняется соответствующий блок кода, и вся цепочка if…else if…else завершается.

Блок else (если он есть) является необязательным и срабатывает только тогда, когда все предыдущие условия ложны.

Пример 4.1.1: Определение времени суток

javascript

let hour = 14; // Текущий час (от 0 до 23)

if (hour >= 6 && hour < 12) {

console.log("Доброе утро!");

} else if (hour >= 12 && hour < 18) {

console.log("Добрый день!");

} else if (hour >= 18 && hour < 22) {

console.log("Добрый вечер!");

} else {

console.log("Доброй ночи!");

}

// Выполнится "Добрый день!"

// Обратите внимание, что здесь мы использовали оператор && (логическое И)

// для объединения двух условий (начало интервала И конец интервала).

4.2. Тернарный оператор (Короткий if…else)

Когда нам нужно присвоить переменной одно значение, если условие истинно, и другое, если оно ложно, тернарный оператор (также известный как условный оператор) становится удобной альтернативой if…else.

Синтаксис: условие ? значение_если_истинно : значение_если_ложно

javascript

let age = 20;

let canVote = (age >= 18) ? "Может голосовать" : "Не может голосовать";

console.log(canVote); // Выведет: Может голосовать

let weather = "солнечно";

let message = (weather === "солнечно") ? "Отличная погода!" : "Возьмите зонт.";

console.log(message); // Выведет: Отличная погода!

Тернарный оператор отлично подходит для простых присваиваний, но для более сложной логики лучше использовать if…else.

4.3. Оператор switch (Для множественных однотипных сравнений)

Когда у вас есть переменная, и вы хотите выполнить разные действия в зависимости от ее конкретного значения (а не диапазона, как в if…else if), оператор switch может быть более читаемым и эффективным.

Термин: switch (переключатель) – конструкция, которая сравнивает значение выражения с несколькими возможными значениями (case) и выполняет соответствующий блок кода.

Синтаксис:

javascript

switch (выражение) {

case значение1:

// Код, если выражение === значение1

break; // Важно! Выход из switch

case значение2:

// Код, если выражение === значение2

break;

// … другие case …

default: // Необязательный блок

// Код, если ни один case не совпал

}

case: Обозначает конкретное значение, с которым будет сравниваться выражение.

break: Очень важен! Он выходит из конструкции switch. Если break отсутствует, выполнение кода продолжится до следующего case или до конца switch (это называется “сваливание” – fall-through).

default: Необязательный блок, который выполняется, если ни одно из значений case не совпало с выражением. Похож на else в if…else if.

Пример 4.3.1: Определение дня недели

javascript

let dayNumber = 3; // 0 – воскресенье, 1 – понедельник, …, 6 – суббота

let dayName;

switch (dayNumber) {

case 0:

dayName = "Воскресенье";

break;

case 1:

dayName = "Понедельник";

break;

case 2:

dayName = "Вторник";

break;

case 3:

dayName = "Среда";

break;

case 4:

dayName = "Четверг";

break;

case 5:

dayName = "Пятница";

break;

case 6:

dayName = "Суббота";

break;

default:

dayName = "Неизвестный день";

}

console.log(dayName); // Выведет: Среда

Пример 4.3.2: switch с отсутствием break (осторожно!)

javascript

let fruit = "яблоко";

let description;

switch (fruit) {

case "яблоко":

description = "Это фрукт.";

// Нет break! Код "свалится" в следующий case.

case "груша":

description += " Оно красное или зеленое."; // Добавится к предыдущему описанию

break; // Здесь break есть.

case "банан":

description = "Это желтый фрукт.";

break;

default:

description = "Неизвестный фрукт.";

}

console.log(description); // Выведет: Это фрукт. Оно красное или зеленое.

// Обратите внимание, как `case "груша"` добавил свою часть к `case "яблоко"`.

Итого: switch хорош для множественных проверок на равенство одного и того же значения. Для диапазонов или более сложных условий лучше использовать if…else if.

4.4. Циклы: for, while, do…while (Для повторения действий)

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

Термин: Цикл (Loop) – конструкция, которая позволяет повторно выполнять блок кода.

Термин: Итерация (Iteration) – однократное выполнение блока кода внутри цикла.

А) Цикл for (Идеален, когда мы знаем, сколько раз нужно повторить)

Цикл for имеет три части, разделенные точкой с запятой:

Инициализация: Выполняется один раз перед началом цикла. Обычно здесь объявляется счетчик.

Условие: Проверяется перед каждой итерацией. Если условие истинно, цикл продолжается. Если ложно, цикл завершается.

Инкремент/Декремент (или шаг): Выполняется после каждой итерации. Обычно увеличивает или уменьшает счетчик.

Синтаксис: for (инициализация; условие; шаг) { // блок кода }

Пример 4.4.1: Вывод чисел от 1 до 5

javascript

for (let i = 1; i <= 5; i++) {

console.log(i);

}

// Вывод:

// 1

// 2

// 3

// 4

// 5

Разбор:

let i = 1;: Перед началом цикла объявлена переменная i и ей присвоено значение 1 (инициализация).

i <= 5;: Перед каждой итерацией проверяется: i меньше или равно 5?i = 1: 1 <= 5 (истина) -> выполняем блок кода, затем i++.

i = 2: 2 <= 5 (истина) -> выполняем блок кода, затем i++.

…

i = 5: 5 <= 5 (истина) -> выполняем блок кода, затем i++.

i = 6: 6 <= 5 (ложь) -> цикл завершается.

i++: После каждой итерации значение i увеличивается на 1.

Пример 4.4.2: Вывод таблицы умножения на 7

javascript

console.log("Таблица умножения на 7:");

for (let multiplier = 1; multiplier <= 10; multiplier++) {

let result = 7 * multiplier;

console.log(`7 * ${multiplier} = ${result}`);

}

// Вывод:

// Таблица умножения на 7:

// 7 * 1 = 7

// 7 * 2 = 14

// …

// 7 * 10 = 70

Б) Цикл while (Идеален, когда мы не знаем точное количество повторений, но знаем условие продолжения)

Цикл while выполняет блок кода, пока условие истинно. Условие проверяется перед каждой итерацией.

Синтаксис: while (условие) { // блок кода }

Пример 4.4.3: Снижение баланса до нуля

javascript

let balance = 100;

let transactionAmount = 10;

let transactionsCount = 0;

while (balance > 0) {

balance = balance – transactionAmount; // Уменьшаем баланс

transactionsCount++; // Увеличиваем счетчик транзакций

console.log(`Баланс после транзакции ${transactionsCount}: ${balance}`);

}

console.log(`Всего транзакций: ${transactionsCount}`);

// Вывод:

// Баланс после транзакции 1: 90

// Баланс после транзакции 2: 80

// …

// Баланс после транзакции 10: 0

// Всего транзакций: 10

Важно: В цикле while вы должны самостоятельно позаботиться о том, чтобы условие в конечном итоге стало ложным. Если условие всегда будет истинным, вы получите бесконечный цикл, который “заморозит” вашу программу.

В) Цикл do…while (Похож на while, но выполняется хотя бы один раз)

Цикл do…while выполняет блок кода один раз, а затем проверяет условие. Если условие истинно, цикл повторяется.

Синтаксис: do { // блок кода } while (условие);

Пример 4.4.4: Гарантированное выполнение хотя бы раз

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

javascript

let userInput;

do {

userInput = prompt("Введите положительное число:"); // prompt() – функция, которая запрашивает ввод у пользователя

console.log("Вы ввели: " + userInput);

} while (userInput < 0); // Условие проверяется ПОСЛЕ первого выполнения

console.log("Спасибо! Вы ввели корректное число.");

(Примечание: prompt() – это функция браузера, которая может отсутствовать в некоторых средах выполнения JS. Для полноты картины мы ее здесь упомянули.)

Когда использовать какой цикл:

for: Когда вы заранее знаете или можете вычислить количество повторений (например, пройти по всем элементам массива, сделать 10 итераций).

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

do…while: Когда блок кода должен выполниться как минимум один раз, даже если условие изначально ложно.

4.5. Специальные циклы for…in и for…of

Эти циклы предназначены для итерации (перебора) по элементам объектов и массивов.

А) Цикл for…in (для перебора свойств объектов)

Он перебирает имена (ключи) свойств объекта.

Синтаксис: for (let key in object) { // код с использованием object[key] }

javascript

let car = {

brand: "Toyota",

model: "Camry",

year: 2022

};

for (let key in car) {

console.log(`Ключ: ${key}, Значение: ${car[key]}`);

}

// Вывод:

// Ключ: brand, Значение: Toyota

// Ключ: model, Значение: Camry

// Ключ: year, Значение: 2022

Важно: for…in может перебирать свойства, унаследованные из прототипа объекта. Для простого перебора собственных свойств обычно используют Object.keys(object) в сочетании с обычным циклом for или forEach.

Б) Цикл for…of (для перебора значений итерируемых объектов)

Этот цикл предназначен для перебора значений элементов в итерируемых объектах, таких как массивы, строки, Map, Set.

Синтаксис: for (let value of iterable) { // код с использованием value }

Пример 4.5.1: Перебор массива

javascript

let colors = ["красный", "зеленый", "синий"];

for (let color of colors) {

console.log(color);

}

// Вывод:

// красный

// зеленый

// синий

Пример 4.5.2: Перебор строки

javascript

let text = "JS";

for (let char of text) {

console.log(char);

}

// Вывод:

// J

// S

Когда что использовать:

for…of – для перебора значений элементов массивов, строк и других итерируемых коллекций.

for…in – для перебора ключей (имен свойств) объектов.

Практические задания:


Задание 1: Система аутентификации пользователей

javascript

// 1. Напишите функцию checkUserAccess(user), которая принимает объект пользователя

// с полями: role (строка), isActive (булево), subscriptionType (строка)

// 2. Реализуйте проверки:

// – Если пользователь не активен, вернуть "Доступ заблокирован"

// – Если роль "admin", вернуть "Полный доступ"

// – Если роль "user" и подписка "premium", вернуть "Расширенный доступ"

// – Если роль "user" и подписка "basic", вернуть "Базовый доступ"

// – Для всех остальных случаев – "Неизвестная роль"

// 3. Используйте конструкцию if…else if…else

Задание 2: Умный калькулятор скидок

javascript

// 1. Создайте функцию calculateDiscount(price, userType, purchaseCount)

// 2. Реализуйте логику расчета скидки с помощью switch и тернарных операторов:

// Базовая скидка:

// – "vip" – 20%

// – "regular" – 10%

// – "new" – 5%

// Дополнительная скидка за количество покупок:

// – Если purchaseCount > 10 – дополнительно 5%

// – Если purchaseCount > 5 – дополнительно 3%

// 3. Верните итоговую цену и размер скидки в процентах

Задание 3: Анализатор данных массива

javascript

// 1. Создайте функцию analyzeArray(numbers), которая принимает массив чисел

// 2. Используя циклы, найдите:

// – Максимальное и минимальное значение

// – Среднее арифметическое

// – Количество четных и нечетных чисел

// – Все простые числа в массиве

// 3. Верните объект с результатами анализа

Задание 4: Генератор отчетов

javascript

// 1. Дан массив объектов с товарами:

const products = [

{ name: "Телефон", price: 500, category: "electronics", stock: 15 },

{ name: "Книга", price: 20, category: "education", stock: 0 },

{ name: "Наушники", price: 100, category: "electronics", stock: 8 }

];

// 2. Используя циклы и условия, создайте отчеты:

// – Список товаров, которых нет в наличии

// – Общую стоимость всех товаров в категории "electronics"

// – Товары с ценой выше 50 и остатком менее 10

Ответы и решения

Решение задания 1:

javascript

function checkUserAccess(user) {

if (!user.isActive) {

return "Доступ заблокирован";

} else if (user.role === "admin") {

return "Полный доступ";

} else if (user.role === "user") {

return user.subscriptionType === "premium"

? "Расширенный доступ"

: "Базовый доступ";

} else {

return "Неизвестная роль";

}

}

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

console.log(checkUserAccess({

role: "admin",

isActive: true,

subscriptionType: "premium"

})); // "Полный доступ"

console.log(checkUserAccess({

role: "user",

isActive: true,

subscriptionType: "basic"

})); // "Базовый доступ"


Решение задания 2:

javascript

function calculateDiscount(price, userType, purchaseCount) {

let baseDiscount = 0;


// Базовая скидка по типу пользователя

switch (userType) {

case "vip":

baseDiscount = 20;

break;

case "regular":

baseDiscount = 10;

break;

case "new":

baseDiscount = 5;

break;

default:

baseDiscount = 0;

}


// Дополнительная скидка за количество покупок

let extraDiscount = purchaseCount > 10 ? 5 : purchaseCount > 5 ? 3 : 0;


const totalDiscount = baseDiscount + extraDiscount;

const finalPrice = price * (1 – totalDiscount / 100);


return {

originalPrice: price,

finalPrice: finalPrice,

totalDiscount: totalDiscount,

baseDiscount: baseDiscount,

extraDiscount: extraDiscount

};

}

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

console.log(calculateDiscount(1000, "vip", 12));

// { originalPrice: 1000, finalPrice: 750, totalDiscount: 25, … }

Решение задания 3:

javascript

function analyzeArray(numbers) {

if (numbers.length === 0) {

return { error: "Массив пуст" };

}


let max = numbers[0];

let min = numbers[0];

let sum = 0;

let evenCount = 0;

let oddCount = 0;

let primes = [];


// Основной цикл анализа

for (let i = 0; i < numbers.length; i++) {

const num = numbers[i];


// Поиск максимума и минимума

if (num > max) max = num;

if (num < min) min = num;


// Сумма для среднего значения

sum += num;


// Четные/нечетные

if (num % 2 === 0) {

evenCount++;

} else {

oddCount++;

}


// Проверка на простое число

if (isPrime(num)) {

primes.push(num);

}

}

return {

max: max,

min: min,

average: sum / numbers.length,

evenCount: evenCount,

oddCount: oddCount,

primes: primes,

totalCount: numbers.length

};

}

// Вспомогательная функция для проверки простых чисел

function isPrime(num) {

if (num < 2) return false;

for (let i = 2; i <= Math.sqrt(num); i++) {

if (num % i === 0) return false;

}

return true;

}

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

console.log(analyzeArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));

Решение задания 4:

javascript

const products = [

{ name: "Телефон", price: 500, category: "electronics", stock: 15 },

{ name: "Книга", price: 20, category: "education", stock: 0 },

{ name: "Наушники", price: 100, category: "electronics", stock: 8 },

{ name: "Ручка", price: 5, category: "education", stock: 50 }

];

// 1. Товары не в наличии

const outOfStock = [];

for (let i = 0; i < products.length; i++) {

if (products[i].stock === 0) {

outOfStock.push(products[i].name);

}

}

// 2. Общая стоимость электроники

let electronicsTotal = 0;

for (let product of products) {

if (product.category === "electronics") {

electronicsTotal += product.price * product.stock;

}

}

// 3. Товары с ценой > 50 и остатком < 10

const criticalProducts = [];

let index = 0;

while (index < products.length) {

const product = products[index];

if (product.price > 50 && product.stock < 10) {

criticalProducts.push({

name: product.name,

price: product.price,

stock: product.stock

});

}

index++;

}

console.log("Нет в наличии:", outOfStock);

console.log("Общая стоимость электроники:", electronicsTotal);

console.log("Критические товары:", criticalProducts);


Чек-лист самопроверки

Я могу создавать сложные условия с помощью if/else и switch

Понимаю разницу между циклами for, while и for…of

Умею использовать тернарный оператор для простых условий

Могу анализировать данные массивов с помощью циклов и условий

Понимаю, когда использовать break и continue в циклах

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

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

## Глава 5: Функции: Ваш Инструментарий.

В предыдущих главах мы много говорили о том, как выполнять код последовательно, принимать решения и повторять действия. Но представьте, что нам нужно выполнить один и тот же набор действий в разных частях программы. Копировать и вставлять код – плохая практика. Это делает код громоздким, трудным для чтения и поддержки, и увеличивает риск ошибок.

Решение этой проблемы – функции.

Термин: Функция (Function) – это именованный блок кода, который выполняет определенную задачу. Вы можете “вызвать” функцию по ее имени, чтобы выполнить этот блок кода. Функции позволяют нам:

Повторно использовать код: Написать код один раз и использовать его многократно.

Разбивать большие задачи на мелкие: Делать программу более модульной и понятной.

Улучшать читаемость кода: Давать осмысленные имена блокам кода.

Упрощать тестирование и отладку: Легче тестировать и находить ошибки в небольших, изолированных функциях.

5.1. Создание функций: Function Declaration vs. Function Expression

Есть несколько способов определить (объявить) функцию в JavaScript. Два самых распространенных – это Function Declaration и Function Expression.

А) Function Declaration (Объявление функции)

Это стандартный способ объявления функции.

Синтаксис: function имяФункции(параметр1, параметр2, …) { // тело функции (код) }

function – ключевое слово, которое указывает, что мы объявляем функцию.

имяФункции – уникальное имя, по которому мы будем вызывать функцию. Следуйте правилам именования переменных (обычно CamelCase, например, calculateSum).

параметр1, параметр2, … – это параметры функции. Они действуют как временные переменные, которые получают значения, передаваемые в функцию при ее вызове. Параметры необязательны; функция может не принимать никаких аргументов.

{ // тело функции } – блок кода, который будет выполнен при вызове функции.

Пример 5.1.1: Функция приветствия

javascript

function greetUser() {

console.log("Приветствую тебя!");

}

// Вызов функции:

greetUser(); // Выведет: Приветствую тебя!

greetUser(); // Можно вызвать несколько раз

Пример 5.1.2: Функция с параметрами

javascript

function greetUserByName(name) { // name – параметр

console.log(`Приветствую, ${name}!`);

}

greetUserByName("Алиса"); // Выведет: Приветствую, Алиса!

greetUserByName("Боб"); // Выведет: Приветствую, Боб!

Когда мы вызываем greetUserByName("Алиса"), строка "Алиса" передается как аргумент в функцию, и внутри функции переменная name получает значение "Алиса".

Поднятие (Hoisting): Одна из особенностей Function Declaration в JavaScript – это “поднятие”. Это означает, что функция, объявленная таким образом, доступна для вызова до того, как она фактически объявлена в коде.

javascript

sayHello(); // Это сработает!

function sayHello() {

console.log("Привет!");

}

JavaScript “поднимает” объявление функции sayHello в начало области видимости. Это удобно, но иногда может сбивать с толку начинающих, поэтому стоит помнить об этом.

Б) Function Expression (Выражение функции)

При таком подходе функция определяется как часть выражения (обычно присваивается переменной).

Синтаксис: let имяПеременной = function(параметр1, …) { // тело функции };

javascript

let greetSomeone = function(name) {

console.log(`Приветствую, ${name}!`);

};

// Вызов функции:

greetSomeone("Чарли"); // Выведет: Приветствую, Чарли!

Основные отличия от Function Declaration:

Поднятие: Функции, определенные как Function Expression, не поднимаются так же, как Function Declaration. Если вы попытаетесь вызвать такую функцию до ее объявления, вы получите ошибку TypeError.javascript// sayGoodbye(); // Ошибка! TypeError: sayGoodbye is not a function

let sayGoodbye = function(name) {

console.log(`До свидания, ${name}!`);

};

sayGoodbye("Дэвид"); // Теперь сработает

Анонимные функции: Function Expression может быть анонимной (без имени), как в примере greetSomeone. Это часто используется при передаче функций в качестве аргументов другим функциям (например, в обработчиках событий или при использовании методов массивов).

5.2. Стрелочные функции (Arrow Functions)

С появлением стандарта ES6 (ECMAScript 2015) были введены стрелочные функции – более краткий и удобный синтаксис для записи функций, особенно для простых случаев.

Синтаксис (основные варианты):

С одним параметром (или без параметров) и одним выражением в теле: Скобки вокруг параметра и фигурные скобки {} опускаются. Результат выражения возвращается автоматически.javascript// Было:

// let greetUserByName = function(name) {

// return `Приветствую, ${name}!`;

// };

// Стало (стрелочная функция):

let greetUserByName = name => `Приветствую, ${name}!`; // скобки вокруг name опущены

console.log(greetUserByName("Ева")); // Выведет: Приветствую, Ева!

// Без параметров:

// let showMessage = function() { return "Сообщение!"; };

let showMessage = () => "Сообщение!"; // Скобки для параметров обязательны, если их нет

console.log(showMessage()); // Выведет: Сообщение!

С несколькими параметрами или несколькими выражениями в теле: Параметры заключаются в скобки, а тело функции – в фигурные скобки {}. В этом случае нужно явно использовать return, если вы хотите вернуть значение.javascript// Было:

// let calculateSum = function(a, b) {

// let sum = a + b;

// return sum;

// };

// Стало (стрелочная функция):

let calculateSum = (a, b) => { // скобки для параметров обязательны

let sum = a + b;

return sum; // return нужен, так как тело функции больше одного выражения

};

console.log(calculateSum(5, 3)); // Выведет: 8

Важные особенности стрелочных функций:

Краткость: Особенно удобны для простых функций.

Отсутствие this: Стрелочные функции не имеют своего собственного контекста this. Они “захватывают” this из окружающей (лексической) области видимости. Это одно из самых важных отличий и делает их очень полезными в определенных ситуациях (например, при работе с методами объектов или внутри обработчиков событий). Мы подробно разберем this позже.

Не подходят для: Методов объектов (если вы хотите, чтобы this ссылался на сам объект), конструкторов (функций, вызываемых с new).

5.3. Параметры и аргументы функций

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

Параметры (Parameters): Имена переменных, перечисленные в определении функции. Они действуют как “приемники” для входящих данных.

Аргументы (Arguments): Фактические значения, которые передаются в функцию при ее вызове.

javascript

function multiply(num1, num2) { // num1 и num2 – ПАРАМЕТРЫ

let result = num1 * num2;

console.log(`Результат умножения ${num1} на ${num2} равен ${result}`);

}

multiply(5, 3); // 5 и 3 – АРГУМЕНТЫ. num1 станет 5, num2 станет 3.

multiply(10, 2); // 10 и 2 – АРГУМЕНТЫ. num1 станет 10, num2 станет 2.

Что происходит, если аргументов меньше или больше, чем параметров?

Недостаточно аргументов: Если вы передали меньше аргументов, чем параметров, недостающие параметры получат значение undefined.javascriptfunction greet(firstName, lastName) {

console.log(`Привет, ${firstName} ${lastName}`);

}

greet("Алиса"); // Выведет: Привет, Алиса undefined

Чтобы избежать этого, можно использовать значения параметров по умолчанию (ES6).javascriptfunction greetWithDefault(firstName, lastName = "Гость") { // lastName по умолчанию "Гость"

console.log(`Привет, ${firstName} ${lastName}`);

}

greetWithDefault("Алиса"); // Привет, Алиса Гость

greetWithDefault("Боб", "Смит"); // Привет, Боб Смит

Слишком много аргументов: Если вы передали больше аргументов, чем параметров, “лишние” аргументы просто игнорируются (если вы не используете специальные техники, такие как arguments или rest-параметры).javascriptfunction addTwoNumbers(a, b) {

console.log(a + b);

}

addTwoNumbers(2, 3, 100); // Выведет: 5 (аргумент 100 проигнорирован)

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

Термин: Rest-параметры (Rest Parameters) … (ES6) – Позволяют представить неизвестное количество аргументов как массив. Это более гибкая альтернатива старому объекту arguments.

javascript

function sumAll(…numbers) { // numbers будет массивом всех переданных аргументов

let total = 0;

for (let num of numbers) {

total += num;

}

return total;

}

console.log(sumAll(1, 2, 3)); // Выведет: 6

console.log(sumAll(10, 20, 30, 40)); // Выведет: 100

console.log(sumAll()); // Выведет: 0

Rest-параметры должны быть последними в списке параметров.

5.4. Возвращаемые значения (return)

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

Когда return выполняется, функция немедленно завершает свою работу, и значение, указанное после return, передается обратно в то место, откуда функция была вызвана.

Если в функции нет return, или return используется без значения, функция неявно возвращает undefined.

Пример 5.4.1: Функция, возвращающая результат

javascript

function calculateArea(width, height) {

if (width <= 0 || height <= 0) {

console.error("Ширина и высота должны быть положительными числами.");

return undefined; // Явно возвращаем undefined в случае ошибки

}

let area = width * height;

return area; // Возвращаем вычисленное значение

}

let rectangle1Area = calculateArea(10, 5); // 50 будет присвоено rectangle1Area

console.log("Площадь 1:", rectangle1Area); // Площадь 1: 50

let rectangle2Area = calculateArea(8, 4);

console.log("Площадь 2:", rectangle2Area); // Площадь 2: 32

let invalidArea = calculateArea(-2, 5); // Выведет ошибку в консоль

console.log("Некорректная площадь:", invalidArea); // Некорректная площадь: undefined

Пример 5.4.2: Функция, не возвращающая значения (неявно возвращает undefined)

javascript

function displayMessage(msg) {

console.log("Сообщение: " + msg);

// Нет return

}

let result = displayMessage("Привет!"); // result будет undefined

console.log("Результат вызова функции:", result); // Результат вызова функции: undefined

5.5. Область видимости (Scope)

Мы уже касались области видимости let и const (блочная) и var (функциональная). Давайте углубимся.

Термин: Область видимости (Scope) – это набор правил, определяющий, где в вашем коде доступны переменные и функции.

JavaScript имеет две основные области видимости:

Глобальная область видимости (Global Scope): Переменные, объявленные вне каких-либо функций или блоков, находятся в глобальной области видимости. Они доступны из любой части вашего кода. В браузере глобальный объект – это window.

Локальная область видимости (Local Scope):Функциональная область видимости: Переменные, объявленные с помощью var внутри функции, доступны только внутри этой функции.

Блочная область видимости: Переменные, объявленные с помощью let и const внутри блока кода (например, {…} внутри if, for, или просто отдельный блок), доступны только внутри этого блока.

Пример 5.5.1: Демонстрация областей видимости

javascript

// Глобальная переменная

let globalVar = "Я глобальный";

function myFunction() {

// Локальная переменная для myFunction

let localVar = "Я локальный для myFunction";

console.log(globalVar); // Доступ к глобальной переменной

console.log(localVar); // Доступ к локальной переменной

if (true) {

// Блочная переменная

let blockVar = "Я внутри блока if";

console.log(blockVar); // Доступ к блочной переменной

console.log(localVar); // Доступ к локальной переменной функции

}

// console.log(blockVar); // Ошибка! blockVar не виден вне блока if

}

myFunction();

console.log(globalVar); // Доступ к глобальной переменной

// console.log(localVar); // Ошибка! localVar не виден вне myFunction

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

Термин: Замыкание (Closure) – более продвинутая концепция, связанная с областями видимости. Это функция, которая “помнит” переменные из той области видимости, в которой она была создана, даже если эта область видимости уже не активна. Мы подробно разберем замыкания позже, но важно понимать, что функции имеют доступ к переменным из внешних областей видимости.

Практическое задание:

Задание 1: Валидатор данных формы

javascript

// 1. Создайте функцию validateEmail(email), которая проверяет:

// – Наличие символа @

// – Длину доменной части не менее 2 символов

// – Возвращает true/false

// 2. Создайте функцию validatePassword(password, minLength = 8), которая:

// – Проверяет длину пароля

// – Проверяет наличие хотя бы одной цифры

// – Возвращает объект { isValid: boolean, errors: [] }

Задание 2: Генератор контента для интерфейса

javascript

// 1. Создайте функцию createUserCard(userData), которая принимает объект с данными:

// { name, email, avatar, isOnline }

// 2. Функция должна ВОЗВРАЩАТЬ HTML-строку для карточки пользователя:

// – Если isOnline = true, добавить класс 'online'

// – Если avatar отсутствует, использовать аватар по умолчанию

// – Использовать значения по умолчанию для отсутствующих полей

Задание 3: Обработчик корзины покупок

javascript

// 1. Создайте функцию addToCart(item, quantity = 1, cart = []), которая:

// – Добавляет товар в корзину

// – Если товар уже есть, увеличивает количество

// – Возвращает НОВЫЙ массив корзины (не изменяя исходный)

// 2. Создайте функцию calculateTotal(cart), которая:

// – Принимает массив товаров { price, quantity }

// – Считает общую стоимость

// – Применяет скидку 5% если сумма > 1000

// – Использует rest parameters и for…of

Задание 4: Утилиты для работы с DOM

javascript

// 1. Создайте функцию createElement(tag, classes = [], text = ''), которая:

// – Создает HTML-элемент указанного тега

// – Добавляет CSS-классы из массива

// – Устанавливает текстовое содержимое

// – Возвращает готовый элемент

// 2. Создайте функцию debounce(callback, delay), которая:

// – "Откладывает" вызов функции callback

// – При повторном вызове сбрасывает таймер

// – Использует замыкание для хранения timeoutId

Ответы и решения

Решение задания 1:

javascript

// Валидатор email

function validateEmail(email) {

if (typeof email !== 'string') return false;

const parts = email.split('@');

return parts.length === 2 && parts[1].length >= 2;

}

// Валидатор пароля

function validatePassword(password, minLength = 8) {

const errors = [];


if (password.length < minLength) {

errors.push(`Пароль должен быть не менее ${minLength} символов`);

}


if (!/\d/.test(password)) {

errors.push('Пароль должен содержать хотя бы одну цифру');

}


return {

isValid: errors.length === 0,

errors: errors

};

}

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

console.log(validateEmail('user@example.com')); // true

console.log(validatePassword('secret123')); // { isValid: true, errors: [] }


Решение задания 2:

javascript

function createUserCard(userData) {

const {

name = 'Аноним',

email = '',

avatar = '/default-avatar.png',

isOnline = false

} = userData;


return `

<div class="user-card ${isOnline ? 'online' : ''}">

<div class="user-info">

${name}

${email ? `${email}
` : ''}

`;

}

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

const user = { name: 'Иван', email: 'ivan@test.ru', isOnline: true };

console.log(createUserCard(user));


Решение задания 3:

javascript

// Добавление в корзину

function addToCart(item, quantity = 1, cart = []) {

const newCart = […cart]; // Создаем копию

const existingItem = newCart.find(cartItem => cartItem.id === item.id);


if (existingItem) {

existingItem.quantity += quantity;

} else {

newCart.push({ …item, quantity });

}


return newCart;

}

// Расчет суммы

function calculateTotal(…items) {

let subtotal = 0;


for (let item of items) {

subtotal += item.price * item.quantity;

}


const discount = subtotal > 1000 ? subtotal * 0.05 : 0;


return {

subtotal: subtotal,

discount: discount,

total: subtotal – discount

};

}

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

const cart = addToCart({ id: 1, name: 'Книга', price: 500 }, 2);

const total = calculateTotal(…cart);

console.log(total);


Решение задания 4:

javascript

// Создание DOM-элемента

function createElement(tag, classes = [], text = '') {

const element = document.createElement(tag);


if (classes.length > 0) {

element.classList.add(…classes);

}


if (text) {

element.textContent = text;

}


return element;

}

// Дебаунс

function debounce(callback, delay) {

let timeoutId;


return function(…args) {

clearTimeout(timeoutId);

timeoutId = setTimeout(() => {

callback.apply(this, args);

}, delay);

};

}

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

const searchInput = document.getElementById('search');

const debouncedSearch = debounce((query) => {

console.log('Ищем:', query);

}, 300);

searchInput.addEventListener('input', (e) => debouncedSearch(e.target.value));


Чек-лист самопроверки:

Я могу создавать чистые функции без побочных эффектов

Понимаю разницу между параметрами и аргументами

Умею использовать значения по умолчанию

Могу работать с rest parameters и spread оператором

Понимаю области видимости и замыкания

Могу создавать функции-утилиты для реальных задач


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

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

## Глава 6: Структуры Данных: Как Организовать Информацию.

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

Термин: Структура данных (Data Structure) – это способ организации, управления и хранения данных, который позволяет эффективно выполнять операции над ними.

Наиболее фундаментальными структурами данных в JavaScript являются массивы (Arrays) и объекты (Objects).

6.1. Массивы (Arrays): Работа с коллекциями

Термин: Массив (Array) – это упорядоченная коллекция элементов. Каждый элемент в массиве имеет свой индекс – числовое значение, определяющее его позицию в массиве. Индексация в JavaScript, как и во многих других языках программирования, начинается с нуля (0).

Создание массива:

Массивы создаются с помощью квадратных скобок []. Элементы внутри скобок разделяются запятыми.

javascript

// Пустой массив

let emptyArray = [];

// Массив строк

let fruits = ["яблоко", "банан", "апельсин"];

// Массив чисел

let numbers = [1, 2, 3, 4, 5];

// Массив смешанных типов (хотя обычно стараются делать массивы однотипными для читаемости)

let mixedArray = ["текст", 10, true, null];

Доступ к элементам массива:

Доступ к элементу осуществляется по его индексу, используя квадратные скобки.

javascript

let fruits = ["яблоко", "банан", "апельсин"];

console.log(fruits[0]); // Выведет: яблоко (первый элемент)

console.log(fruits[1]); // Выведет: банан (второй элемент)

console.log(fruits[2]); // Выведет: апельсин (третий элемент)

// Можно получить длину массива

console.log(fruits.length); // Выведет: 3

// Доступ к последнему элементу:

console.log(fruits[fruits.length – 1]); // Выведет: апельсин

Изменение элементов массива:

Элементы массива можно изменять, обращаясь к ним по индексу и присваивая новое значение.

javascript

let colors = ["красный", "зеленый", "синий"];

colors[1] = "желтый"; // Заменяем "зеленый" на "желтый"

console.log(colors); // Выведет: ["красный", "желтый", "синий"]

Добавление и удаление элементов (основные методы):

Массивы в JavaScript динамичны, то есть их размер может изменяться. Для этого существуют специальные методы:

push(element): Добавляет один или несколько элементов в конец массива. Возвращает новую длину массива.javascriptlet numbers = [1, 2];

numbers.push(3); // numbers теперь: [1, 2, 3]

numbers.push(4, 5); // numbers теперь: [1, 2, 3, 4, 5]

let newLength = numbers.push(6);

console.log(numbers); // [1, 2, 3, 4, 5, 6]

console.log(newLength); // 6

pop(): Удаляет последний элемент из массива и возвращает его.javascriptlet fruits = ["яблоко", "банан", "апельсин"];

let lastFruit = fruits.pop();

console.log(fruits); // ["яблоко", "банан"]

console.log(lastFruit); // "апельсин"

unshift(element): Добавляет один или несколько элементов в начало массива. Возвращает новую длину массива.javascriptlet numbers = [2, 3];

numbers.unshift(1); // numbers теперь: [1, 2, 3]

numbers.unshift(0, -1); // numbers теперь: [-1, 0, 1, 2, 3]

let newLength = numbers.unshift(-2);

console.log(numbers); // [-2, -1, 0, 1, 2, 3]

console.log(newLength); // 6

shift(): Удаляет первый элемент из массива и возвращает его.javascriptlet fruits = ["яблоко", "банан", "апельсин"];

let firstFruit = fruits.shift();

console.log(fruits); // ["банан", "апельсин"]

console.log(firstFruit); // "яблоко"

Методы для работы с частью массива:

splice(startIndex, deleteCount, item1, item2, …): Очень мощный метод, который может удалять, добавлять или заменять элементы в массиве.startIndex: Индекс, с которого начинать изменения.

deleteCount: Количество элементов для удаления (0 – если нужно только добавить).

item1, item2, …: Элементы для добавления (если они есть).

Возвращает массив удаленных элементов (если они были).

javascriptlet numbers = [1, 2, 3, 4, 5];

// Удалить 2 элемента, начиная с индекса 2

let removed = numbers.splice(2, 2);

console.log(numbers); // [1, 2, 5] (удалены 3 и 4)

console.log(removed); // [3, 4]

// Вставить элементы, не удаляя ничего, начиная с индекса 1

numbers.splice(1, 0, "a", "b");

console.log(numbers); // [1, "a", "b", 2, 5]

// Заменить 1 элемент, начиная с индекса 3, на новый элемент

numbers.splice(3, 1, "X");

console.log(numbers); // [1, "a", "b", "X", 5]

slice(startIndex, endIndex): Создает новый массив, содержащий копию части исходного массива. Не изменяет исходный массив.startIndex: Индекс, с которого начинать копирование.

endIndex (необязательный): Индекс, ДО которого включать элементы. Если не указан, копирует до конца массива.

javascriptlet letters = ["a", "b", "c", "d", "e"];

// Скопировать элементы с индекса 1 до индекса 4 (не включая 4)

let subset1 = letters.slice(1, 4);

console.log(subset1); // ["b", "c", "d"]

console.log(letters); // ["a", "b", "c", "d", "e"] (исходный массив не изменился)

// Скопировать элементы с индекса 2 до конца

let subset2 = letters.slice(2);

console.log(subset2); // ["c", "d", "e"]

// Использование отрицательных индексов (отсчет с конца)

let lastTwo = letters.slice(-2); // Два последних элемента

console.log(lastTwo); // ["d", "e"]

Другие полезные методы массивов:

concat(array1, array2, …): Объединяет два или более массивов в новый массив.javascriptlet arr1 = [1, 2];

let arr2 = [3, 4];

let combined = arr1.concat(arr2, [5, 6]);

console.log(combined); // [1, 2, 3, 4, 5, 6]

console.log(arr1); // [1, 2] (исходный массив не изменился)

Синтаксис с spread-оператором (…) (let combined = […arr1, …arr2];) часто предпочтительнее для объединения массивов, так как он более современный и гибкий.

join(separator): Объединяет все элементы массива в одну строку, используя указанный separator. Если separator не указан, используется запятая.javascriptlet words = ["Hello", "world", "!"];

let sentence = words.join(" "); // Объединяем через пробел

console.log(sentence); // "Hello world !"

let numbersString = [1, 2, 3].join("-");

console.log(numbersString); // "1-2-3"

includes(element): Проверяет, есть ли элемент в массиве. Возвращает true или false.javascriptlet numbers = [1, 2, 3, 4];

console.log(numbers.includes(3)); // true

console.log(numbers.includes(5)); // false

indexOf(element): Возвращает индекс первого вхождения элемента в массив. Если элемент не найден, возвращает -1.javascriptlet fruits = ["apple", "banana", "apple", "orange"];

console.log(fruits.indexOf("banana")); // 1

console.log(fruits.indexOf("apple")); // 0 (индекс первого вхождения)

console.log(fruits.indexOf("grape")); // -1

reverse(): Инвертирует порядок элементов в массиве на месте (изменяет исходный массив) и возвращает ссылку на измененный массив.javascriptlet letters = ["a", "b", "c"];

letters.reverse();

console.log(letters); // ["c", "b", "a"]

Итерация по массивам (уже видели for…of, но есть и другие способы):

С помощью for цикла:javascriptlet animals = ["cat", "dog", "elephant"];

for (let i = 0; i < animals.length; i++) {

console.log(`Animal at index ${i}: ${animals[i]}`);

}

forEach(callbackFunction): Выполняет функцию callbackFunction один раз для каждого элемента массива. callbackFunction получает три аргумента: element, index, array.javascriptlet fruits = ["apple", "banana", "cherry"];

fruits.forEach(function(fruit, index, arr) {

console.log(`Fruit at index ${index}: ${fruit}`);

// console.log(arr); // Можно получить доступ к самому массиву

});

// С использованием стрелочной функции:

fruits.forEach((fruit, index) => {

console.log(`Fruit #${index + 1}: ${fruit}`);

});

6.2. Объекты (Objects): Хранение данных в формате “ключ-значение”

Термин: Объект (Object) – это неупорядоченная коллекция свойств. Каждое свойство состоит из ключа (key) и значения (value), которые связаны парой двоеточия :. Ключи обычно являются строками (или символами), а значения могут быть любого типа данных (включая другие объекты или массивы).

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

Создание объекта:

Объекты создаются с помощью фигурных скобок {}.

javascript

// Пустой объект

let emptyObject = {};

// Объект "человек"

let person = {

name: "Алиса", // Свойство: ключ "name", значение "Алиса"

age: 30, // Свойство: ключ "age", значение 30

isStudent: false, // Свойство: ключ "isStudent", значение false

city: "Москва", // Свойство: ключ "city", значение "Москва"

hobbies: ["чтение", "путешествия"] // Свойство: значение – массив

};

// Объект "автомобиль"

let car = {

brand: "Toyota",

model: "Camry",

year: 2022,

// Свойство, значение которого – другой объект

engine: {

type: "Petrol",

volume: 2.5

},

// Свойство, значение которого – функция (метод объекта)

start: function() {

console.log("Двигатель запущен!");

}

};

Доступ к свойствам объекта:

Есть два основных способа:

Точечная нотация (Dot Notation): объект.ключ (наиболее распространенный и предпочтительный способ).javascriptconsole.log(person.name); // Выведет: Алиса

console.log(person.age); // Выведет: 30

console.log(car.brand); // Выведет: Toyota

console.log(car.engine.type); // Доступ к свойству вложенного объекта. Выведет: Petrol

Скобочная нотация (Bracket Notation): объект[ключ] (обязательна, если ключ – переменная или содержит специальные символы/пробелы).javascriptconsole.log(person["name"]); // Выведет: Алиса

console.log(person["city"]); // Выведет: Москва

// Пример, когда скобочная нотация обязательна:

let propertyName = "age";

console.log(person[propertyName]); // Работает! Получим 30.

// person.propertyName выдало бы ошибку или undefined,

// так как искалось бы свойство с ключом "propertyName".

// Если ключ содержит пробелы или спецсимволы, скобочная нотация необходима:

let anotherPerson = {

"first name": "Иван",

"last-name": "Петров"

};

console.log(anotherPerson["first name"]); // "Иван"

// console.log(anotherPerson.first name); // Ошибка!

Изменение и добавление свойств:

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

javascript

let user = { name: "Петр" };

// Изменение свойства

user.name = "Петр Иванов";

console.log(user.name); // Петр Иванов

// Добавление нового свойства

user.age = 25;

user["isAdmin"] = false; // Добавление через скобочную нотацию

console.log(user); // { name: "Петр Иванов", age: 25, isAdmin: false }

Удаление свойств:

Используется оператор delete.

javascript

let product = { id: 1, name: "Ноутбук", price: 1000 };

delete product.price;

console.log(product); // { id: 1, name: "Ноутбук" }

Методы объектов (функции внутри объектов):

Функции, которые являются свойствами объекта, называются методами.

javascript

let calculator = {

a: 10,

b: 5,

add: function() { // Метод add

return this.a + this.b; // this ссылается на объект calculator

},

subtract: function() { // Метод subtract

return this.a – this.b;

}

};

console.log(calculator.add()); // Выведет: 15

console.log(calculator.subtract()); // Выведет: 5

// Можно вызывать методы объекта:

calculator.result = calculator.add();

console.log(calculator.result); // 15

Термин: this – контекст выполнения. В методах объекта this обычно ссылается на сам объект, который вызывает этот метод. Понимание this – одна из ключевых (и иногда непростых) тем в JavaScript.

Перебор свойств объекта:

for…in цикл: Как мы уже видели, перебирает ключи объекта.javascriptlet book = {

title: "Война и мир",

author: "Лев Толстой",

year: 1869

};

for (let key in book) {

console.log(`${key}: ${book[key]}`);

}

// Вывод:

// title: Война и мир

// author: Лев Толстой

// year: 1869

Object.keys(obj): Возвращает массив ключей объекта. Далее можно использовать forEach или for…of.javascriptlet car = { brand: "BMW", model: "X5", year: 2023 };

let carKeys = Object.keys(car); // ["brand", "model", "year"]

console.log(carKeys);

carKeys.forEach(key => {

console.log(`${key}: ${car[key]}`);

});

Object.values(obj): Возвращает массив значений объекта.javascriptlet car = { brand: "BMW", model: "X5", year: 2023 };

let carValues = Object.values(car); // ["BMW", "X5", 2023]

console.log(carValues);

carValues.forEach(value => {

console.log(value);

});

Object.entries(obj): Возвращает массив пар [ключ, значение].javascriptlet car = { brand: "BMW", model: "X5", year: 2023 };

let carEntries = Object.entries(car);

// carEntries будет: [ ["brand", "BMW"], ["model", "X5"], ["year", 2023] ]

console.log(carEntries);

carEntries.forEach(entry => {

let key = entry[0];

let value = entry[1];

console.log(`${key}: ${value}`);

});

// С деструктуризацией (очень удобно!):

carEntries.forEach(([key, value]) => {

console.log(`${key} –> ${value}`);

});

6.3. Объект Math

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

Популярные свойства:

Math.PI: Число Пи (≈ 3.14159)

Math.E: Основание натурального логарифма (≈ 2.71828)

Популярные методы:

Math.round(x): Округляет число x до ближайшего целого.javascriptconsole.log(Math.round(4.7)); // 5

console.log(Math.round(4.4)); // 4

console.log(Math.round(4.5)); // 5

Math.floor(x): Округляет число x вниз до ближайшего целого.javascriptconsole.log(Math.floor(4.7)); // 4

console.log(Math.floor(4.4)); // 4

console.log(Math.floor(-4.4)); // -5 (округляет вниз, т.е. к меньшему числу)

Math.ceil(x): Округляет число x вверх до ближайшего целого.javascriptconsole.log(Math.ceil(4.7)); // 5

console.log(Math.ceil(4.4)); // 5

console.log(Math.ceil(-4.4)); // -4 (округляет вверх, т.е. к большему числу)

Math.abs(x): Возвращает абсолютное значение (модуль) числа x.javascriptconsole.log(Math.abs(-10)); // 10

console.log(Math.abs(10)); // 10

Math.sqrt(x): Возвращает квадратный корень из x.javascriptconsole.log(Math.sqrt(9)); // 3

console.log(Math.sqrt(2)); // 1.4142135623730951

Math.pow(base, exponent): Возвращает base в степени exponent.javascriptconsole.log(Math.pow(2, 3)); // 8 (2 в кубе)

console.log(Math.pow(5, 2)); // 25 (5 в квадрате)

Math.random(): Возвращает псевдослучайное число с плавающей точкой между 0 (включительно) и 1 (не включительно).javascriptconsole.log(Math.random()); // Например: 0.123456789…

console.log(Math.random()); // Другое случайное число

Генерация случайного целого числа в диапазоне: Часто нужно получить случайное целое число, например, от 0 до 9.javascript// Случайное целое от 0 до 9 (включительно)

let min = 0;

let max = 9;

let randomInt = Math.floor(Math.random() * (max – min + 1)) + min;

console.log(randomInt); // Будет число от 0 до 9

Math.random() * (max – min + 1): Создает случайное число от 0 до max – min + 1 (не включая верхнюю границу).

Math.floor(…): Округляет вниз, чтобы получить целое число.

+ min: Сдвигает диапазон, чтобы он начинался с min.

6.4. Объект Date

Термин: Объект Date – это встроенный объект JavaScript, который используется для работы с датами и временем. Он позволяет создавать, получать и устанавливать компоненты даты и времени (год, месяц, день, час, минута, секунда).

Создание объекта Date:

Без аргументов: Создает объект Date с текущей датой и временем.javascriptlet now = new Date();

console.log(now); // Выведет текущую дату и время в формате, зависящем от локали

С указанием даты и времени: Аргументы можно передавать в разном формате, но самый надежный – это комбинация числовых значений.javascript// new Date(год, месяц, день, час, минута, секунда, миллисекунда)

// Месяцы нумеруются с 0 (январь) до 11 (декабрь)!

let specificDate = new Date(2023, 10, 21, 14, 30, 0, 0); // 21 Ноября 2023, 14:30:00

console.log(specificDate); // Выведет: Tue Nov 21 2023 14:30:00 GMT+0000 (Coordinated Universal Time) – формат может отличаться

Важно: Месяцы в Date нумеруются с 0 (январь) по 11 (декабрь). Это частый источник ошибок.

С указанием строки: Можно передать строку, но формат строки может интерпретироваться по-разному разными браузерами, поэтому этот способ менее надежен.javascriptlet dateFromString = new Date("2023-11-21T14:30:00"); // ISO 8601 формат, более надежный

console.log(dateFromString);

Получение компонентов даты и времени:

Объект Date имеет множество методов для получения отдельных частей даты/времени:

getFullYear(): Возвращает год (4 цифры).

getMonth(): Возвращает месяц (0-11). Помните про 0-индексацию!

getDate(): Возвращает день месяца (1-31).

getDay(): Возвращает день недели (0 – воскресенье, 1 – понедельник, …, 6 – суббота).

getHours(): Возвращает час (0-23).

getMinutes(): Возвращает минуты (0-59).

getSeconds(): Возвращает секунды (0-59).

getTime(): Возвращает количество миллисекунд, прошедших с 1 января 1970 года (Unix Timestamp). Полезно для сравнения дат или измерения интервалов.

javascript

let myDate = new Date(2024, 0, 15, 10, 0, 0); // 15 Января 2024, 10:00:00

console.log("Год:", myDate.getFullYear()); // 2024

console.log("Месяц:", myDate.getMonth()); // 0 (Январь)

console.log("День месяца:", myDate.getDate()); // 15

console.log("День недели:", myDate.getDay()); // 1 (Понедельник)

console.log("Час:", myDate.getHours()); // 10

console.log("Минуты:", myDate.getMinutes()); // 0

console.log("Секунды:", myDate.getSeconds()); // 0

console.log("Timestamp:", myDate.getTime()); // Большое число миллисекунд

Установка компонентов даты и времени:

Также есть методы для установки значений:

setFullYear(year)

setMonth(month)

setDate(date)

setHours(hours, minutes, seconds, ms) (можно установить все сразу)

setTime(milliseconds)

javascript

let today = new Date();

today.setFullYear(2025);

today.setMonth(0); // Устанавливаем январь

today.setDate(1);

console.log(today); // Дата станет 1 Января 2025 года

Сравнение дат:

Даты можно сравнивать, используя обычные операторы сравнения (<, >, <=, >=). Сравнение происходит по их значению в миллисекундах (getTime()).

javascript

let date1 = new Date(2024, 0, 15);

let date2 = new Date(2024, 0, 16);

if (date1 < date2) {

console.log("date1 раньше date2"); // Выполнится

}

Практическое задание:

Создайте массив studentScores с несколькими числами (оценками).Добавьте новую оценку в конец массива с помощью push().

Удалите последнюю оценку с помощью pop() и сохраните ее в переменной droppedScore.

Добавьте оценку 100 в начало массива с помощью unshift().

Удалите первую оценку с помощью shift() и сохраните ее.

Используйте splice(), чтобы заменить оценку с индексом 2 на 95.

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

Используйте includes(), чтобы проверить, есть ли в studentScores оценка 70.

Используйте indexOf() для поиска первой оценки 80.

Используйте join() для преобразования массива studentScores в строку с разделителем “-“.

Переберите массив studentScores с помощью forEach() и выведите каждую оценку.

Создайте объект bookInfo со свойствами title, author, publicationYear, genre.Выведите название книги, используя точечную нотацию.

Выведите автора, используя скобочную нотацию и переменную propertyName = "author".

Измените publicationYear на более поздний.

Добавьте новое свойство isBestseller со значением true.

Удалите свойство genre.

Используйте for…in для перебора оставшихся свойств объекта и вывода их в консоль.

Используйте Object.values() для получения массива значений объекта и выведите его.

Используйте Object.entries() для получения пар ключ-значение и переберите их с помощью forEach() и деструктуризации.

Используя Math.random(), Math.floor() и Math.max(), Math.min(), напишите функцию getRandomInt(min, max), которая возвращает случайное целое число в заданном диапазоне (включая min и max).

Создайте объект userProfile со свойствами firstName, lastName. Напишите метод getFullName(), который возвращает полное имя, используя this.

Создайте объект currentDate типа Date.Выведите текущий год, месяц (не забудьте добавить 1, чтобы получить номер месяца от 1 до 12), день месяца.

Установите дату на 1 число следующего месяца.

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

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

## Глава 7: Взаимодействие с Веб-Страницей: DOM.

В предыдущих главах мы освоили основы языка JavaScript: переменные, типы данных, операторы, условия, циклы и функции. Мы также научились работать с коллекциями данных – массивами и объектами. Теперь пришло время соединить JavaScript с тем, что видит пользователь – с веб-страницей.

Термин: DOM (Document Object Model) – это объектно-ориентированное представление HTML-документа. DOM (Document Object Model) представляет собой независимый от платформы и языка интерфейс программирования, который предоставляет возможность программам и скриптам взаимодействовать с HTML-, XHTML- и XML-документами. С его помощью можно получать доступ к содержимому документов, а также изменять их структуру, контент и визуальное представление.

Представьте DOM как дерево, где каждый узел дерева представляет собой часть HTML-документа: тег, атрибут, текст внутри тега. JavaScript может получить доступ к этому “дереву” и манипулировать им – изменять содержимое, стили, структуру, реагировать на действия пользователя.

Когда браузер загружает HTML-страницу, он создает DOM-представление этой страницы. JavaScript, запущенный в браузере, может взаимодействовать с этим DOM.

7.1. Что такое DOM (Структура дерева)

DOM не устанавливает жестких рамок для структуры документа. Любой документ, имеющий определенную структуру, может быть представлен в виде древовидной структуры узлов. Каждый узел в этой структуре соответствует элементу, текстовому фрагменту, графическому объекту или другому компоненту документа. Узлы связаны между собой иерархическими отношениями, подобными «родитель-потомок».

DOM представляет HTML-документ в виде иерархической структуры:

Документ (Document): Корневой узел всего DOM. В JavaScript это глобальный объект document.

Элементы (Elements): Каждый HTML-тег (например, <html>, <body>, <h1>, <p>, <div>) является узлом-элементом в DOM.

Атрибуты (Attributes): Атрибуты HTML-тегов (например, src в теге , href в теге <a>, class или id) также являются узлами, но они обычно связаны с узлами-элементами.

Текстовые узлы (Text Nodes): Текст внутри HTML-элементов (например, “Привет, мир!” внутри тега <p>) также является узлом.

Пример HTML-структуры и ее DOM-представления:

html

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<title>Пример DOM</title>

</head>

<body>

Заголовок

Это первый параграф.

<div class="content">

Это второй параграф.

</body>

</html>

DOM-представление этой структуры можно схематично показать так:

Document

└── <html>

├── <head>

│ ├── <meta charset="UTF-8">

│ └── <title> (Текстовый узел: "Пример DOM")

└── <body>

├── <h1> (Текстовый узел: "Заголовок")

├── <p id="intro"> (Текстовый узел: "Это первый параграф.")

└── <div class="content">

└── <p> (Текстовый узел: "Это второй параграф.")

7.2. Как найти элементы на странице

Чтобы манипулировать элементами, нам сначала нужно получить к ним доступ. JavaScript предоставляет несколько методов для поиска элементов в DOM.

А) Поиск по ID (getElementById)

Термин: ID (Identifier) – уникальный идентификатор элемента на странице. Каждый id должен быть уникален в пределах одного HTML-документа.

Метод document.getElementById(id) возвращает первый элемент, у которого указанный id.

javascript

// Предполагая, что на странице есть …

let introParagraph = document.getElementById("intro");

console.log(introParagraph); // Выведет объект …

// Теперь мы можем работать с этим элементом:

introParagraph.textContent = "Новый текст для параграфа."; // Изменяем текст

Б) Поиск по именам тегов (getElementsByTagName)

Метод document.getElementsByTagName(tagName) возвращает HTML-коллекцию (похожа на массив, но не совсем) всех элементов с указанным именем тега.

html

<ul>

Элемент 1

Элемент 2

Какой-то текст.

<ul>

Элемент 3

javascript

let allParagraphs = document.getElementsByTagName("p");

console.log(allParagraphs); // HTMLCollection [ Какой-то текст.
] (в данном случае)

let allLists = document.getElementsByTagName("ul");

console.log(allLists); // HTMLCollection [ …, … ]

// Доступ к элементам в коллекции по индексу

console.log(allLists[0]); // Первый <ul>

console.log(allLists[1]); // Второй <ul>

// Перебор коллекции (можно использовать for…of или обычный for)

for (let list of allLists) {

console.log(list);

}

Важно: getElementsByTagName возвращает живую коллекцию. Это значит, что если DOM изменится (например, добавятся новые элементы <li>), коллекция автоматически обновится.

В) Поиск по именам классов (getElementsByClassName)

Метод document.getElementsByClassName(className) возвращает HTML-коллекцию всех элементов, у которых есть указанный CSS-класс. Можно передавать несколько классов, разделенных пробелом, но это будет искать элементы, у которых есть ВСЕ перечисленные классы.

html

Важный элемент 1

Обычный элемент

Важный элемент 2

javascript

let importantItems = document.getElementsByClassName("important");

console.log(importantItems); // HTMLCollection [ …, …
]

let allItems = document.getElementsByClassName("item");

console.log(allItems); // HTMLCollection [ …, …, …
]

// Поиск элементов с двумя классами (очень редко нужно)

let itemAndImportant = document.getElementsByClassName("item important");

console.log(itemAndImportant); // HTMLCollection [ …
]

getElementsByClassName также возвращает живую коллекцию.

Г) Современные и универсальные методы (querySelector, querySelectorAll)

Эти методы используют синтаксис CSS-селекторов, что делает их очень мощными и гибкими.

document.querySelector(selector): Возвращает первый элемент, который соответствует указанному CSS-селектору.javascript// Поиск по ID

let introPara = document.querySelector("#intro"); // То же, что getElementById("intro")

// Поиск по имени тега

let firstH1 = document.querySelector("h1");

// Поиск по классу

let firstImportantItem = document.querySelector(".important"); // Найдет первый элемент с классом "important"

// Поиск по имени тега внутри другого тега

let firstParagraphInDiv = document.querySelector("div p"); // Найдет первый <p> внутри <div>

// Поиск по комбинации селекторов

let specificDiv = document.querySelector("div.content p"); // Найдет <p> внутри <div class="content">

document.querySelectorAll(selector): Возвращает NodeList (статическую коллекцию, похожую на массив, но не живую) всех элементов, которые соответствуют указанному CSS-селектору.javascript// Получить все параграфы

let allParagraphs = document.querySelectorAll("p");

console.log(allParagraphs); // NodeList [ …
, …
]

// Получить все элементы с классом "item"

let allItems = document.querySelectorAll(".item");

console.log(allItems);

// Получить все элементы h1, h2, h3

let headings = document.querySelectorAll("h1, h2, h3");

// Получить все элементы li внутри ul

let listItems = document.querySelectorAll("ul li");

// Перебор NodeList (можно использовать forEach, for…of)

listItems.forEach((item, index) => {

console.log(`List item ${index + 1}: ${item.textContent}`);

});

Когда использовать какой метод:

getElementById: Если у вас есть id и вам нужен один конкретный элемент. Очень быстро.

querySelector: Универсальный метод для поиска первого элемента по любому CSS-селектору. Гибкий и удобный.

querySelectorAll: Для поиска всех элементов, соответствующих CSS-селектору. Возвращает NodeList, который удобно перебирать.

getElementsByTagName, getElementsByClassName: Могут быть немного быстрее querySelectorAll в специфических случаях, но querySelectorAll часто предпочтительнее из-за его универсальности и поддержки CSS-селекторов.

7.3. Изменение содержимого и атрибутов элементов

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

А) Изменение текстового содержимого:

element.textContent: Устанавливает или возвращает текст внутри элемента. HTML-теги внутри текста будут отображаться как обычный текст.

element.innerHTML: Устанавливает или возвращает HTML-код внутри элемента. Это значит, что если вы присвоите сюда строку с HTML-тегами, они будут интерпретированы браузером. Используйте с осторожностью, чтобы избежать XSS-уязвимостей (если содержимое приходит от пользователя).

html

Старый текст.

javascript

let paragraph = document.getElementById("myParagraph");

// Изменение текста

paragraph.textContent = "Новый текст."; // Отобразится как обычный текст

console.log(paragraph.textContent); // "Новый текст."

// Изменение HTML-содержимого

let div = document.getElementById("myDiv");

div.innerHTML = "Жирный текст и курсив."; // HTML-теги будут обработаны

console.log(div.innerHTML); // "Жирный текст и курсив."

// Если использовать textContent вместо innerHTML с HTML-тегами

div.textContent = "Жирный текст"; // Отобразится как "Жирный текст" (буквально)

Б) Изменение атрибутов:

Прямой доступ (для стандартных атрибутов): Если атрибут является стандартным свойством элемента (например, id, src, href, class, value), вы можете получить или установить его напрямую.html

Ссылка

javascriptlet img = document.getElementById("myImage");

img.src = "new_image.png"; // Изменяем источник изображения

img.alt = "Новое описание"; // Изменяем атрибут alt

let link = document.getElementById("myLink");

link.href = "https://new-example.com"; // Изменяем ссылку

link.textContent = "Перейти на новый сайт"; // Изменяем текст ссылки

Методы getAttribute(), setAttribute(), removeAttribute(): Более универсальные методы, которые работают с любыми атрибутами, включая пользовательские (data-*).html<button data-action="save">Сохранить</button>

javascriptlet button = document.querySelector('button');

// Получить атрибут

console.log(button.getAttribute('data-action')); // "save"

console.log(button.getAttribute('disabled')); // null (если атрибута нет)

// Установить атрибут

button.setAttribute('disabled', ''); // Делаем кнопку неактивной (значение атрибута может быть пустым)

button.setAttribute('data-action', 'delete'); // Изменяем атрибут

// Удалить атрибут

button.removeAttribute('disabled');

В) Изменение стилей:

Стили элемента можно изменять через его свойство style. Каждый CSS-свойство, написанное через дефис (например, background-color), преобразуется в camelCase (например, backgroundColor) для использования в JavaScript.

html

javascript

let box = document.getElementById("myBox");

// Изменение стиля

box.style.width = "200px"; // Устанавливаем ширину

box.style.height = "150px"; // Устанавливаем высоту

box.style.backgroundColor = "lightgreen"; // Устанавливаем фоновый цвет

box.style.border = "2px solid red"; // Добавляем рамку

// Получение текущего стиля (может быть сложнее, чем кажется, если стили заданы в CSS-файлах, а не инлайново)

console.log(box.style.width); // "200px" (если был инлайново задан или последний измененный)

Работа с классами CSS:

Часто гораздо удобнее управлять стилями, добавляя или удаляя CSS-классы, чем менять отдельные свойства style. Это позволяет отделять логику JavaScript от стилизации.

element.classList: Это свойство возвращает объект DOMTokenList, который имеет удобные методы для управления классами:add(className): Добавляет класс.

remove(className): Удаляет класс.

toggle(className): Добавляет класс, если его нет, и удаляет, если он есть.

contains(className): Проверяет, есть ли у элемента указанный класс (возвращает true или false).

html

javascript

let divWithClass = document.getElementById("myDivWithClass");

// Добавить класс

divWithClass.classList.add("highlight"); // Класс "highlight" будет добавлен

console.log(divWithClass.className); // "box normal highlight"

// Удалить класс

divWithClass.classList.remove("normal"); // Класс "normal" будет удален

console.log(divWithClass.className); // "box highlight"

// Переключить класс

divWithClass.classList.toggle("active"); // Класс "active" будет добавлен

divWithClass.classList.toggle("active"); // Класс "active" будет удален

console.log(divWithClass.classList.contains("highlight")); // true

console.log(divWithClass.classList.contains("normal")); // false

CSS, который будет работать с этими классами:

css

.box {

width: 100px;

height: 50px;

border: 1px solid black;

}

.highlight {

background-color: yellow;

font-weight: bold;

}

.active {

border-color: blue;

transform: scale(1.1); /* Небольшое увеличение */

}

7.4. Создание и удаление элементов

JavaScript может не только изменять существующие элементы, но и создавать новые, а также удалять их из DOM.

Создание элемента:

document.createElement(tagName): Создает новый элемент с указанным именем тега. Этот элемент пока не находится на странице, он существует только в памяти.javascriptlet newParagraph = document.createElement("p"); // Создали элемент <p>

newParagraph.textContent = "Это новый параграф, созданный JavaScript.";

newParagraph.classList.add("info-text"); // Добавили класс

Добавление элемента на страницу:

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

parentElement.appendChild(childElement): Добавляет childElement как последнего потомка parentElement.javascript// Предположим, у нас есть

let container = document.getElementById("container");

let newParagraph = document.createElement("p");

newParagraph.textContent = "Добавлен в конец контейнера.";

container.appendChild(newParagraph); // Параграф появится внутри div#container

parentElement.insertBefore(newElement, referenceElement): Вставляет newElement перед referenceElement внутри parentElement.javascript// Предположим, у нас есть Существующий

let container = document.getElementById("container");

let existingPara = document.getElementById("existingPara");

let newPara = document.createElement("p");

newPara.textContent = "Добавлен перед существующим.";

container.insertBefore(newPara, existingPara); // Новый параграф появится перед existingPara

Удаление элемента:

element.remove(): Удаляет элемент из DOM.javascriptlet elementToRemove = document.getElementById("itemToDelete");

if (elementToRemove) { // Важно проверять, существует ли элемент, прежде чем удалять

elementToRemove.remove();

}

parentElement.removeChild(childElement): Более старый метод, который удаляет childElement из parentElement.javascriptlet parent = document.getElementById("parent");

let child = document.getElementById("child");

if (parent && child) {

parent.removeChild(child);

}

7.5. Работа с классами CSS (краткое повторение)

Мы уже видели classList для добавления, удаления и переключения классов. Это наиболее рекомендуемый способ управления стилями элемента с помощью JavaScript.

Пример 7.5.1: Динамическое изменение стиля кнопки

html

<button id="myButton" class="btn">Нажми меня</button>

css

.btn {

padding: 10px 20px;

border: 1px solid gray;

background-color: lightgray;

cursor: pointer;

}

.btn.active { /* Стиль для активной кнопки */

background-color: dodgerblue;

color: white;

font-weight: bold;

}

javascript

let button = document.getElementById("myButton");

button.addEventListener("click", function() { // Добавим обработчик события клика (об этом позже)

button.classList.toggle("active"); // Переключит класс 'active' при каждом клике

});

При каждом нажатии на кнопку будет добавляться или удаляться класс active, что будет менять ее внешний вид согласно CSS-правилам.

Практическое задание:

Создайте простую HTML-страницу с <h1>, <p> и <ul>.Используйте document.getElementById() для доступа к <h1> и измените его текст.

Используйте document.querySelector() для доступа к первому <li> в <ul> и измените его textContent.

Используйте document.querySelectorAll() для получения всех <li> в <ul> и добавьте им класс list-item с помощью forEach() и element.classList.add().

Создайте кнопку <button id="changeColorBtn">Изменить цвет</button> и <div> с id myBox, который имеет начальный стиль (цвет фона, размеры) через CSS.При нажатии на кнопку, используя addEventListener (которое мы рассмотрим позже), измените цвет фона div на случайный. Для генерации случайного цвета используйте Math.random() для RGB-компонентов.

Напишите функцию createListItem(text), которая создает новый элемент <li> с заданным текстом и добавляет его в конец существующего <ul> на странице.

Создайте элемент <p> с текстом “Этот параграф будет удален”. Добавьте его на страницу. Затем найдите этот параграф по его textContent (это можно сделать, например, через document.querySelectorAll('p') и цикл, или если у него есть ID) и удалите его с помощью element.remove().

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

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

## Глава 8: Реакция на Действия: События.

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

Термин: Событие (Event) – это сигнал, который генерируется системой (браузером, операционной системой, пользовательским агентом) в ответ на определенное действие. JavaScript может “слушать” эти сигналы и выполнять определенный код в ответ.

8.1. Что такое события

Браузер постоянно генерирует события. Основные типы событий, с которыми мы будем работать:

События мыши:click: Нажатие левой кнопки мыши по элементу.

dblclick: Двойной щелчок левой кнопкой мыши.

mousedown: Нажатие кнопки мыши (любой) над элементом.

mouseup: Отпускание кнопки мыши (любой) над элементом.

mousemove: Движение мыши над элементом.

mouseover: Курсор мыши вошел в область элемента.

mouseout: Курсор мыши покинул область элемента.

События клавиатуры:keydown: Нажатие клавиши.

keyup: Отпускание клавиши.

keypress: Нажатие клавиши (устарел, используется keydown или input).

События формы:submit: Отправка формы.

change: Изменение значения элемента формы (input, select, textarea), когда он теряет фокус.

input: Изменение значения элемента формы, происходит мгновенно при каждом вводе.

focus: Элемент получил фокус.

blur: Элемент потерял фокус.

События документа и окна:load: Полная загрузка страницы (все ресурсы, включая изображения, скрипты, стили, загружены).

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

resize: Изменение размера окна браузера.

scroll: Прокрутка страницы.

8.2. Как реагировать на события: Обработчики событий (addEventListener)

Самый современный и рекомендуемый способ “слушать” события – это метод addEventListener(). Он позволяет прикрепить одну или несколько функций-обработчиков к конкретному событию на конкретном элементе.

Термин: Обработчик события (Event Handler) – это функция, которая будет вызвана, когда произойдет определенное событие.

Синтаксис: element.addEventListener(eventType, handlerFunction, options);

element: Элемент, на котором мы хотим “слушать” событие (например, document.getElementById('myButton')).

eventType: Строка, имя события, которое мы хотим отслеживать (например, 'click', 'keydown', 'mouseover').

handlerFunction: Функция, которая будет вызвана, когда событие произойдет.

options (необязательный): Объект с дополнительными настройками (например, capture для фазы распространения события, once для однократного срабатывания, passive для оптимизации прокрутки).

Пример 8.2.1: Обработка клика по кнопке

html

<button id="myBtn">Нажми меня</button>

javascript

let button = document.getElementById('myBtn');

button.addEventListener('click', function() {

console.log("Кнопка была нажата!");

alert("Вы нажали на кнопку!");

});

При каждом клике на кнопку myBtn будет выведено сообщение в консоль и появилось бы всплывающее окно.

Пример 8.2.2: Обработка ввода в поле формы

html

<input type="text" id="myInput">

javascript

let inputField = document.getElementById('myInput');

let outputParagraph = document.getElementById('output');

// Используем событие 'input', которое срабатывает каждый раз при изменении значения

inputField.addEventListener('input', function() {

let currentValue = inputField.value; // .value – свойство поля ввода, хранящее его текущее значение

outputParagraph.textContent = "Вы вводите: " + currentValue;

});

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

Отмена обработчика:

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

javascript

function handleClick() {

console.log("Обработчик клика сработал.");

}

let myButton = document.getElementById('myBtn');

myButton.addEventListener('click', handleClick);

// Позже, если нужно удалить обработчик:

// myButton.removeEventListener('click', handleClick);

Старые методы (не рекомендуются для нового кода):

Ранее использовались HTML-атрибуты (onclick="myFunction()") или прямое присваивание свойств (element.onclick = myFunction;). Эти методы имеют ограничения:

Позволяют прикрепить только один обработчик на событие.

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

8.3. Объект события (Event Object)

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

javascript

let myElement = document.getElementById('someElement');

myElement.addEventListener('click', function(event) {

console.log(event); // Выведет объект события

// event содержит информацию о том, что произошло

});

Полезные свойства объекта события:

event.target: Ссылка на элемент, который ИНИЦИИРОВАЛ событие. Это может быть не тот элемент, на котором мы “слушаем” событие, если используется всплытие.

event.currentTarget: Ссылка на элемент, к которому прикреплен текущий обработчик.

event.type: Тип события (например, 'click', 'mousemove').

event.clientX, event.clientY: Координаты курсора мыши относительно видимой области окна (viewport) в момент события.

event.pageX, event.pageY: Координаты курсора мыши относительно всего документа (включая прокрученную часть).

event.key: Нажатая клавиша (для событий клавиатуры, например, 'Enter', 'a', 'ArrowRight').

event.keyCode / event.which: Код нажатой клавиши (устаревшие, лучше использовать event.key).

event.target.value: Для элементов формы – текущее значение поля.

Пример 8.3.1: Получение координат мыши и информации о клавише

html

<div id="mouseInfo" style="width: 200px; height: 100px; background-color: lightblue; margin-top: 10px;">

Двигайте мышью здесь

javascript

let mouseInfoDiv = document.getElementById('mouseInfo');

let keyInfoPara = document.getElementById('keyInfo');

mouseInfoDiv.addEventListener('mousemove', function(event) {

// event.clientX и event.clientY – координаты относительно окна браузера

let x = event.clientX;

let y = event.clientY;

mouseInfoDiv.textContent = `Мышь: X=${x}, Y=${y}`;

});

document.addEventListener('keydown', function(event) { // Слушаем событие на всем документе

keyInfoPara.textContent = `Нажата клавиша: ${event.key} (код: ${event.code})`; // event.code – более современный идентификатор клавиши

});

8.4. Всплытие и погружение событий (Event Bubbling & Capturing)

Когда событие происходит на одном элементе, оно не остается “привязанным” только к нему. События в DOM проходят через два этапа:

Фаза погружения (Capturing Phase): Событие “спускается” от корневого элемента документа (window или document) вниз, к целевому элементу.

Фаза всплытия (Bubbling Phase): Событие “всплывает” от целевого элемента вверх, к корневому элементу документа.

Термин: Всплытие (Event Bubbling) – процесс, при котором событие, инициированное на внутреннем элементе, последовательно “всплывает” по цепочке родительских элементов, запуская обработчики на каждом из них.

Термин: Погружение (Event Capturing) – процесс, обратный всплытию. Событие сначала проходит через родительские элементы (двигаясь сверху вниз), а затем достигает целевого элемента.

По умолчанию, addEventListener прикрепляет обработчик к фазе всплытия. Вы можете указать третьему параметру options, чтобы прикрепить обработчик к фазе погружения ({ capture: true }).

Почему это важно?

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

Термин: Делегирование событий (Event Delegation) – мощная техника, когда вместо того, чтобы вешать обработчики на множество одинаковых дочерних элементов, мы вешаем один обработчик на их общего родителя. Внутри этого обработчика мы проверяем, на каком именно дочернем элементе произошло событие (используя event.target), и выполняем нужные действия.

Пример 8.4.1: Делегирование событий для списка

html

<ul id="myList">

Элемент 1

Элемент 2

Элемент 3

Без делегирования (плохой подход, если элементов много):

javascript

// const listItems = document.querySelectorAll('#myList li');

// listItems.forEach(item => {

// item.addEventListener('click', function() {

// console.log('Нажали на: ' + this.textContent);

// });

// });

// Если добавить новый элемент после загрузки страницы, на него уже не будет обработчика!

С делегированием (лучший подход):

javascript

let list = document.getElementById('myList');

list.addEventListener('click', function(event) {

// event.target – это элемент, на котором произошло событие (например, <li>)

// event.currentTarget – это сам <ul>, к которому прикреплен обработчик

// Проверяем, действительно ли кликнули по элементу <li>

// event.target.tagName вернет "LI" (в верхнем регистре)

if (event.target && event.target.tagName === "LI") {

console.log('Нажали на: ' + event.target.textContent);

// Можно добавить изменения стиля, например:

// event.target.style.textDecoration = 'line-through';

}

});

Преимущества делегирования:

Производительность: Меньше обработчиков висит на странице.

Динамические элементы: Если вы добавляете новые элементы <li> на страницу динамически (через JavaScript), на них автоматически будет распространяться обработчик родительского элемента, без необходимости вешать новый обработчик на каждый элемент.

8.5. preventDefault и stopPropagation

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

event.preventDefault(): Отменяет стандартное действие браузера, связанное с событием.Пример 8.5.1: Отмена отправки формыhtml<form id="myForm">

<input type="text" name="data">

<button type="submit">Отправить</button>

</form>

javascriptlet myForm = document.getElementById('myForm');

myForm.addEventListener('submit', function(event) {

event.preventDefault(); // Отменяем стандартное поведение формы (перезагрузка страницы)

console.log("Форма отправлена (но не перезагрузили страницу).");

// Здесь мы можем обработать данные формы с помощью JavaScript

let formData = new FormData(myForm); // Получаем данные формы

let dataValue = formData.get('data'); // Получаем значение поля 'data'

console.log("Данные из формы:", dataValue);

});

Без event.preventDefault() при нажатии на кнопку “Отправить” страница бы перезагрузилась.

event.stopPropagation(): Останавливает всплытие (или погружение) события. Обработчики на родительских элементах не будут вызваны.Пример 8.5.2: Остановка всплытияhtml<div id="parent" style="padding: 20px; background-color: lightcoral;">

Родитель

<button id="childBtn" style="padding: 10px; background-color: lightgreen;">Ребенок</button>

javascriptlet parentDiv = document.getElementById('parent');

let childButton = document.getElementById('childBtn');

parentDiv.addEventListener('click', function() {

console.log("Клик по РОДИТЕЛЮ.");

});

childButton.addEventListener('click', function(event) {

console.log("Клик по РЕБЕНКУ.");

event.stopPropagation(); // Останавливаем всплытие события

console.log("Всплытие остановлено.");

});

Если кликнуть на кнопку “Ребенок”:

Сначала выполнится обработчик кнопки (“Клик по РЕБЕНКУ.”).

Затем event.stopPropagation() прервет всплытие.

Обработчик родительского div (“Клик по РОДИТЕЛЮ.”) НЕ выполнится.

Если бы stopPropagation() не было, то после клика по кнопке, событие всплыло бы к div, и выполнился бы и его обработчик.

Практическое задание:

Создайте HTML-страницу с кнопкой (id="toggleTextBtn") и параграфом (id="textToToggle").При нажатии на кнопку, добавьте или удалите класс hidden у параграфа.

CSS для .hidden { display: none; }.

Используйте addEventListener для события click на кнопке.

Создайте поле ввода (<input type="text" id="nameInput">) и пустой параграф (
).Слушайте событие input на поле ввода.

В обработчике, обновите текст параграфа greetingOutput, показывая введенное значение: “Привет, [введенное имя]!”.

Создайте список (<ul id="taskList">). В нем несколько элементов <li>.Используйте делегирование событий, чтобы при клике на любой <li> он подчеркивался (добавлялся класс completed).

CSS для .completed { text-decoration: line-through; }.

Убедитесь, что работает и для уже существующих, и для новых элементов <li> (подумайте, как добавить новый элемент через JS и чтобы на него тоже действовало).

Создайте форму с полем ввода и кнопкой “Поиск”.Добавьте обработчик события submit на форму.

Используйте event.preventDefault(), чтобы отменить стандартную отправку формы.

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

Создайте два вложенных элемента: div (родитель) и button (ребенок).Прикрепите обработчик события click к родителю и выведите “Клик по родителю”.

Прикрепите обработчик события click к кнопке. Внутри него выведите “Клик по кнопке” и вызовите event.stopPropagation().

Проверьте, что происходит при клике на кнопку, а что при клике на область родителя вне кнопки.

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

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

## Глава 9: Асинхронность: Работа с Временем и Ожиданием.

В предыдущих главах мы писали код, который выполнялся синхронно. Это значит, что инструкции выполнялись последовательно, одна за другой. Если одна инструкция требовала много времени (например, загрузка большого файла с сервера), вся программа “зависала” до тех пор, пока эта инструкция не завершится. В веб-разработке это неприемлемо – пользователь не должен ждать, пока загрузится картинка, или пока скрипт выполнит долгую операцию.

Термин: Синхронное выполнение (Synchronous Execution) – инструкции выполняются строго по порядку. Каждая последующая инструкция ждет завершения предыдущей.

Термин: Асинхронное выполнение (Asynchronous Execution) – инструкции могут выполняться “параллельно” или вне основного потока выполнения. Программа не ждет завершения долгой операции, а продолжает выполнять другие задачи, получая результат асинхронной операции позже.