Files
Web-Lab-1/docs/theory.md
T
2026-10-20 12:58:34 +03:00

66 KiB
Raw Blame History

Теоретические вопросы к лабораторной работе №1

Ответы на контрольные вопросы. Примеры по возможности взяты из собственной работы (index.html, script.js) — так проще отвечать на защите.


1. Протокол HTTP

HTTP (HyperText Transfer Protocol) — прикладной текстовый протокол клиент-серверного взаимодействия, работающий поверх TCP (порт 80) или TLS/TCP (HTTPS, порт 443). Работает по схеме «запрос — ответ»: клиент всегда инициирует обмен, сервер отвечает.

Ключевое свойство — отсутствие состояния (stateless). Сервер не обязан помнить предыдущие запросы клиента; каждый запрос самодостаточен. Состояние надстраивается сверху: cookies, сессии, токены, а на клиенте — localStorage (как в этой работе).

Версии: HTTP/1.1 (текстовый, keep-alive соединения), HTTP/2 (бинарные кадры, мультиплексирование нескольких запросов в одном соединении, сжатие заголовков HPACK), HTTP/3 (поверх QUIC/UDP).

Структура запроса

МЕТОД URI ВЕРСИЯ            ← строка запроса (start line)
Заголовок: значение         ← заголовки, по одному на строку
Заголовок: значение
                            ← ПУСТАЯ строка — обязательный разделитель
тело запроса                ← необязательно (есть у POST/PUT/PATCH)

Пример:

POST /check-point HTTP/1.1
Host: se.ifmo.ru
Content-Type: application/x-www-form-urlencoded
Content-Length: 24
User-Agent: Mozilla/5.0

xval=1&yval=2.5&rval=3

Структура ответа

ВЕРСИЯ КОД ПОЯСНЕНИЕ        ← строка статуса
Заголовок: значение
                            ← пустая строка
тело ответа

Пример:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1024

<!DOCTYPE html>...

Методы запросов

Метод Назначение Безопасный Идемпотентный Тело
GET Получить ресурс да да нет
POST Отправить данные, создать ресурс нет нет да
PUT Полностью заменить ресурс нет да да
PATCH Частично изменить ресурс нет нет да
DELETE Удалить ресурс нет да необяз.
HEAD Как GET, но только заголовки да да нет
OPTIONS Узнать поддерживаемые методы (CORS preflight) да да нет
TRACE Диагностика — вернуть запрос как есть да да нет

Два свойства, которые обязательно спросят:

  • Безопасный (safe) — не изменяет состояние сервера, только читает.
  • Идемпотентный — повторный одинаковый запрос даёт тот же результат. DELETE /point/5 можно послать трижды — точка всё равно одна удалённая. А три POST создадут три записи, поэтому POST не идемпотентен — отсюда предупреждение браузера «повторить отправку формы?» при обновлении страницы.

Коды ответов сервера

Пять классов по первой цифре:

Класс Смысл
1xx Информационные — запрос принят, обработка продолжается
2xx Успех
3xx Перенаправление
4xx Ошибка клиента
5xx Ошибка сервера

Конкретные коды, которые нужно знать:

Код Название Когда
100 Continue Сервер готов принять тело
101 Switching Protocols Переход на WebSocket
200 OK Успех, тело содержит результат
201 Created Ресурс создан (ответ на POST/PUT)
204 No Content Успех, тела нет
301 Moved Permanently Переехало навсегда, обновите ссылки
302 Found Временное перенаправление
304 Not Modified Ресурс не изменился, берите из кэша
400 Bad Request Синтаксически некорректный запрос
401 Unauthorized Не аутентифицирован (нужно войти)
403 Forbidden Аутентифицирован, но нет прав
404 Not Found Ресурс не найден
405 Method Not Allowed Метод не поддерживается этим URI
409 Conflict Конфликт состояния
418 I'm a teapot Шутка из RFC 2324
422 Unprocessable Entity Синтаксис верен, но данные не прошли валидацию
429 Too Many Requests Превышен лимит частоты
500 Internal Server Error Необработанное исключение на сервере
501 Not Implemented Метод не реализован
502 Bad Gateway Прокси получил некорректный ответ от бэкенда
503 Service Unavailable Сервер перегружен или на обслуживании
504 Gateway Timeout Прокси не дождался ответа

Разница 401 и 403 — частый вопрос: 401 «я тебя не знаю, представься», 403 «я тебя знаю, но тебе сюда нельзя».

Заголовки

Заголовки — это метаданные в формате Имя: значение. Имена регистронезависимы. Делятся на четыре группы: общие, запроса, ответа и сущности (описывающие тело).

Заголовки запроса:

Заголовок Смысл
Host Домен — обязателен в HTTP/1.1, позволяет виртуальный хостинг
User-Agent Клиент: браузер, ОС, версия
Accept Какие MIME-типы клиент готов принять
Accept-Language Предпочитаемые языки (ru-RU,ru;q=0.9)
Accept-Encoding Поддерживаемое сжатие (gzip, br)
Cookie Cookies, ранее установленные сервером
Authorization Данные аутентификации (Bearer <token>)
Referer Страница, с которой пришёл запрос
If-Modified-Since Условный запрос — отдай, только если изменилось
Content-Type Тип тела запроса
Content-Length Длина тела в байтах
Connection keep-alive / close
Origin Источник запроса, используется в CORS

Заголовки ответа:

