JS

Модуль 3: JS

Освоим основы JavaScript, взаимодействие с DOM, обработку событий, работу с формами и хранилищами, а также Fetch API для получения данных.

Введение в 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');

Операторы и условия

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('Гость');
}

Практика

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

Функции

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());

Практика

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

Циклы и массивы

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]);
}

Практика

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

Работа с DOM — Часть 1 (Поиск и изменение)

const title = document.getElementById('title');
const button = document.querySelector('.btn');
button.addEventListener('click', () => {
  title.textContent = 'Новый заголовок';
  title.style.color = '#4f46e5';
});

Практика

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

Работа с 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.

Работа с формами в 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).

Хранение данных на клиенте

// Тема
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));

Практика

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

Асинхронность. 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 и выведите заголовки на страницу.

Современный 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>

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);

Производительность (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">

Безопасность: 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'">

Инструменты: npm, Vite/Webpack, PostCSS, Sass

  • npm: управление зависимостями, команды scripts.
  • Vite: дев‑сервер, HMR, сборка с esbuild/rollup.
  • Webpack: гибкая сборка модулей, код‑сплиттинг.
  • PostCSS/Sass: автопрефиксы, nested‑синтаксис, переменные и миксины.
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

Тестирование: 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/); });

Фреймворки: 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), разделение кода.

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.

Настройки

Цветовая схема

Тема