Модуль 3: JavaScript — Интерактивность и логика
Освоим основы JavaScript, взаимодействие с DOM, обработку событий, работу с формами и хранилищами, а также Fetch API для получения данных.
3.1 Введение в JavaScript. Синтаксис и подключение
JS добавляет интерактивность. Подключают перед закрывающим </body>, чтобы не блокировать загрузку.
<script src="app.js"></script>
                Переменные и типы
let count = 0;         // можно переназначить
const appName = 'SKL'; // константа
console.log(typeof 42, typeof 'str', typeof true, typeof null, typeof undefined);
                Консоль
console.log('info');
console.warn('warning');
console.error('error');
                3.2 Операторы и условия
const hour = new Date().getHours();
if (hour < 12) {
  console.log('Доброе утро');
} else if (hour < 18) {
  console.log('Добрый день');
} else {
  console.log('Добрый вечер');
}
const role = 'admin';
switch (role) {
  case 'admin': console.log('Администратор'); break;
  case 'user': console.log('Пользователь'); break;
  default: console.log('Гость');
}
                Практика
Напишите скрипт, который приветствует пользователя в зависимости от времени суток.
3.3 Функции
function sum(a, b) { return a + b; }          // declaration
const multiply = (a, b) => a * b;            // arrow
const greet = (name = 'мир') => `Привет, ${name}!`; // значение по умолчанию
console.log(sum(2,3), multiply(2,3), greet());
                Практика
Сделайте набор функций‑калькуляторов: сумма, разность, среднее, площадь круга.
3.4 Циклы и массивы
const projects = ['Сайт', 'Бот', 'Игра'];
projects.push('Чат');
projects.forEach((p, i) => console.log(i + 1, p));
for (let i = 0; i < projects.length; i++) {
  if (i === 2) break;
  console.log('for:', projects[i]);
}
                Практика
Выведите в консоль нумерованный список проектов и посчитайте их количество.
3.5 Работа с DOM — Часть 1 (Поиск и изменение)
const title = document.getElementById('title');
const button = document.querySelector('.btn');
button.addEventListener('click', () => {
  title.textContent = 'Новый заголовок';
  title.style.color = '#4f46e5';
});
                Практика
По клику на кнопку меняйте текст и стиль произвольного элемента.
3.6 Работа с DOM — Часть 2 (События)
const modal = document.getElementById('modal');
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');
openBtn.addEventListener('click', () => modal.classList.add('open'));
closeBtn.addEventListener('click', () => modal.classList.remove('open'));
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') modal.classList.remove('open');
});
                Практика
Соберите простое модальное окно, которое открывается и закрывается по клику и клавише Esc.
3.7 Работа с формами в JS
const form = document.querySelector('form');
const email = form.querySelector('input[type="email"]');
form.addEventListener('submit', (e) => {
  e.preventDefault();
  const value = email.value.trim();
  const isValid = /.+@.+\..+/.test(value);
  if (!isValid) {
    alert('Введите корректный email');
    return;
  }
  alert('Спасибо! Форма принята');
});
                Практика
Соберите форму обратной связи и проверьте обязательные поля (имя, email).
3.8 Хранение данных на клиенте
// Тема
const theme = localStorage.getItem('theme') || 'light';
localStorage.setItem('theme', theme);
// Простые заметки
const notes = JSON.parse(localStorage.getItem('notes') || '[]');
notes.push('Сделать портфолио');
localStorage.setItem('notes', JSON.stringify(notes));
                Практика
Сделайте список заметок/задач, который сохраняется между перезагрузками.
3.9 (Бонус) Асинхронность. Fetch API
async function loadPosts() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=3');
  const posts = await res.json();
  console.log(posts);
}
loadPosts();
                Практика
Получите 3 поста из JSONPlaceholder и выведите заголовки на страницу.
3.10 Современный JS (ES6+) и модули
- Деструктуризация, rest/spread, шаблонные строки, опциональная цепочка 
?., nullish‑объединение??. - Коллекции: 
Map,Set,WeakMap,WeakSet. - Модули: 
<script type="module">,import/export, отложенная загрузка. 
<script type="module">
  import { sum } from './utils/math.js';
  console.log(sum(2, 3));
