JavaScript — язык веб-разработки

Продолжительность: 20 уроков · Уровень: с нуля

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

1. Что такое JavaScript

JavaScript (сокращённо JS) — язык сценариев, который по умолчанию работает прямо в браузере пользователя. Он не имеет отношения к языку Java — это разные языки с похожим названием.

Современный JavaScript используется повсюду:

ГдеЧто позволяет делать
Браузеринтерактив: меню, слайдеры, формы, игры
Node.jsсерверные приложения, API, работа с базами данных
React Nativeмобильные приложения под iOS и Android
Electronдесктопные приложения (VS Code написан на JS)
По данным опросов JavaScript много лет остаётся самым востребованным языком программирования в мире.

2. Подключение скрипта

Самый простой способ — вставить код прямо в страницу между тегами <script>:

<script>
  console.log("Привет, мир!");
</script>

Лучше выносить код в отдельный файл и подключать его так:

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

Скрипт подключают обычно перед закрывающим тегом </body> — к этому моменту страница уже отрисована, и все элементы доступны. Можно также использовать атрибут defer в <head>.

Консоль разработчика

В браузере нажми F12 и открой вкладку Console. Туда выводится результат console.log(), там же удобно экспериментировать: пишешь выражение — получаешь ответ.

Начни с простого: открой консоль на любой странице и введи 2 + 2 * 3. Браузер тут же выведет результат.

3. Переменные

Переменные хранят значения. Современные объявления — let для изменяемых и const для неизменяемых:

let age = 25;
age = 26;                 // ok, let можно менять

const name = "Аня";
name = "Оля";             // ошибка: const нельзя переприсвоить

Старое объявление var лучше не использовать: его область видимости — функция, а не блок, из-за чего возникают неожиданные баги.

Правила имён

4. Типы данных и операторы

Основные типы данных:

ТипПримерОписание
number42, 3.14числа, включая дробные
string"текст", 'текст'строки
booleantrue, falseлогические значения
nullnull«пусто» (задаётся сознательно)
undefinedundefinedзначение отсутствует
object{ a: 1 }объекты, массивы, функции

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

// строгое сравнение — используй всегда
5 === "5"          // false: число не равно строке
5 == "5"           // true: нестрогое сравнение (избегай)
5 !== 6            // true

// логические операторы
age >= 18 && hasPassport   // И
role === "admin" || role === "moderator"  // ИЛИ
!isBlocked                            // НЕ
Всегда используй === и !==. Нестрогие == и != приводят типы и дают сюрпризы вроде "1" == 1 → true.

5. Строки

Строки можно соединять оператором +, но удобнее шаблонные литералы в обратных кавычках:

const name = "Иван";
const age = 30;

// конкатенация (устаревший стиль)
const s1 = "Меня зовут " + name + ", мне " + age + " лет.";

// шаблонная строка (современный стиль)
const s2 = `Меня зовут ${name}, мне ${age} лет.`;

Полезные методы строк:

const txt = "Hello, World!";
txt.length;             // 13
txt.toUpperCase();      // "HELLO, WORLD!"
txt.includes("World");  // true
txt.startsWith("Hell"); // true
txt.replace("Hello", "Hi");  // "Hi, World!"
"a,b,c".split(",");     // ["a", "b", "c"]
"  text  ".trim();      // "text"

6. Условия и циклы

if / else и тернарный оператор

const score = 85;
let grade;

if (score >= 90) {
  grade = "A";
} else if (score >= 75) {
  grade = "B";
} else {
  grade = "C";
}

// короткая запись «условие ? да : нет»
const status = score >= 60 ? "сдал" : "не сдал";

Циклы

// классический цикл с счётчиком
for (let i = 0; i < 5; i++) {
  console.log(i);
}

// по элементам коллекции
const colors = ["red", "green", "blue"];
for (const color of colors) {
  console.log(color);
}

// while — пока условие истинно
let n = 10;
while (n > 0) {
  n--;
}
i++ — это сокращение от i = i + 1. Есть также i-- и i += 2.

7. Функции

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

// объявление функции
function add(a, b) {
  return a + b;
}
add(2, 3);              // 5

// функциональное выражение
const multiply = function (a, b) {
  return a * b;
};

// стрелочная функция — короткая запись
const square = (x) => x * x;
square(4);              // 16

Параметры могут иметь значения по умолчанию:

function greet(name = "Гость") {
  return `Привет, ${name}!`;
}
greet("Аня");   // "Привет, Аня!"
greet();        // "Привет, Гость!"