Заголовок Смысл
Content-Type MIME-тип и кодировка тела (text/html; charset=UTF-8)
Content-Length Длина тела
Content-Encoding Применённое сжатие
Server ПО сервера (nginx/1.24)
Set-Cookie Установить cookie у клиента
Location Куда перенаправить (с кодами 3xx)
Cache-Control Правила кэширования (no-cache, max-age=3600)
ETag Версия-отпечаток ресурса для условных запросов
Last-Modified Дата последнего изменения
Access-Control-Allow-Origin CORS: кому разрешён кросс-доменный доступ
Content-Disposition attachment; filename="f.pdf" — скачать как файл
WWW-Authenticate Метод аутентификации (вместе с 401)

В данной работе HTTP-обмена фактически нет: страница статическая, а отправка формы перехватывается e.preventDefault(), и всё считается на клиенте. Браузер делает лишь GET-запросы за index.html, script.js и assets/oscar.png.


2. Язык разметки HTML

HTML (HyperText Markup Language) — язык разметки, а не программирования: он описывает структуру и смысл документа, но не содержит алгоритмов. Актуальная версия — HTML5 (living standard WHATWG).

Особенности

  • Декларативность — описывается что это, а не как это сделать.
  • Теговая структура — элементы образуют дерево вложенности.
  • Устойчивость к ошибкам — браузер не падает на некорректной разметке, а исправляет её по стандартным правилам восстановления. В отличие от XHTML, который требовал строгой валидности.
  • Семантичность — HTML5 ввёл теги со смыслом (<header>, <article>, <nav>), важные для SEO и программ чтения с экрана.
  • Регистронезависимость имён тегов и атрибутов.
  • Разделение ответственности — структура в HTML, оформление в CSS, поведение в JS.

Синтаксис элемента

<тег атрибут="значение">содержимое</тег>
<img src="x.png" alt="описание">     <!-- одиночный (void) элемент -->
<!-- комментарий -->

Элементы делятся на блочные (div, p, table — занимают всю ширину, начинаются с новой строки) и строчные (span, a, b — только по содержимому). Это поведение по умолчанию, в CSS меняется через display.

Основные теги

Структура документа:

Тег Назначение
<!DOCTYPE html> Объявление типа документа, включает стандартный режим
<html> Корневой элемент
<head> Метаданные, не отображаются
<body> Видимое содержимое
<meta> Кодировка, viewport, описание
<title> Заголовок окна/вкладки
<link> Подключение внешних ресурсов (CSS, favicon)
<style> Встроенные стили
<script> JavaScript

Текст и блоки: <h1>–<h6> (заголовки), <p>, <br>, <hr>, <b>/<strong>, <i>/<em>, <span>, <div>, <pre>, <code>.

<b> и <i> задают только начертание, <strong> и <em> несут смысл важности и акцента — их озвучивают скринридеры.

Списки: <ul> (маркированный), <ol> (нумерованный), <li> (элемент), <dl>/<dt>/<dd> (список определений).

Ссылки и медиа: <a>, <img>, <video>, <audio>, <canvas>, <svg>, <iframe>.

Таблицы: <table>, <caption>, <thead>, <tbody>, <tfoot>, <tr>, <th> (ячейка-заголовок), <td> (ячейка данных).

Формы: <form>, <input>, <textarea>, <select>/<option>, <button>, <label>, <fieldset>/<legend>.

Семантические теги HTML5: <header>, <footer>, <nav>, <main>, <section>, <article>, <aside>, <figure>/<figcaption>.

Атрибуты

Глобальные — применимы к любому тегу:

Атрибут Назначение
id Уникальный идентификатор элемента на странице
class Список классов через пробел, для CSS и JS
style Инлайновые стили (нежелательно)
title Всплывающая подсказка
lang Язык содержимого
hidden Скрыть элемент
data-* Произвольные пользовательские данные
tabindex Порядок обхода по Tab

Специфичные для тегов:

  • <a href target rel> — адрес, окно открытия, отношение
  • <img src alt width height> — alt обязателен для доступности
  • <input type name value placeholder required disabled checked>
  • <form action method enctype>
  • <td colspan rowspan>
  • <canvas width height>
  • <script src defer async>

Пример из работы — атрибут data-val на кнопках выбора R:

<button type="button" class="r-button" data-val="3">3</button>

Читается из JS: btn.getAttribute('data-val'). Это стандартный способ привязать данные к элементу, не нарушая валидность разметки.


3. Структура HTML-страницы и DOM

Структура страницы

<!DOCTYPE html>                      <!-- тип документа -->
<html lang="ru">                     <!-- корень, язык -->
<head>
    <meta charset="UTF-8">           <!-- кодировка — первой! -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Лабораторная работа №1</title>
    <style> /* ... */ </style>
</head>
<body>
    <!-- видимое содержимое -->
    <script src="script.js"></script>
</body>
</html>

Почему именно так:

  • <!DOCTYPE html> включает стандартный режим отображения. Без него браузер уходит в quirks mode и имитирует баги старого IE.
  • <meta charset> должен идти в первых 1024 байтах, иначе браузер начнёт разбирать страницу в неверной кодировке и русский текст превратится в мусор.
  • <meta viewport> обязателен для адаптивности на мобильных устройствах.
  • <script> в конце <body> — чтобы к моменту выполнения весь DOM уже существовал. Альтернатива — <script defer> в <head>.

DOM (Document Object Model)

DOM — программный интерфейс документа: браузер разбирает (парсит) HTML и строит в памяти дерево объектов, где каждый тег, текст, атрибут и комментарий становится узлом (Node). Это дерево доступно из JavaScript для чтения и изменения, причём изменения немедленно отражаются на экране.