</script>
                3.11 Web APIs: события, Fetch/Streams, Storage
События
const controller = new AbortController();
const handler = (e) => console.log('mousemove');
document.addEventListener('mousemove', handler, { passive: true, signal: controller.signal });
setTimeout(() => controller.abort(), 3000);
                Fetch + Streams
const res = await fetch('/big.json');
const reader = res.body.getReader();
let received = 0;
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  received += value.length;
  console.log('Получено байт:', received);
}
                Cache/Storage
await caches.open('app-v1').then((cache) => cache.add('/offline.html'));
const db = await indexedDB.open('app-db', 1);
                3.12 Производительность (LCP/CLS/INP)
- LCP: оптимизируйте главное изображение: 
preload/fetchpriority, подходящий формат. - CLS: резервируйте место медиаконтенту, не вставляйте динамику над контентом.
 - INP: обработчики событий — быстрые, используйте 
passive, делайте работу вне главного потока черезrequestIdleCallback/Web Workers. 
<link rel="preload" as="image" href="/img/hero@1x.avif" imagesrcset="/img/hero@1x.avif 1x, /img/hero@2x.avif 2x" imagesizes="100vw">
                3.13 Безопасность: CSP, XSS, CORS
- CSP: ограничивает источники скриптов/стилей/изображений. Избегайте inline‑скриптов, используйте nonce/sha256.
 - XSS: никогда не вставляйте пользовательский ввод как HTML; экранируйте, используйте 
textContent. - CORS: сервер должен явно разрешать домены/методы/заголовки; на клиенте — используйте 
mode: 'cors'. 
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-abc'; img-src 'self' data:; object-src 'none'">
                3.14 Инструменты: npm, Vite/Webpack, PostCSS, Sass
- npm: управление зависимостями, команды 
scripts. - Vite: дев‑сервер, HMR, сборка с esbuild/rollup.
 - Webpack: гибкая сборка модулей, код‑сплиттинг.
 - PostCSS/Sass: автопрефиксы, nested‑синтаксис, переменные и миксины.
 
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}
                3.15 Тестирование: Jest и Playwright
- Jest: модульные тесты, моки, снапшоты.
 - Playwright: e2e‑тесты в реальном браузере; авто‑ожидания, тестирование доступности.
 
// jest
import { sum } from './sum';
test('adds', () => expect(sum(1,2)).toBe(3));
// playwright.config.ts
// test('home page', async ({ page }) => { await page.goto('/'); await expect(page).toHaveTitle(/SKL/); });
                3.16 Фреймворки: React и Vue
Фреймворки упрощают построение сложных интерфейсов за счёт компонентов и реактивности.
React
import { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
                Vue
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
  <button @click="count++">{{ count }}</button>
</template>
                Лучшие практики: компонентная архитектура, состояние как источник истины, типизация (TS), тесты, линтеры (ESLint/Prettier), разделение кода.
3.17 Best practices
- Прогрессивное улучшение: базовая функциональность без JS, улучшения поверх.
 - Доступность по умолчанию: семантика, фокус‑индикаторы, контраст, клавиатурная навигация.
 - Производительность: бюджет по размерам, lazy‑loading, code‑splitting, критический CSS, кэширование.
 - Безопасность: CSP, избегать 
innerHTML, валидация на клиенте и сервере, sanitize. - Код‑стайл: ESLint + Prettier, единый формат, meaningful‑имена, маленькие функции.
 - Типизация: TypeScript для масштабируемости и снижения числа ошибок.
 - Git‑практики: маленькие коммиты, осмысленные сообщения, PR‑ревью, CI.
 - Тесты: пирамида тестирования: unit > integration > e2e, покрытия критических путей.
 - Мониторинг: логирование ошибок (Sentry), метрики Web Vitals, профилирование.
 
Итоговый проект модуля 3
Добавьте интерактивность на сайт‑портфолио: мобильное меню, слайдер галереи, рабочая форма с валидацией, тёмная тема с сохранением в localStorage.