Callback — функция как аргумент

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}
repeat(3, (i) => console.log("раз", i));

8. Массивы

Массив — упорядоченный набор значений. Основные операции:

const nums = [1, 2, 3];
nums.push(4);            // [1, 2, 3, 4] — добавить в конец
nums.pop();              // убрать с конца
nums.unshift(0);         // [0, 1, 2, 3] — добавить в начало
nums.shift();            // убрать с начала
nums.includes(2);        // true
nums.indexOf(3);         // 2

Современные переборы и преобразования — это цепочки методов:

const prices = [100, 250, 40, 90];

prices.forEach((p) => console.log(p));        // просто перебрать
const doubled = prices.map((p) => p * 2);    // новый массив
const big = prices.filter((p) => p > 80);    // отфильтровать

const total = prices.reduce((sum, p) => sum + p, 0);   // сумма = 480
Методы map, filter и reduce не изменяют исходный массив, а возвращают новый. Это главный стиль работы с данными в современном JS.

9. Объекты

const user = {
  name: "Аня",
  age: 26,
  skills: ["JS", "HTML", "CSS"],
  greet() {
    return `Привет, я ${this.name}`;
  }
};

user.name;            // "Аня" — через точку
user["age"];          // 26 — через скобки (если ключ в переменной)
user.skills.push("Python");
user.greet();         // "Привет, я Аня"

Объект легко превратить в JSON-строку и обратно — так данные передаются между клиентом и сервером:

const json = JSON.stringify(user);   // строка JSON
const copy = JSON.parse(json);       // обратно в объект
const clone = { ...user, age: 27 };  // копия с новым полем age

10. DOM: работа со страницей

DOM — это объектная модель документа: страница видится как дерево элементов, которые можно находить и менять.

Поиск элементов

// по CSS-селектору — первый подходящий
const btn = document.querySelector(".btn-save");

// по идентификатору
const title = document.getElementById("title");

// все подходящие элементы (NodeList)
const items = document.querySelectorAll(".card");

Изменение содержимого и стилей

title.textContent = "Новый заголовок";
title.style.color = "blue";
title.classList.add("highlight");       // добавить класс
title.classList.toggle("active");       // включить/выключить
title.classList.remove("highlight");
Не путай innerHTML и textContent. innerHTML разбирает HTML-разметку и опасен при вставке пользовательских данных (XSS). Для обычного текста используй textContent.

Создание элементов

const li = document.createElement("li");
li.textContent = "Новый пункт";
document.querySelector("ul").appendChild(li);

11. События

Событие — реакция на действие пользователя или браузера: клик, ввод текста, отправка формы.

const btn = document.querySelector(".btn-save");

btn.addEventListener("click", () => {
  alert("Сохранено!");
});

document.querySelector("input").addEventListener("input", (e) => {
  console.log(e.target.value);     // что введено
});

form.addEventListener("submit", (e) => {
  e.preventDefault();              // отменить перезагрузку страницы
  // ...обработка формы...
});

Частые события: click, input, change, submit, keydown, scroll, mouseover.

Один обработчик на общий родитель ловит события от всех детей — это называется «делегирование» и работает быстрее, чем вешать обработчик на каждый элемент: list.addEventListener("click", (e) => { if (e.target.tagName === "LI") { ... } }).

12. Асинхронность и загрузка данных

Запросы к серверу и таймеры не блокируют страницу — код «ждёт» результата с помощью промисов.

Таймеры

setTimeout(() => console.log("через 2 сек"), 2000);
const id = setInterval(() => console.log("каждую секунду"), 1000);
clearInterval(id);   // остановить

fetch и async/await

async function loadUsers() {
  try {
    const res = await fetch("https://api.example.com/users");
    if (!res.ok) {
      throw new Error(`Ошибка: ${res.status}`);
    }
    const users = await res.json();
    console.log(users);
  } catch (err) {
    console.error("Не удалось загрузить:", err);
  }
}
loadUsers();
Старый стиль с then тоже встречается в коде: fetch(url).then(res => res.json()).then(data => {...}). async/await — более читаемая современная альтернатива.

13. Обработка ошибок и отладка

Ошибки — часть разработки: важно не паниковать, а системно искать причину. Разберём инструменты: try/catch для перехвата, console для диагностики и отладчик DevTools для пошагового исполнения.

// перехват и обработка ошибки
try {
  const data = JSON.parse(text);
} catch (err) {
  console.error("Некорректный JSON:", err);
} finally {
  console.log("выполнится всегда");
}