Важно: DOM строится из HTML, но не равен ему. Браузер исправляет ошибки разметки, достраивает пропущенные <tbody>, а JS может добавить узлы, которых в исходном файле не было вовсе.

Типы узлов: Document (корень), Element (тег), Text (текст), Comment, Attr, DocumentFragment.

Фрагмент дерева этой работы:

document
└── html
    ├── head
    │   ├── meta, title
    │   └── style
    └── body
        ├── table#layout-table
        │   └── tbody
        │       ├── tr → td#header
        │       ├── tr → td.canvas-cell → canvas#graphCanvas
        │       │     td.content-cell → form#point-form → table → ...
        │       └── tr → td.content-cell → table#results-table → tbody#results-body
        ├── img#oscar-easter-egg
        └── script

Работа с DOM из JavaScript

Поиск узлов:

document.getElementById('graphCanvas');            // по id, один элемент
document.querySelector('.r-button');               // первый по CSS-селектору
document.querySelectorAll('input[name="xval"]');   // все по селектору
document.getElementsByClassName('r-button');       // живая коллекция
document.getElementsByTagName('td');

querySelectorAll возвращает статический NodeList — снимок на момент вызова. getElementsBy* возвращают живые коллекции, которые сами обновляются при изменении DOM.

Навигация: parentNode, children, firstElementChild, nextElementSibling.

Изменение:

el.textContent = 'текст';                  // текст, безопасно
el.innerHTML = '<td>1</td>';               // парсится как HTML — риск XSS
el.setAttribute('data-x', '1');
el.classList.add('active');
el.style.color = 'red';

const tr = document.createElement('tr');   // создать узел
parent.appendChild(tr);                    // вставить
el.remove();                               // удалить

События — третья функция DOM помимо структуры и изменения:

el.addEventListener('click', (e) => { /* ... */ });

Событие проходит три фазы: погружение (capturing) от document к цели, цель (target), всплытие (bubbling) обратно наверх. По умолчанию обработчики срабатывают на всплытии. Отсюда приём делегирования: один обработчик на родителе обслуживает все дочерние элементы, включая добавленные позже.

Пример из работы — построение строк таблицы истории:

function renderHistory() {
    resultsBody.innerHTML = '';                      // очистить поддерево
    [...history].reverse().forEach(rec => {
        const tr = document.createElement('tr');     // создать узел
        tr.innerHTML = `<td>${rec.x}</td>...`;       // заполнить
        resultsBody.appendChild(tr);                 // вставить в дерево
    });
}

Событие DOMContentLoaded означает «HTML разобран, дерево DOM построено» (в отличие от load, который ждёт ещё и картинки со стилями). В работе на нём выполняется первичная отрисовка:

window.addEventListener('DOMContentLoaded', () => {
    renderHistory();
    drawGraph();
    validateForm();
});

4. HTML-формы

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

Тег <form> и его атрибуты

<form action="/submit" method="post" enctype="multipart/form-data">
Атрибут Назначение
action URL обработчика. Пустой или отсутствует — текущая страница
method HTTP-метод: get (по умолчанию) или post
enctype Кодирование тела при POST
name Имя формы
target Где открыть ответ (_blank, _self)
novalidate Отключить встроенную браузерную валидацию
autocomplete on/off — автозаполнение

Задание метода HTTP-запроса

Метод задаётся атрибутом method:

<form method="get">   <!-- данные в URL -->
<form method="post">  <!-- данные в теле запроса -->

method="get" — данные формы кодируются в строку запроса URL:

GET /search?xval=1&yval=2.5&rval=3 HTTP/1.1
  • Запрос виден в адресной строке, попадает в историю браузера и логи прокси.
  • Длина URL ограничена (практически ~2000 символов).
  • Можно сохранить в закладки и переслать ссылкой.
  • Кэшируется, идемпотентен.
  • Применение: поиск, фильтры, пагинация — всё, что только читает.

method="post" — данные передаются в теле запроса:

POST /submit HTTP/1.1
Content-Type: application/x-www-form-urlencoded

xval=1&yval=2.5&rval=3
  • Данных может быть сколько угодно, включая файлы.
  • В URL не видны (но не зашифрованы — защищает только HTTPS).
  • Не кэшируется, не идемпотентен — обновление страницы вызывает предупреждение о повторной отправке.
  • Применение: регистрация, авторизация, создание и изменение данных.

Значения enctype для POST:

Значение Когда
application/x-www-form-urlencoded По умолчанию, key=value&key=value
multipart/form-data Обязательно при загрузке файлов
text/plain Только для отладки

Методы PUT/DELETE HTML-формами не поддерживаются — только через JavaScript (fetch, XMLHttpRequest).

Правила размещения форм на страницах

  1. Формы нельзя вкладывать друг в друга — <form> внутри <form> недопустим, браузер проигнорирует внутреннюю.
  2. Форм на странице может быть несколько, но они независимы: отправляется только та, в которой произошёл submit.
  3. Поля должны находиться внутри <form>, иначе не попадут в отправку. Исключение HTML5 — атрибут form="id-формы" позволяет связать поле, лежащее вне формы.
  4. Передаются только поля с атрибутом name. Поле без name не отправляется, даже если заполнено. id для отправки не используется — он нужен CSS и JS.
  5. Поля с disabled не отправляются; с readonly — отправляются.
  6. Невыбранные чекбоксы и радиокнопки не отправляются вообще (а не как пустое значение) — обработчик должен считать отсутствие ключа ответом «нет».
  7. Радиокнопки одной группы обязаны иметь одинаковый name — именно это делает выбор взаимоисключающим.
  8. <form> — блочный элемент; внутри таблицы его следует размещать целиком внутри <td>, а не между <tr>, иначе разметка невалидна.
  9. Каждое поле должно иметь <label for="id"> — это и доступность (скринридеры), и удобство (клик по подписи активирует поле).
  10. Кнопка <button> внутри формы по умолчанию имеет type="submit". Чтобы кнопка не отправляла форму, нужен явный type="button".
  11. Клиентская валидация не отменяет серверную: любые проверки в браузере обходятся через DevTools или прямым HTTP-запросом.

Виды полей ввода

<input> различается атрибутом type:

type Описание
text Однострочный текст
password Текст со скрытыми символами
number Число со счётчиком, атрибуты min, max, step
email С встроенной проверкой формата
tel, url Телефон, адрес
search Поле поиска
checkbox Флажок — независимый множественный выбор
radio Радиокнопка — выбор одного из группы с общим name
file Выбор файла, требует enctype="multipart/form-data"
date, time, datetime-local, month, week Выбор даты и времени
range Ползунок
color Выбор цвета
hidden Скрытое поле — не видно, но отправляется
submit Кнопка отправки
reset Кнопка сброса значений
button Кнопка без действия по умолчанию
image Кнопка-картинка

Другие элементы ввода:

  • <textarea rows cols> — многострочный текст
  • <select> + <option> — выпадающий список; с multiple — множественный выбор; <optgroup> группирует варианты
  • <button> — кнопка с произвольным HTML внутри
  • <datalist> — список подсказок для input
  • <output>, <progress>, <meter> — вывод значений

Атрибуты валидации HTML5: required, min/max, minlength/maxlength, pattern (регулярное выражение), step.

Форма в данной работе

<form id="point-form">
    <!-- X: чекбоксы — множественный выбор -->
    <input type="checkbox" name="xval" value="-2"> -2
    ...
    <!-- Y: текстовое поле -->
    <input type="text" id="y-input" name="yval" placeholder="-3 ... 5">
    <!-- R: кнопки + скрытое поле -->
    <button type="button" class="r-button" data-val="1">1</button>
    <input type="hidden" id="r-hidden" name="rval" value="">
    <button type="submit" id="submit-btn" disabled>Проверить попадание</button>
</form>

Разбор решений:

  • Типы полей заданы вариантом: X — checkbox, Y — text, R — button.

  • У <form> нет action и method, потому что отправка перехватывается и обрабатывается на клиенте:

    form.addEventListener('submit', (e) => {
        e.preventDefault();   // отменить стандартную отправку и перезагрузку
        ...
    });
    

    Без preventDefault() браузер выполнил бы GET на ту же страницу, и всё состояние было бы потеряно.

  • Кнопки R имеют type="button" — иначе каждое нажатие отправляло бы форму.

  • Выбранный R хранится в <input type="hidden">: кнопки сами по себе значение не хранят, а скрытое поле — штатный способ держать его в форме.

  • Чекбоксы X дают множественный выбор, поэтому один submit порождает столько записей, сколько галочек поставлено:

    checkboxes.forEach(cb => {
        if (cb.checked) { history.push({...}); }
    });
    
  • submitBtn.disabled = !isValid — кнопка заблокирована, пока данные некорректны, а сообщения выводятся в <span class="error"> рядом с полем, а не через alert (это прямо указано в списке частых ошибок).


5. Каскадные таблицы стилей (CSS)

CSS (Cascading Style Sheets) — язык описания внешнего вида документа. Отделяет оформление от структуры: HTML отвечает за смысл, CSS — за вид.

Способы подключения

  1. Внешний файл — <link rel="stylesheet" href="style.css">; кэшируется, переиспользуется между страницами.
  2. Встроенная таблица — <style> в <head>. Используется в этой работе, так как требование п.2 задания — «таблицы стилей должны располагаться в самом веб-документе».
  3. Инлайновый стиль — атрибут style="..." у тега. Нежелателен.

Структура правила

селектор {
    свойство: значение;   /* это объявление */
    свойство: значение;
}

Селектор + блок объявлений = правило. Пример из работы:

#header {
    background-color: #eeeeee;
    color: #000000;
    padding: 10px;
    text-align: center;
    font-family: monospace;
    font-size: 16px;
}

Виды селекторов

Вид Синтаксис Что выбирает
Универсальный * Все элементы
Элемента (тега) td Все <td>
Класса .r-button Все с class="r-button"
Идентификатора #header Элемент с id="header"
Атрибута input[type="text"] По наличию/значению атрибута
Группировка table, th, td Каждый из перечисленных

Комбинаторы (отношения между элементами):

Комбинатор Синтаксис Что выбирает
Вложенный (потомка) A B Все B внутри A на любой глубине
Дочерний A > B Только непосредственных детей A
Соседний A + B Первый B сразу после A
Последующий A ~ B Все B после A на том же уровне

Разница потомка и дочернего — обязательный вопрос: #point-form td выберет и <td> во вложенной таблице подсказок, а #point-form > table > tbody > tr > td — только ячейки верхнего уровня.

Псевдоклассы — состояние элемента: :hover, :focus, :active, :visited, :checked, :disabled, :first-child, :last-child, :nth-child(2n), :not(.x).