// бросить собственную ошибку
if (!email.includes("@")) {
  throw new Error("Неверный email");
}
Вместо «печатаю значение и смотрю» учись ставить breakpoint: видно и значения, и порядок исполнения, и стек вызовов.
Не приглушай ошибки пустым catch. Логируй хотя бы строку и решай, как реагировать: исправить, показать пользователю или повторить запрос.

14. Модули: import и export

Когда кода становится много, его разбивают на файлы — модули. Современные браузеры и сборщики понимают синтаксис ES Modules: export объявляет наружу, import подключает.

// utils.js
export const formatPrice = (n) => `${n} ₽`;
export function sum(list) {
  return list.reduce((a, b) => a + b, 0);
}

// main.js
import { formatPrice, sum } from "./utils.js";

console.log(formatPrice(sum([10, 20]))); // "30 ₽"

Модуль подключается атрибутом type="module":

<script type="module" src="main.js"></script>
В реальных проектах модули компилируются в один файл сборщиком, поэтому разбитие на файлы ничего не «стоит».

15. LocalStorage и состояние

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

// сохранить и прочитать
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");

// объекты сериализуются в JSON
const cart = [{ id: 1, qty: 2 }];
localStorage.setItem("cart", JSON.stringify(cart));
const saved = JSON.parse(localStorage.getItem("cart") || "[]");

// удалить
localStorage.removeItem("theme");
localStorage.clear();
ХранилищеЖизньКогда использовать
localStorageпостояннонастройки, кэш, черновики
sessionStorageдо закрытия вкладкивременное состояние формы
cookieпо срокупередача данных на сервер
localStorage — не место для секретов: его читает любой скрипт на странице. Персональные данные храните на сервере.
При чтении проверяйте, что JSON «доехал»: обёртка try/catch спасёт от битых данных старой версии страницы.

16. Практика: список задач Todo

Соберём всё в мини-приложение: массив задач, рендер списка, обработчик клика, сохранение в localStorage. Пример связывает типы данных, функции, DOM, события и хранилище.

let tasks = JSON.parse(localStorage.getItem("tasks") || "[]");

function render() {
  const ul = document.querySelector("#list");
  ul.innerHTML = "";
  for (const task of tasks) {
    const li = document.createElement("li");
    li.textContent = task;
    li.addEventListener("click", () => {
      tasks = tasks.filter((t) => t !== task);
      localStorage.setItem("tasks", JSON.stringify(tasks));
      render();
    });
    ul.appendChild(li);
  }
}

document.querySelector("#add").addEventListener("click", () => {
  const input = document.querySelector("#text");
  if (!input.value.trim()) return;
  tasks.push(input.value.trim());
  input.value = "";
  localStorage.setItem("tasks", JSON.stringify(tasks));
  render();
});

render();
Запустите логику на любой HTML-странице с полями input#text, button#add и ul#list. Перезагружайте страницу — список сохраняется.
Дальше: типизация в TypeScript, компонентный подход в Vue или React, серверная часть в Node.js.

17. Деструктуризация: spread и rest

Деструктуризация «распаковывает» объекты и массивы в переменные — код становится короче и чище, чем обращение по индексам и точкам.

const user = { name: "Ира", age: 28, city: "Москва" };

const { name, city } = user;   // name, city
const [first, ...rest] = [10, 20, 30]; // first=10, rest=[20,30]

const copy = { ...user, city: "Питер" }; // копия с правкой
Spread в параметрах функций — способ принимать любое количество аргументов без arguments.
Деструктуризация не клонирует вложенные объекты поверхностно: ...obj копирует только верхний уровень.

18. Числа и даты: Math, Date, Intl

Стандартные модули JavaScript закрывают почти все задачи с числами, временем и форматированием — без сторонних библиотек.

Math.round(2.6);   // 3
new Date(2026, 0, 15); // 15 января 2026

new Intl.NumberFormat("ru-RU", { style: "currency",
    currency: "RUB" }).format(1234); // "1 234,00 ₽"

Date хранит время от 1 января 1970 UTC. Безопаснее использовать getTime() и формат ISO для передач в API, а не строковые toString().

Для сложных часовых поясов (летнее время, история) даже Intl не хватает — тогда подключайте библиотеку date-fns/Temporal.
new Date("жстроки") разбирается по-разному в браузерах. Всегда передавайте ISO-формат 2006-09-19T12:00:00Z.

19. Регулярные выражения: поиск и замена

Регулярки — язык шаблонов для строк: валидация, поиск, замена, разбор фрагментов. Запись вида /шаблон/флаги или конструктор new RegExp().