Псевдоэлементы — виртуальные части: ::before, ::after, ::first-line, ::placeholder.

Особенности применения

  • Классы — для стилей, повторяющихся у многих элементов. Основной рабочий инструмент.
  • Идентификаторы — для уникальных элементов. Злоупотреблять не стоит: высокая специфичность мешает переопределению.
  • Селекторы тегов — для базовых правил на весь документ (body, сброс отступов).
  • Чем короче и менее специфичен селектор, тем легче поддерживать код. Длинные цепочки вроде div#main table.data tbody tr td span делают стили хрупкими.
  • Браузер читает селекторы справа налево, поэтому последняя часть должна быть как можно более конкретной.

Все четыре требуемых видов селекторов в работе присутствуют:

body { ... }                        /* селектор элемента */
.content-cell { ... }               /* селектор класса */
#layout-table { ... }               /* селектор идентификатора */
#point-form label { ... }           /* потомка */
td.canvas-cell > * { ... }          /* дочерних элементов */
.content-cell[colspan="2"] > h3 { } /* атрибута + дочерний */
#point-form input[type="text"] { }  /* тег + атрибут */
.r-button.active { ... }            /* два класса на одном элементе */

Приоритеты правил

Когда к одному элементу применимы несколько правил, конфликт решается каскадом — по шагам, в строгом порядке:

1. Происхождение и важность (от низшего к высшему):

  1. Стили браузера по умолчанию
  2. Стили пользователя
  3. Стили автора (<style>, <link>, style="")
  4. Стили автора с !important
  5. Стили пользователя с !important

2. Специфичность — считается как тройка (A, B, C):

Разряд Что считается Вес
A Идентификаторы #id 100
B Классы, атрибуты, псевдоклассы 10
C Теги и псевдоэлементы 1

Инлайновый style="" стоит выше любого селектора (условно 1000), !important — выше всего, кроме пользовательского !important. Универсальный селектор * и :not() сами по себе веса не добавляют.

Примеры подсчёта:

Селектор Специфичность Вес
td 0,0,1 1
.content-cell 0,1,0 10
td.canvas-cell 0,1,1 11
.r-button.active 0,2,0 20
#point-form td 1,0,1 101
style="..." — 1000

3. Порядок следования — при равной специфичности побеждает правило, объявленное позже. Именно поэтому .r-button.active в работе написано после .r-button — хотя здесь и специфичность выше.

Наследование

Часть свойств передаётся от родителя к детям автоматически: color, font-family, font-size, font-weight, line-height, text-align, visibility. Не наследуются свойства блочной модели: margin, padding, border, background, width, height.

Пример из работы — наследование и каскадирование:

body {
    font-family: Arial, sans-serif;   /* наследуется всеми потомками */
    color: #000000;
}
#header {
    color: #000000;     /* комментарий в коде: «Каскадирование» */
    font-family: monospace;  /* переопределяет унаследованный Arial */
}
#header span.variant {
    color: #cc0000;     /* переопределяет на уровень глубже */
}

<b> внутри #header получает monospace по наследству, а span.variant перебивает цвет более специфичным правилом.

Преимущества CSS перед атрибутом style

  1. Разделение ответственности — структура отдельно, оформление отдельно. HTML остаётся читаемым и семантичным.
  2. Переиспользование — одно правило .r-button оформляет все пять кнопок. С style пришлось бы дублировать код пять раз.
  3. Поддерживаемость — чтобы изменить вид всех кнопок, правится одна строка, а не N тегов. Меньше кода — меньше шансов пропустить место.
  4. Размер и кэширование — внешний CSS-файл загружается один раз и кэшируется для всех страниц сайта; инлайновые стили заново передаются с каждой страницей.
  5. Каскад и наследование работают — инлайновый стиль их фактически отключает, он всегда главнее и не переопределяется ничем, кроме !important.
  6. Полная функциональность — через style невозможны псевдоклассы (:hover, :focus), псевдоэлементы (::before), медиазапросы (@media), анимации (@keyframes).
  7. Адаптивность — медиазапросы для разных размеров экрана доступны только в таблицах стилей.
  8. Доступность — пользователь может подключить свою таблицу стилей (крупный шрифт, высокий контраст); инлайновые стили её перебьют.
  9. Отладка — DevTools показывает всю цепочку правил и причину победы одного над другим.

6. LESS, Sass, SCSS

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

Зачем они появились

Чистый CSS долго не имел переменных, средств повторного использования и структурирования. В больших проектах это приводило к дублированию, длинным повторяющимся селекторам и трудно поддерживаемым файлам.

Ключевые особенности (общие)

Возможность Описание
Переменные Именованные значения: цвета, размеры, шрифты в одном месте
Вложенность Селекторы внутри селекторов повторяют структуру HTML
Миксины Переиспользуемые блоки объявлений, в т.ч. с параметрами
Наследование @extend — один селектор перенимает правила другого
Операции Арифметика: 10px * 2, работа с цветами
Функции Встроенные (darken, lighten) и свои
Импорт Разбиение на файлы, объединяемые при компиляции без лишних HTTP-запросов
Условия и циклы @if, @for, @each — у Sass/SCSS развиты сильнее

Sass и SCSS

Sass (Syntactically Awesome Style Sheets, 2006) — написан на Ruby, сейчас реализация Dart Sass. Имеет два синтаксиса:

Sass (indented, .sass) — отступы вместо фигурных скобок, без точек с запятой:

$primary: #3399FF
.button
  background: $primary
  padding: 5px 10px

SCSS (Sassy CSS, .scss) — синтаксис, надмножество CSS: любой валидный CSS является валидным SCSS. Сейчас основной вариант.

$primary: #3399FF;
$padding: 5px;

@mixin button-base($bg) {
    background-color: $bg;
    padding: $padding ($padding * 2);
    cursor: pointer;
}

.r-button {
    @include button-base(#dddddd);
    border: 1px solid #888888;

    &.active {                  // & — ссылка на родительский селектор
        @include button-base(#888888);
        color: #ffffff;
    }

    &:hover { background-color: darken(#dddddd, 10%); }
}

Компилируется в:

.r-button { background-color: #dddddd; padding: 5px 10px; cursor: pointer;
            border: 1px solid #888888; }
.r-button.active { background-color: #888888; padding: 5px 10px;
                   cursor: pointer; color: #fff; }
.r-button:hover { background-color: #c4c4c4; }

LESS (Leaner Style Sheets, 2009) — написан на JavaScript, работает под Node.js, может выполняться и в браузере. Синтаксис ближе к CSS; переменные через @:

@primary: #3399FF;
@padding: 5px;

.button-base(@bg) {
    background-color: @bg;
    padding: @padding (@padding * 2);
}

.r-button {
    .button-base(#dddddd);
    &.active { .button-base(#888888); color: #ffffff; }
}

Сравнительная характеристика

Критерий Sass/SCSS LESS
Год 2006 2009
Реализация Dart (ранее Ruby) JavaScript (Node.js)
Синтаксис переменных $var @var
Синтаксисы Два: .sass и .scss Один: .less
Миксины @mixin / @include — явные Любой класс может быть миксином
Логика Полноценные @if, @for, @each, @while, свои функции Ограниченная: guard-выражения, рекурсия
Модульность @use / @forward — пространства имён @import
Выполнение в браузере Нет Да (less.js, только для разработки)
Экосистема Крупнее: Bootstrap 4+, Foundation Bootstrap 3, Semantic UI
Порог входа Чуть выше Ниже

Коротко: SCSS мощнее и является отраслевым стандартом; LESS проще и ближе к чистому CSS. Конфликт @ в LESS с директивами CSS (@media, @import) — известное неудобство.

Существует также Stylus (гибкий синтаксис, необязательные скобки и двоеточия) и PostCSS — не препроцессор, а инструмент преобразования CSS плагинами (autoprefixer, postcss-preset-env).

Совместимость с браузерами и трансляция

Это ключевой момент: браузеры не понимают LESS, Sass и SCSS вообще. Браузер умеет только CSS. Поэтому исходники компилируются в обычный CSS до отправки клиенту:

style.scss  ──[компилятор]──►  style.css  ──►  <link rel="stylesheet">

Отсюда следствия:

  • Совместимость с браузерами — стопроцентная, потому что на выходе получается обычный CSS. Препроцессор не создаёт проблем поддержки сам по себе; ограничения задают только те CSS-свойства, которые вы используете.
  • Вся обработка происходит на этапе сборки — во время выполнения накладных расходов нет.
  • Для отладки генерируются source maps: DevTools показывает строку исходного .scss, а не скомпилированного .css.

Способы трансляции:

sass style.scss style.css              # Dart Sass, разово
sass --watch src:dist                  # автоматически при изменениях
lessc style.less style.css             # LESS

Также: сборщики (Webpack с sass-loader, Vite, Gulp), встроенная поддержка в IDE, плагины фреймворков.

Современная оговорка. Часть возможностей препроцессоров уже появилась в самом CSS: кастомные свойства (--main-color: #333; color: var(--main-color)) — они, в отличие от переменных препроцессора, живут во время выполнения и меняются из JS; функция calc(); встроенная вложенность селекторов (поддерживается современными браузерами с 2023 г.). Поэтому необходимость в препроцессорах снизилась, но миксины, циклы, модульность и функции по-прежнему остаются их преимуществом.

В данной работе препроцессоры не применяются: по заданию стили должны быть в самом документе внутри <style>, объём невелик, и подключение сборки стало бы «использованием микроскопа для забивания гвоздей».


7. Клиентские сценарии

Клиентский сценарий (client-side script) — программный код, который передаётся вместе со страницей и выполняется в браузере пользователя, а не на сервере. Противоположность — серверные сценарии (PHP, Java, Python), исполняемые до отправки ответа.

Особенности

Сильные стороны:

  • Мгновенная реакция без обращения к серверу — нет задержки сети.
  • Снижение нагрузки на сервер и трафика — вычисления переносятся на устройство клиента.
  • Полный доступ к DOM и браузерным API — можно менять страницу без перезагрузки, рисовать на Canvas, читать геолокацию, хранить данные.
  • Интерактивность — анимации, drag&drop, динамические интерфейсы, SPA.

Ограничения и риски:

  • Код полностью открыт пользователю. Исходник виден в DevTools; минификация лишь затрудняет чтение. Никаких секретов, ключей и паролей в клиентском коде быть не может.
  • Клиентскую валидацию невозможно считать защитой. Пользователь может отключить JS, изменить код в DevTools или послать запрос напрямую через curl. Поэтому серверная валидация обязательна всегда — клиентская нужна лишь для удобства и скорости отклика.
  • Зависимость от среды — разные браузеры, версии, устройства; нужны полифиллы и транспиляция.
  • JS может быть отключён — отсюда принцип постепенного улучшения (progressive enhancement): базовая функциональность должна работать без JS.
  • Производительность ограничена устройством клиента; тяжёлые вычисления блокируют единственный поток и «подвешивают» интерфейс.
  • Безопасность — уязвимости XSS, когда чужой скрипт исполняется в контексте страницы.

Сферы применения

Сфера Примеры
Валидация форм Проверка значений до отправки
Динамический интерфейс Табы, модальные окна, аккордеоны, подсказки
Асинхронный обмен данными AJAX, fetch — обновление части страницы
Графика и визуализация Canvas, SVG, WebGL, графики и диаграммы
Анимация Переходы, эффекты, параллакс
Локальное хранение localStorage, sessionStorage, IndexedDB
SPA и фреймворки React, Vue, Angular
Работа с медиа Плееры, обработка изображений
Реальное время WebSocket, чаты, уведомления
Устройство пользователя Геолокация, камера, датчики, уведомления
Офлайн-режим Service Workers, PWA
Аналитика Сбор поведенческих метрик

Язык JavaScript

JavaScript — высокоуровневый, динамический, интерпретируемый язык программирования. Создан Бренданом Айком в Netscape в 1995 году за десять дней; изначально назывался Mocha, затем LiveScript. Имя «JavaScript» — маркетинговый ход: к языку Java отношения не имеет.

Стандартизован как ECMAScript (ECMA-262). JavaScript — его наиболее известная реализация.

Характеристики языка:

Свойство Пояснение
Динамическая типизация Тип определяется во время выполнения, переменная может менять тип
Слабая типизация Автоматическое приведение типов ('2' == 2 → true)
Интерпретируемый + JIT Движки (V8, SpiderMonkey, JavaScriptCore) компилируют «на лету»
Многопарадигменный Императивный, объектно-ориентированный, функциональный
Прототипное наследование Объекты наследуют от объектов, а не от классов; class — синтаксический сахар
Функции — объекты первого класса Передаются как аргументы, возвращаются, замыкания
Однопоточность Один поток выполнения, event loop, асинхронность без блокировки
Автоматическая сборка мусора Память освобождается автоматически

Где исполняется: браузер (основная среда), сервер (Node.js, Deno, Bun), мобильные приложения (React Native), десктоп (Electron), встраиваемые системы.

Пример из данной работы. Все три характерные сферы клиентских сценариев задействованы:

// 1. Валидация ввода без обращения к серверу
function validateY(showErrors = false) {
    const valStr = yInput.value.trim().replace(',', '.');
    if (valStr === '') { /* сообщение об ошибке */ return false; }
    const y = Number(valStr);
    if (isNaN(y)) { return false; }
    if (y < -3 || y > 5) { return false; }
    return true;
}

// 2. Графика на Canvas
ctx.arc(center, center, R_PIXELS, -Math.PI / 2, 0, false);

// 3. Локальное хранение между перезагрузками
localStorage.setItem(STORAGE_KEY, JSON.stringify(history));

Показательно, что данные из localStorage в работе перепроверяются при загрузке:

history = rawData.filter(rec => {
    if (![1, 2, 3, 4, 5].includes(rec.r)) return false;
    ...
});

Это иллюстрация главного ограничения клиентских сценариев: всё, что доступно клиенту, — недоверенные данные, потому что пользователь может их изменить. По той же причине в работе проверяются значения data-val у кнопок R и value у чекбоксов — они редактируются через DevTools.


8. Версии ECMAScript. Возможности ES6 и ES7

ECMAScript — стандарт языка (спецификация ECMA-262), разрабатываемый комитетом TC39 организации ECMA International. JavaScript — реализация этого стандарта.

История версий

Версия Год Главное
ES1 1997 Первая редакция
ES2 1998 Приведение к стандарту ISO
ES3 1999 Регулярные выражения, try/catch, do-while
ES4 — Отменена из-за разногласий, слишком радикальна
ES5 2009 strict mode, JSON, методы массивов (forEach, map, filter, reduce), геттеры/сеттеры, Object.defineProperty
ES5.1 2011 Уточнения
ES6 / ES2015 2015 Крупнейшее обновление языка
ES7 / ES2016 2016 Маленький релиз, начало ежегодного цикла
ES2017 2017 async/await
ES2018 2018 Spread для объектов, for await, Promise.finally
ES2019 2019 flat, trimStart/End, необязательный параметр catch
ES2020 2020 ?., ??, BigInt, Promise.allSettled, динамический import()
ES2021 2021 replaceAll, ??=, Promise.any, числовые разделители
ES2022 2022 Top-level await, приватные поля #, at(), Object.hasOwn
ES2023 2023 findLast, неразрушающие методы массивов (toSorted, with)
ES2024–2025 Object.groupBy, Promise.withResolvers, Array.fromAsync и др.

С 2015 года версии выпускаются ежегодно и называются по году. Отсюда практика называть «современный JS» не версией, а годом.

ES6 (ES2015) — ключевые возможности

1. let и const — блочная область видимости

let x = 1;        // можно переприсвоить
const y = 2;      // нельзя (но содержимое объекта меняется)

До ES6 был только var с областью видимости на всю функцию и всплытием (hoisting), что порождало классический баг с замыканиями в цикле.

2. Стрелочные функции

const sum = (a, b) => a + b;
rButtons.forEach(btn => btn.classList.remove('active'));

Короткий синтаксис, неявный return без фигурных скобок, и главное — не имеют своего this, а берут его из окружающего контекста. Это убрало необходимость в var self = this и .bind(this).

3. Шаблонные строки (template literals)

tr.innerHTML = `<td>${rec.x}</td><td>${rec.y}</td>`;

Обратные кавычки, подстановка ${выражение}, многострочность.

4. Деструктуризация

const { x, y, r } = rec;          // из объекта
const [first, second] = arr;      // из массива
const { x: coordX = 0 } = rec;    // переименование и значение по умолчанию

5. Параметры по умолчанию, rest и spread

function validateY(showErrors = false) { }     // default
function sum(...nums) { }                       // rest — собрать в массив
const copy = [...history];                      // spread — развернуть
Math.max(...arr);

6. Классы — синтаксический сахар над прототипами

class Point {
    constructor(x, y) { this.x = x; this.y = y; }
    hit(r) { return this.x ** 2 + this.y ** 2 <= r ** 2; }
    static zero() { return new Point(0, 0); }
}
class Marked extends Point { /* super(), переопределение */ }

7. Промисы — работа с асинхронностью без «callback hell»

fetch('/api/points')
    .then(res => res.json())
    .then(data => render(data))
    .catch(err => console.error(err));

8. Модули

export function checkHit(x, y, r) { }      // module.js
import { checkHit } from './module.js';

Штатное разделение кода на файлы на уровне языка.

9. Новые структуры данных: Map (ключи любого типа), Set (уникальные значения), WeakMap, WeakSet.

10. Итераторы, for...of и генераторы

for (const rec of history) { }             // по значениям (vs for...in — по ключам)
function* gen() { yield 1; yield 2; }      // генератор

11. Новые методы: Array.from, Array.of, find, findIndex, fill, Object.assign, String.prototype.includes/startsWith/endsWith/repeat, Number.isNaN, Number.isInteger.

12. Прочее: краткая запись свойств объекта ({ x, y } вместо { x: x, y: y }), вычисляемые имена свойств ({ [key]: value }), символы (Symbol), Proxy и Reflect, оператор ** (возведение в степень — формально ES7), хвостовая рекурсия.

ES7 (ES2016) — две возможности

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

1. Оператор возведения в степень **

2 ** 10              // 1024 — вместо Math.pow(2, 10)
x ** 2 + y ** 2 <= r ** 2

Именно в это преобразуется пользовательский ввод в данной работе:

.replace(/\^/g, '**');     // x^2 → x**2

2. Array.prototype.includes

[1, 2, 3, 4, 5].includes(3);       // true
[NaN].includes(NaN);               // true  — а indexOf даёт -1!

Удобнее и честнее, чем arr.indexOf(x) !== -1: возвращает булево значение и корректно находит NaN. Применяется в работе для проверки допустимых значений:

if (![1, 2, 3, 4, 5].includes(rec.r)) return false;
if (![-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2].includes(rec.x)) return false;

Коротко о последующих версиях

  • ES2017: async/await — асинхронный код в синхронном стиле; Object.values, Object.entries, padStart/padEnd.
  • ES2020: опциональная цепочка obj?.a?.b и нулевое слияние a ?? b — обе конструкции сильно сократили защитные проверки.
  • ES2022: приватные поля классов #field, at(-1).

Совместимость и транспиляция

Браузеры поддерживают разные версии стандарта. Решения:

  • Транспиляция (Babel, TypeScript, SWC) — современный код переписывается в ES5 для старых браузеров.
  • Полифиллы (core-js) — реализация отсутствующих методов на старом JS.
  • Browserslist — настройка целевых браузеров, по которой сборщик решает, что преобразовывать.
  • Таблицы поддержки: caniuse.com, compat-table.

В данной работе используются ES6+ конструкции (const/let, стрелочные функции, шаблонные строки, spread, параметры по умолчанию) и ES7 (includes, **). Транспиляция не нужна: все современные браузеры поддерживают их нативно, а сборка усложнила бы проект без выгоды.


Краткая шпаргалка для защиты

Вопрос Ответ одной фразой
HTTP stateless? Да, сервер не помнит клиента; состояние — через cookies/сессии/токены
GET vs POST GET — данные в URL, кэшируется, идемпотентен, для чтения; POST — в теле, для изменений
401 vs 403 401 — не представился; 403 — представился, но нет прав
Идемпотентность Повтор запроса даёт тот же результат; POST — не идемпотентен
HTML — язык программирования? Нет, язык разметки: описывает структуру, не алгоритмы
Зачем DOCTYPE Включает стандартный режим вместо quirks mode
DOM Дерево объектов документа в памяти браузера, доступное из JS
textContent vs innerHTML Первый вставляет текст, второй парсит HTML — риск XSS
Почему <script> в конце body Чтобы DOM был построен; альтернатива — defer
Какие поля отправляются Только с атрибутом name и без disabled
Можно ли вложить формы Нет
Приоритет CSS Важность → специфичность (id=100, класс=10, тег=1) → порядок
A B vs A > B Все потомки на любой глубине vs только прямые дети
Что не наследуется Блочная модель: margin, padding, border, background, width
Зачем CSS вместо style="" Переиспользование, каскад, псевдоклассы, медиазапросы, кэширование
Понимает ли браузер SCSS Нет, компилируется в CSS до отправки; совместимость 100%
Sass vs LESS $ vs @; Dart vs JS; у Sass сильнее логика и модульность
Достаточно ли клиентской валидации Нет, обходится через DevTools; серверная обязательна
ES6 год и главное 2015; let/const, стрелочные, классы, промисы, модули, деструктуризация
ES7 что добавил ** и Array.prototype.includes