const email = "ivan@example.com";
email.match(/^[\w.+-]+@[\w-]+\.[\w.]+$/); // проверка

"a1b2c3".replace(/\d/g, "#"); // "a#b#c#"
"2026-09-19".match(/(\d+)-(\d+)-(\d+)/);// 3 группы
СимволЗначение
\d, \w, \sцифра, слово, пробел
.**, +, ?любой, один+, один?
[a-z]диапазон символов
^, $начало и конец строки
Используйте флаги глобально (g) и без учёта регистра (i) — редкие флаги (u, s) включайте осознанно.
Дорогие регулярки на пользовательском вводе могуть «съесть» CPU на длинных строках (ReDoS). Ограничивайте длину обрабатываемых данных.
Валидацию форм и input мы разбираем в разделе 11 этого курса.

20. Циклы событий: таймеры, микро- и макрозадачи

JavaScript выполняет код по очереди, но асинхронные операции встают в очередь событий. Порядок запуска определяется циклом событий (event loop).

console.log("1");                 // сразу
setTimeout(() => console.log("3"), 0); // макрозадача
Promise.resolve().then(() => console.log("2")); // микрозадача
// вывод: 1, 2, 3

Микрозадачи (promises, queueMicrotask) выполняются после текущего «куска», макрозадачи (setTimeout, события) — после очереди микрозадач.

Для отладки асинхронности используйте «Async» стек в DevTools и отладочные точки на загрузке промисов.
Не «заваливайте» event loop тяжёлыми вычислениями в обработчиках — разбивайте на chunk (setTimeout) или переносите в Web Worker.

21. Map, Set и другие коллекции

Объект как словарь работает хорошо, пока ключи — строки. Для чисел, произвольных объектов и гарантии порядка существуют коллекции Map и Set.

const cache = new Map();
cache.set(user.id, user);        // ключ — число
cache.has(id) && cache.get(id);

const tags = new Set(['js', 'es6']);
tags.add('async'); tags.has('js'); // true
[...tags];                         // без дублей
Для кэша в рамках сессии Map удобнее объекта: O(1), встроенный size и метод clear().
Не используйте Object как словарь, если ключи не строки и не символы: сценарии с __proto__ и порядком свойств порождают трудноуловимые баги (см. раздел 9).

22. Генераторы и итерируемые объекты

Генератор — функция, которая «замораживается» на каждом yield и возобновляется по запросу. Это ленивая фабрика значений, экономящая память на больших наборах.

function* ids() {
  let i = 0;
  while (true) yield ++i;   // бесконечная последовательность
}
const it = ids();
it.next().value; // 1, 2, 3 ...

const range = { *[Symbol.iterator]() { yield 1; yield 2; } };
[...range]; // [1, 2]
Комбинируйте с промисами: async-генераторы (for await...) читают API постранично, не загружая всё в память.
Бесконечный генератор без условия выхода — «тихий» вечный цикл: всегда предусматривайте остановку при переборе.

23. Классы, прототипы и контекст this

Классы в JavaScript — аккуратный синтаксис поверх прототипов: наследование идёт через prototype, а this определяется способом вызова, а не местом написания.

class User {
  constructor(name) { this.name = name; }
  sayHi() { return `Hi, ${this.name}`; }
}
const u = new User('Аня');
setTimeout(() => u.sayHi(), 0); // стрелка сохранит this
Для «прилипания» this в обработчиках событий используйте стрелки или .bind() — иначе получите классический баг «иногда не работает».
Не полагайтесь на this внутри «классических» function в колбэке: контекст теряется в самый неожиданный момент.

24. Валидация форм и работа с пользовательским вводом

Форма — главный источник данных с сайта. Валидация делится на встроенную (required, pattern) и кастомную на JS: нужны обе — одна без другой неполна.

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const email = form.email.value.trim();
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    showError('email', 'Введите корректный email');
    return;
  }
  fetch(form.action, {
    method: 'POST', body: new FormData(form)
  });
});
Доступ к полям легче через form.elements: имя формы открывает поля поиском, а не цепочкой querySelector.
Никогда не доверяйте данным формы на сервере без повторной проверки: клиентский JS обходится за секунды.

Что дальше

Ты освоил основы JavaScript. Дальше стоит углубиться в HTML и CSS, если они ещё не пройдены, а затем перейти к фреймворкам — React или Vue, и к серверной разработке на Node.js. Обзоры всех курсов ждут тебя на главной странице.

← Вернуться к курсам