Post

Срез 2

Изучение теории. Неделя 2

Срез 2

1. GP. Work with backend (AJAX, HTTP requests, headers, etc)

1.1. Протокол HTTPS

HTTPS (HyperText Transfer Protocol Secure) — это защищённая версия HTTP, использующая шифрование для безопасной передачи данных. Он работает поверх TLS/SSL, обеспечивая аутентификацию сервера и шифрование трафика. Когда клиент подключается к серверу, происходит «рукопожатие» (handshake), в ходе которого согласуются параметры шифрования. Это предотвращает перехват и подмену данных. HTTPS использует порт 443 по умолчанию и обязателен для современных веб-сайтов.

1.2. Статус-коды HTTP и их группы

HTTP-статусы — это трёхзначные коды, указывающие на результат запроса. Они делятся на пять групп:

  • 1xx (Информационные) — запрос принят, обработка продолжается
  • 2xx (Успех) — запрос успешно выполнен (например, 200 OK)
  • 3xx (Перенаправление) — требуется дополнительное действие (301 Moved Permanently)
  • 4xx (Ошибка клиента) — запрос содержит ошибку (404 Not Found)
  • 5xx (Ошибка сервера) — сервер не смог обработать запрос (500 Internal Server Error)

1.3. AJAX и XHR

AJAX (Asynchronous JavaScript and XML) — технология асинхронных запросов к серверу без перезагрузки страницы. XHR (XMLHttpRequest) — устаревший API для таких запросов, но всё ещё поддерживается. AJAX позволяет отправлять HTTP-запросы в фоне, получать данные (JSON, XML, HTML) и обновлять интерфейс динамически. Современная альтернатива — fetch().

1.4. JSON и его использование в запросах

JSON (JavaScript Object Notation) — текстовый формат обмена данными, основанный на синтаксисе JavaScript. Он лёгкий, читаемый и поддерживается всеми языками. В HTTP-запросах JSON используется как тело запроса (Content-Type: application/json) или ответа. Например, REST API часто возвращают данные в JSON, которые можно обработать в JavaScript через JSON.parse().

1.5. Использование fetch() для запросов

fetch() — современный API для HTTP-запросов в JavaScript, возвращающий Promise. Пример GET-запроса:

1
2
3
fetch('<https://api.example.com/data>')
  .then(response => response.json())
  .then(data => console.log(data));

Он поддерживает методы (GET, POST и др.), заголовки и обработку ошибок. В отличие от XHR, fetch() более удобен и основан на промисах.

1.6. Методы CRUD HTTP

CRUD (Create, Read, Update, Delete) соответствует HTTP-методам:

  • POST — создание ресурса
  • GET — получение ресурса
  • PUT/PATCH — обновление (полное/частичное)
  • DELETE — удаление

Эти методы используются в REST API для взаимодействия с сервером.

1.7. Принципы REST

REST (Representational State Transfer) — архитектурный стиль для API, основанный на:

  • Статусности клиент-серверного взаимодействия
  • Единообразии интерфейса (HTTP-методы, URL как идентификаторы)
  • Кэшируемости, многоуровневой системе и др.

RESTful API используют JSON/XML и стандартные HTTP-методы для операций с ресурсами.

1.8. Концепция и использование CORS

CORS (Cross-Origin Resource Sharing) — механизм, разрешающий запросы между разными доменами. Браузер блокирует кросс-доменные запросы без заголовка Access-Control-Allow-Origin. Сервер должен явно разрешить такие запросы, иначе возникнет ошибка. CORS критичен для безопасного взаимодействия API с фронтендом.

1.9. Различия между HTTP методами PUT, PATCH и POST

  • POST используется для создания новых ресурсов, например при добавлении нового товара в корзину интернет-магазина
  • PUT применяется для полного замещения ресурса, что идеально подходит для обновления профиля пользователя, где мы заменяем все его данные новыми.
  • PATCH предназначен для частичного обновления отдельных полей ресурса, например при изменении только email адреса без затрагивания других данных профиля

PUT требует всех полей, PATCH — только изменяемых.

1.10. AbortController

AbortController позволяет отменять fetch() или другие асинхронные операции. Пример:

1
2
3
4
const controller = new AbortController();
fetch(url, { signal: controller.signal })
  .catch(err => if (err.name === 'AbortError') console.log('Запрос отменён'));
controller.abort(); // Отмена запроса

Это полезно для прерывания долгих запросов при переходе между страницами.

1.11. Работа с JSON ответами

JSON-ответы обрабатываются через response.json() в fetch():

1
2
3
4
fetch(url)
  .then(response => response.json()) // Парсинг JSON
  .then(data => console.log(data))
  .catch(error => console.error('Ошибка:', error));

Если сервер возвращает невалидный JSON, возникнет ошибка парсинга.

1.12. Установка заголовков в HTTP запросах

Заголовки (headers) задаются в fetch() или XHR. Это может понадобиться для передачи авторизационных данных (Authorization), указания типа контента (Content-Type), управления кешированием (Cache-Control) или для работы с API, требующими специфичные заголовки. Заголовки помогают серверу правильно обработать запрос и вернуть нужный ответ.

1
2
3
4
5
6
fetch(url, {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  }
});

1.13. Обход ограничений CORS

Способы обхода CORS:

  • Настройка сервера (Access-Control-Allow-Origin: *)
  • Прокси-сервер (запросы через свой бэкенд)
  • JSONP (устаревший метод для GET)
  • Отключение CORS в браузере (только для разработки)

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

1.14. Способы установки непрерывного соединения

Для долгоживущих соединений используются:

  • WebSocket — двусторонняя связь в реальном времени
  • Server-Sent Events (SSE) — потоковые сообщения от сервера
  • Long Polling — сервер держит запрос открытым до новых данных

Эти методы полезны для чатов, уведомлений и live-обновлений.

2. GP. Request parameters and JSON

2.1. Клонирование объектов через JSON

1
2
let someObj = {1: 1, 2: 2}
let deepCopy = JSON.parse(JSON.stringify(someObj))

Клонирование объектов через JSON — это простой способ создания глубокой копии объекта в JavaScript. Объект преобразуется в JSON-строку JSON.stringify(), а затем обратно в объект JSON.parse().

Плюсы:

  • Простота
  • Глубокая копия

Минусы:

  • Не копирует функции, undefined, Symbol и циклические ссылки

2.2. Добавление тела запроса в HTTP-запросы

Тело запроса добавляется для методов, которые подразумевают передачу данных, таких как POST, PUT или PATCH. Оно может представлять собой строку, объект FormData, Blob или другие подходящие форматы в зависимости от типа содержимого. При работе с JSON данные предварительно сериализуются через JSON.stringify(), а для форм используется либо URL-кодированная строка, либо объект FormData.

1
2
3
4
5
fetch(url, {
  method: 'POST',
  body: JSON.stringify({ key: 'value' }),
  headers: { 'Content-Type': 'application/json' }
});

Важно: Для JSON обязательно указывать Content-Type: application/json.

2.3. Отправка запросов с различными типами содержимого

HTTP-запросы могут передавать данные в разных форматах, что определяется заголовком Content-Type. Для JSON-данных используется application/json, для форм — application/x-www-form-urlencoded или multipart/form-data, а для бинарных данных — соответствующий MIME-тип.

Выбор типа содержимого влияет на способ сериализации данных и их обработку на сервере. Например, JSON требует явного преобразования объекта в строку, а форматы для загрузки файлов используют специальные объекты вроде FormData. Корректная настройка этого заголовка гарантирует, что сервер сможет правильно интерпретировать переданную информацию.

Пример с FormData:

1
2
3
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch(url, { method: 'POST', body: formData });

2.4. Чтение и обработка ответов от сервера

При работе с HTTP-запросами ответы от сервера требуют корректной обработки, включая проверку статуса и преобразование данных. После получения ответа необходимо сначала убедиться в его успешности через свойство ok или статус-код, затем выбрать подходящий метод для чтения содержимого, например json() для JSON-данных или text() для простого текста. Обработка ошибок реализуется через блок catch или проверку статуса, чтобы избежать проблем с невалидными данными. Современные подходы рекомендуют использовать асинхронные функции с try/catch для более читаемого кода. Важно учитывать, что некоторые методы чтения ответа можно вызвать только один раз, поэтому данные стоит сохранять в переменную при необходимости повторного использования.

1
2
3
4
5
6
7
fetch(url)
  .then(response => {
    if (!response.ok) throw new Error('Ошибка HTTP: ' + response.status);
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error(error));

В файлике у этой темы больше вопросов:

  • Как обработать ответ от сервера с типом содержимого application/xml в приложении на React?
  • Как отправить HTTP-запрос с содержимым типа application/json и обработать ответ от сервера?
  • Каким образом можно отправить HTTP-запрос с содержимым типа ‘application/x-www-form-urlencoded’, и как это отличается от отправки JSON-данных?

Но эти вопросы не указаны в CRM trainee, так что игнорим их.

3. JS. Classes

3.1. Создание классов и базовый синтаксис

В JavaScript классы представляют собой удобный синтаксис для работы с прототипным наследованием. Они объявляются с помощью ключевого слова class и могут содержать конструктор, методы и свойства. Например, простой класс User с методом greet создаётся как функция-конструктор под капотом, но с более понятным синтаксисом. Важно помнить, что классы нельзя вызывать без оператора new, и все методы автоматически добавляются в прототип объекта.

1
2
3
4
5
6
7
8
class User {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello, ${this.name}!`;
  }
}

3.2. Инстанциирование классов и использование конструктора

Когда мы создаём экземпляр класса с помощью new, автоматически вызывается конструктор, который инициализирует объект. В конструкторе через this мы можем задавать начальные значения свойств. Например, при создании объекта user на основе класса User конструктор принимает параметр name и присваивает его свойству экземпляра. Если конструктор не определён явно, JavaScript использует пустой конструктор по умолчанию.

1
2
// new instance of a class User
let someUser = new User("someName")

3.3. Наследование и методы классов

Механизм наследования реализуется через ключевое слово extends, позволяя дочернему классу перенимать функциональность родительского. При этом в конструкторе дочернего класса обязательно нужно вызывать super() для корректной инициализации родительских свойств. Дочерний класс может не только использовать методы родителя, но и переопределять их или добавлять новые. Например, класс Admin, расширяющий User, может вводить дополнительные свойства вроде role и новые методы для администрирования.

1
2
3
4
5
6
7
8
9
class Admin extends User {
  constructor(name, role) {
    super(name); // Вызов родительского конструктора
    this.role = role;
  }
  grantAccess() {
    return `${this.name} (${this.role}) has admin rights.`;
  }
}

3.4. Статические свойства и методы в классах

Статические методы и свойства принадлежат самому классу, а не его экземплярам, и вызываются без создания объекта. Они полезны для утилитарных функций, которые логически относятся к классу, но не требуют работы с конкретным экземпляром. Например, класс MathUtils может содержать статический метод square для вычисления квадрата числа, вызываемый напрямую через класс. Важно, что статические методы не имеют доступа к нестатическим свойствам через this.

3.5. Определение и использование приватных свойств

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

3.6. Реализация наследования приватных и защищенных свойств

Хотя приватные свойства # не наследуются, можно использовать защищённые свойства (с префиксом _), доступные в дочерних классах по соглашению. Например, если родительский класс Animal имеет защищённое свойство _legs, дочерний класс Cat сможет его использовать, но это требует дисциплины от разработчика, так как технически такие свойства остаются публичными. Приватные же методы и свойства полностью изолированы в рамках своего класса.

3.7. Статические методы и их особенности

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

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

3.8. Приватные и защищенные методы

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

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

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

3.9. Полиморфизм в классах JavaScript

Полиморфизм позволяет переопределять методы родительского класса в дочерних, изменяя их поведение. Например, если класс Animal имеет метод speak, возвращающий общий звук, то класс Dog может переопределить этот метод для возврата конкретного звука "Woof!". При этом, если нужно, дочерний метод может вызывать родительскую реализацию через super.method(), комбинируя или расширяя функциональность.

3.10. Геттеры и сеттеры для работы с приватными свойствами

Геттеры и сеттеры предоставляют контролируемый доступ к приватным свойствам, позволяя добавлять логику при чтении или записи. Например, в классе Person сеттер для свойства age может проверять, что возраст не отрицательный, а геттер — возвращать значение приватного поля #age. Это удобно для валидации данных и скрытия внутренней реализации, обеспечивая более безопасное взаимодействие с объектом.

3.11. Множественное наследование и его эмуляция

JavaScript не поддерживает множественное наследование напрямую, но его можно имитировать с помощью миксинов или композиции. Например, можно создать класс C, который в конструкторе копирует методы из экземпляров классов A и B через Object.assign. Однако такой подход требует осторожности, так как может усложнить архитектуру. Часто композиция (использование объектов внутри других объектов) оказывается более предпочтительной альтернативой.

3.12. Использование символов для создания приватных свойств

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

Современный синтаксис с # предпочтительнее для создания приватных свойств, но символы по-прежнему находят применение в специфических сценариях, где требуется гибкость или поддержка устаревшего кода.

3.13. Статические свойства/методы и паттерн «одиночка»

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

3.14. Ленивая инициализация свойств и инкапсуляция

Ленивая инициализация откладывает создание ресурсоёмких свойств до момента их первого использования. Например, в классе LazyInit приватное свойство #data заполняется только при первом вызове геттера data, который вызывает метод #loadData. Это оптимизирует производительность, особенно когда свойство требуется не всегда. Инкапсуляция здесь играет ключевую роль, скрывая сложную логику инициализации от внешнего кода.

4. JS. Method of Object, Array, Function.prototype

4.1. Методы перебора объектов (Object.keys, for/in)

Для перебора свойств объекта чаще всего используют Object.keys(), который возвращает массив ключей, или цикл for/in, который проходит по всем перечисляемым свойствам, включая унаследованные. Object.keys() удобен для работы только с собственными свойствами объекта, тогда как for/in требует дополнительной проверки через hasOwnProperty(), чтобы избежать обработки унаследованных полей. Оба метода полезны в разных сценариях: первый — когда нужен строгий контроль над перебираемыми свойствами, второй — когда требуется анализировать всю цепочку прототипов. –>

4.2. Методы работы с массивами: map и forEach

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

4.3. Основные методы работы с массивами (find, filter, some, includes)

Методы find(), filter(), some() и includes() решают разные задачи при работе с массивами: find() возвращает первый элемент, удовлетворяющий условию, filter() создает новый массив с элементами, прошедшими проверку, some() проверяет, есть ли хотя бы один подходящий элемент, а includes() определяет, содержится ли конкретное значение в массиве. find() и filter() полезны для поиска данных, some() и includes() — для проверок, причем includes() работает только с примитивами, тогда как some() принимает функцию-предикат.

4.4. Методы клонирования объектов

Клонирование объектов в JavaScript может быть поверхностным или глубоким. Поверхностное копирование (через Object.assign() или spread-оператор ...) копирует только верхний уровень, оставляя вложенные объекты. Глубокое клонирование требует рекурсивного подхода или использования JSON.parse(JSON.stringify()), что работает для простых данных, но теряет функции и специальные типы. Для сложных структур лучше применять библиотеки вроде Lodash или писать собственные рекурсивные функции, учитывающие все типы данных.

4.5. Суммарные и агрегирующие операции с массивами (reduce)

Метод reduce() позволяет агрегировать данные массива в единое значение, накапливая результат через callback-функцию. Он полезен для вычисления сумм, объединения строк, группировки объектов или создания сложных структур данных. reduce() принимает аккумулятор и текущий элемент, последовательно применяя логику ко всем элементам массива. Этот метод особенно мощный в комбинации с другими операциями, такими как map() или filter(), позволяя выполнять комплексные преобразования за один проход по данным.

4.6. Клонирование объектов в JavaScript

В JavaScript клонирование объектов может быть поверхностным или глубоким. Поверхностное копирование создает новый объект, но вложенные свойства остаются связанными с оригиналом — для этого используют оператор spread ... или Object.assign(). Глубокое клонирование полностью реплицирует объект со всеми уровнями вложенности, что можно реализовать через рекурсивную функцию или JSON.parse(JSON.stringify()), хотя последний метод имеет ограничения (теряет функции и специальные типы данных). Для сложных случаев лучше применять специализированные библиотеки типа Lodash или писать собственные функции глубокого копирования, учитывающие все возможные типы данных и циклические ссылки.

4.7. Перебор элементов массива с помощью методов find и includes

Методы find() и includes() служат для поиска элементов, но работают по-разному: find() принимает функцию-предикат и возвращает первый подходящий элемент, а includes() проверяет наличие конкретного значения, возвращая булево значение. find() подходит для работы с объектами и сложными условиями, тогда как includes() оптимален для примитивов. Оба метода останавливают перебор при первом совпадении, что делает их эффективными для больших массивов.

4.8. Сортировка массивов объектов по ключу

Сортировка массивов объектов выполняется через метод sort() с callback-функцией, сравнивающей значения нужного ключа. Для строк используется localeCompare(), для чисел — вычитание. Важно помнить, что sort() изменяет исходный массив, поэтому для сохранения оригинала нужно предварительно создать копию. Сложные условия сортировки (по нескольким полям или в разных направлениях) требуют комбинирования сравнений внутри callback-функции.

4.9. Методы работы с массивом: reduce

Метод reduce() является мощным инструментом для агрегации данных массива в единое значение. Он последовательно обрабатывает каждый элемент, сохраняя промежуточный результат в аккумуляторе. Помимо базовых операций (суммирование, конкатенация), reduce() позволяет реализовывать сложные преобразования - группировку объектов, создание lookup-таблиц, композицию функций. Важной особенностью является возможность задавать начальное значение аккумулятора, что особенно полезно при работе с объектами или нестандартными типами данных. Этот метод часто комбинируют с другими операциями массива для создания эффективных и выразительных цепочек обработки данных.

4.10. Различия и применения методов map и forEach

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

4.11. Продвинутое использование метода array.reduce

Метод reduce() выходит за рамки простых суммирований, позволяя реализовать группировку, преобразование структур данных или даже комбинацию нескольких операций в одном проходе. Например, с его помощью можно создать объект-индекс по ключу, объединить данные из нескольких массивов или реализовать state-менеджмент. Важно правильно инициализировать аккумулятор и учитывать порядок обработки элементов, особенно в сложных сценариях.

4.12. Клонирование объектов и изоляция ссылок

При клонировании объектов важно изолировать вложенные ссылки, чтобы изменения в копии не затрагивали оригинал. Поверхностное копирование (spread-оператор, Object.assign()) не решает эту проблему для вложенных объектов, поэтому для глубокого клонирования используют рекурсивные функции или сериализацию через JSON. Однако JSON-метод не копирует функции, undefined или циклические ссылки, что требует альтернативных подходов для сложных структур.

4.13. Методы работы с массивами: some, every, includes

Методы some() и every() проверяют, удовлетворяют ли элементы массива условию: some() возвращает true, если хотя бы один элемент прошел проверку, а every() — только если все элементы соответствуют условию. includes() проверяет наличие конкретного значения, работая исключительно с примитивами. Эти методы полезны для валидации данных или проверки условий без необходимости перебора вручную.

4.14. Алгоритмы сортировки массивов объектов

Сортировка объектов требует указания callback-функции, определяющей порядок сравнения. Для сложных условий можно комбинировать несколько ключей или использовать внешние библиотеки для стабильной сортировки. Важно учитывать, что стандартный sort() в JavaScript не всегда стабилен, что может повлиять на порядок элементов с одинаковыми ключами. Для локале-зависимой сортировки строк применяют Intl.Collator.

4.15. Глубокое понимание и различия методов Object.keys и Object.values

Object.keys() возвращает массив ключей объекта, а Object.values() — массив значений. Оба метода работают только с собственными перечисляемыми свойствами, игнорируя унаследованные. Object.entries() дополняет эту пару, возвращая пары [ключ, значение]. Эти методы полезны для преобразования объектов в массивы, что упрощает перебор и обработку данных в функциональном стиле.

5. Browser. DOM

5.1. Основы DOM

DOM (Document Object Model) — это программное представление HTML-документа в виде дерева объектов, которое позволяет JavaScript взаимодействовать со структурой страницы. Каждый HTML-элемент становится узлом (нодой) в этом дереве, что дает возможность динамически изменять содержимое, стили и поведение страницы. DOM создается браузером при загрузке HTML и служит интерфейсом между кодом и визуальным отображением.

5.2. Типы узлов в DOM

В DOM существует несколько типов узлов: элементы (HTML-теги), текстовые узлы (содержимое внутри тегов), комментарии, атрибуты и другие. Наиболее важными являются элементы (Node.ELEMENT_NODE) и текстовые узлы (Node.TEXT_NODE). Например, тег <div> — это элемент, а текст внутри него — текстовый узел. Узлы могут иметь родительские, дочерние и соседние связи, формируя древовидную структуру.

5.3. Понятие и структура DOM-дерева

DOM-дерево — это иерархическая структура, где корневым узлом является document, а HTML-элементы образуют ветви и листья. Каждый элемент может содержать дочерние узлы (например, <ul> содержит <li>), а также иметь атрибуты и текстовое содержимое. Дерево строится по принципу вложенности, что позволяет перемещаться между узлами с помощью свойств вроде parentNode, childNodes и nextSibling.

5.4. Методы поиска элементов в DOM

Для поиска элементов в DOM используются методы:

  • getElementById() — находит элемент по id.
  • querySelector() — возвращает первый элемент, соответствующий CSS-селектору.
  • querySelectorAll() — возвращает все подходящие элементы.
  • getElementsByClassName() и getElementsByTagName() — устаревшие, но работают.
  • querySelector и querySelectorAll — самые гибкие, так как поддерживают сложные CSS-селекторы.

5.5. Инспектирование DOM в браузере

Инструменты разработчика (DevTools) в браузере позволяют исследовать DOM в реальном времени. Вкладка Elements (или Inspector) отображает структуру дерева, атрибуты и стили. Можно редактировать HTML, добавлять или удалять элементы, а также просматривать изменения сразу на странице. Это полезно для отладки и тестирования макетов.

5.6. Влияние изменений в DOM на производительность

Частые изменения DOM (например, множественные добавления или удаления элементов) могут снижать производительность, так как браузер вынужден пересчитывать стили и макет (reflow и repaint). Для оптимизации используют:

  • Фрагменты (DocumentFragment) для группового добавления элементов
  • Виртуальный DOM (как в React) для минимизации прямых изменений
  • Отложенные операции с requestAnimationFrame

5.7. Инструменты разработчика для анализа и оптимизации DOM

В DevTools есть инструменты для анализа производительности DOM:

  • Performance — запись и анализ нагрузки при операциях с DOM.
  • Rendering — отображение перерисовок (paint flashing) и перерасчетов макета.
  • Memory — проверка утечек памяти из-за неочищенных ссылок на DOM-узлы.

Эти инструменты помогают находить «узкие места» и оптимизировать взаимодействие с DOM.

5.8. Shadow DOM

Shadow DOM — это технология, позволяющая изолировать часть DOM-дерева, создавая «теневой» корневой узел (shadowRoot). Это используется в веб-компонентах для инкапсуляции стилей и структуры, предотвращая конфликты с основной страницей. Shadow DOM можно создать через element.attachShadow({ mode: ‘open’ }), после чего добавлять в него элементы, которые не будут видны в основном DOM.

6. Browser. Search methods

  1. Объясните разницу между результатами querySelectorAll и getElementsByClassName с точки зрения «статической» и «живой» коллекции. Как это может повлиять на логику приложения при изменении DOM во время работы?
    • При выборе между querySelectorAll и getElementsByClassName ключевым критерием становится тип возвращаемой коллекции. querySelectorAll генерирует статический NodeList, который не изменяется после создания, тогда как getElementsByClassName возвращает «живую» HTMLCollection, автоматически отражающую актуальное состояние DOM.
    • «Живые» коллекции полезны при частых обновлениях структуры страницы, так как исключают необходимость повторных запросов. Однако они могут вызывать проблемы с производительностью при массовых операциях, поскольку браузер постоянно пересчитывает их состав. В отличие от них, querySelectorAll обеспечивает стабильность, но требует явного обновления при изменениях. Дополнительно querySelectorAll поддерживает любые селекторы, а getElementsByClassName ограничен только классами.
  2. Сравните querySelector и getElementById: в каких случаях вы выберете каждый из них, какие у них ограничения и различия по входным данным и возвращаемому результату?
    • При выборе между методами доступа к элементам важно учитывать их особенности. getElementById демонстрирует максимальную производительность для работы с уникальными элементами, в то время как querySelector обеспечивает большую гибкость за счёт поддержки сложных селекторов.
  3. Что такое DOM и какой метод в браузере чаще всего используют, чтобы получить элемент по его уникальному идентификатору (id)? Объясните, что именно возвращается этот метод.
    • DOM представляет HTML-документ в виде древовидной структуры объектов, где каждый элемент становится узлом. Это позволяет JavaScript динамически изменять содержимое и структуру страницы.
    • Для работы с конкретными элементами чаще всего используют метод getElementById, который находит элемент по его уникальному идентификатору. Этот метод работает быстрее альтернатив, так как браузеры оптимизируют поиск по ID. Он возвращает один элемент или null, если ничего не найдено, что делает его идеальным для точечных манипуляций с конкретными частями страницы.

6.1. Основы DOM (Document Object Model)

DOM представляет HTML-документ в виде древовидной структуры объектов, где каждый элемент становится узлом. Это позволяет JavaScript динамически изменять содержимое и структуру страницы. Браузер автоматически строит DOM при загрузке HTML, создавая иерархию элементов, атрибутов и текстовых узлов. Любые изменения в DOM мгновенно отражаются на отображаемой странице, обеспечивая интерактивность.

6.2. Методы доступа к элементам по ID

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

6.3. Введение в CSS селекторы

CSS-селекторы предоставляют мощный инструмент для выбора элементов по различным критериям: типу тега, классу, идентификатору или атрибутам. В JavaScript эти же селекторы используются в методах querySelector и querySelectorAll, позволяя гибко находить нужные элементы. Понимание CSS-селекторов критически важно для эффективной работы с DOM, так как открывает возможности для точного таргетирования элементов без лишнего кода.

6.4. Основные методы работы с DOM в JavaScript

Работа с DOM включает несколько ключевых операций: поиск элементов, изменение их свойств и структуры, а также обработку событий. Для поиска используют методы вроде getElementById или querySelector, для модификации - свойства innerHTML и classList, для управления структурой - методы appendChild и removeChild. Каждая операция влияет на производительность, поэтому важно выбирать оптимальные подходы для конкретных задач.

6.5. Доступ к элементам формы в JavaScript

Формы и их элементы доступны через специальные коллекции и свойства. Объект document.forms содержит все формы страницы, а свойство elements каждой формы предоставляет доступ к её полям. Особенно удобно обращаться к элементам по их атрибутам name, что упрощает обработку пользовательского ввода. Значения полей можно читать и изменять через свойство value, а состояние переключателей - через checked.

6.6. Различные способы получения элементов DOM

JavaScript предлагает несколько подходов к поиску элементов: по уникальному идентификатору, по имени класса, по тегу или с помощью CSS-селекторов. Каждый метод имеет свои преимущества: getElementById - скорость, getElementsByClassName - работу с группами элементов, а querySelector - гибкость сложных выборок. Выбор метода зависит от конкретной задачи и требований к производительности.

6.7. Применение CSS селекторов в JavaScript

Использование CSS-селекторов в JavaScript через методы querySelector и querySelectorAll открывает возможности для точного и выразительного поиска элементов. Можно выбирать элементы по их положению в структуре, состоянию или комбинации атрибутов. Этот подход особенно полезен при работе с динамическими интерфейсами, где элементы могут менять классы или атрибуты в процессе работы приложения.

6.8. Использование метода querySelector и его особенности

Метод querySelector является одним из наиболее гибких инструментов для поиска элементов в DOM благодаря поддержке CSS-селекторов. Его ключевая особенность — возврат первого найденного элемента, соответствующего указанному селектору, или null, если совпадений нет. В отличие от getElementById, он позволяет использовать сложные селекторы, включая комбинации классов, атрибутов и псевдоклассов, что делает его универсальным для различных сценариев. Однако важно учитывать, что querySelector работает только с текущим состоянием DOM на момент вызова и не возвращает «живую» коллекцию. Это означает, что последующие изменения в структуре страницы не повлияют на результат ранее выполненного запроса. Кроме того, при неправильно составленном селекторе метод может вернуть неожиданный элемент или null, поэтому валидация результата часто становится необходимой.

6.9. Сравнение методов доступа к DOM элементам (querySelector vs getElementById)

При выборе между методами доступа к элементам важно учитывать их особенности. getElementById демонстрирует максимальную производительность для работы с уникальными элементами, в то время как querySelector обеспечивает большую гибкость за счёт поддержки сложных селекторов. Для массовых операций с группами элементов лучше подходят getElementsByClassName или querySelectorAll, в зависимости от необходимости в “живой” коллекции.

6.10. Подробное изучение метода getElementById и его использование

Метод getElementById остается самым быстрым и надежным способом доступа к элементам с уникальными идентификаторами. Он принимает строку с id и возвращает соответствующий элемент, не требуя символа #, в отличие от querySelector. Если элемент не найден, возвращается null, что позволяет легко проверять наличие элемента перед выполнением операций с ним.

Критически важной особенностью getElementById является его исключительная производительность — браузеры оптимизируют поиск по ID, используя внутренние хэш-таблицы. Это делает метод идеальным для сценариев, где важна скорость, например, при частых обновлениях интерфейса. Однако его применение ограничено работой только с id, поэтому для сложных выборок он не подходит.

6.11. Глубокий анализ метода querySelector и querySelectorAll

Методы querySelector и querySelectorAll представляют современный подход к поиску элементов, основанный на CSS-селекторах. В то время как querySelector возвращает первый подходящий элемент, querySelectorAll предоставляет статическую коллекцию всех совпадений в виде NodeList. Оба метода поддерживают сложные селекторы, включая вложенность, псевдоклассы (:hover, :nth-child) и комбинаторы (>, +), что позволяет точно таргетировать элементы без дополнительной фильтрации.

Главное отличие querySelectorAll от «живых» коллекций (например, getElementsByClassName) — его статичность. Возвращаемый NodeList фиксируется на момент вызова и не обновляется при изменениях DOM. Это может быть как преимуществом (предсказуемость), так и недостатком (необходимость повторного запроса при модификации страницы). Для перебора элементов NodeList удобно использовать методы массивов, предварительно преобразовав его через Array.from().

6.12. Сравнительный анализ методов доступа к DOM (querySelectorAll vs getElementsByClassName)

При выборе между querySelectorAll и getElementsByClassName ключевым критерием становится тип возвращаемой коллекции. querySelectorAll генерирует статический NodeList, который не изменяется после создания, тогда как getElementsByClassName возвращает «живую» HTMLCollection, автоматически отражающую актуальное состояние DOM.

«Живые» коллекции полезны при частых обновлениях структуры страницы, так как исключают необходимость повторных запросов. Однако они могут вызывать проблемы с производительностью при массовых операциях, поскольку браузер постоянно пересчитывает их состав. В отличие от них, querySelectorAll обеспечивает стабильность, но требует явного обновления при изменениях. Дополнительно querySelectorAll поддерживает любые селекторы, а getElementsByClassName ограничен только классами.

6.13. Продвинутое использование CSS селекторов для манипуляции DOM

Сложные CSS-селекторы позволяют реализовывать изощрённые сценарии выборки элементов без написания громоздкого JavaScript-кода. Можно отбирать элементы по их позиции в дереве, состоянию или комбинации атрибутов. Особенно мощными являются псевдоклассы, позволяющие выбирать элементы по их динамическому состоянию или положению среди соседей, что значительно упрощает многие задачи по работе с интерфейсами.

6.14. Изучение различий между статическим и живым NodeList

Разница между статическими и «живыми» коллекциями элементов (NodeList и HTMLCollection) заключается в их реакции на изменения DOM. Статический NodeList, возвращаемый querySelectorAll, фиксирует состояние элементов на момент вызова и не обновляется, даже если структура страницы меняется. Это делает его предсказуемым, но требует повторного запроса для получения актуальных данных.

«Живые» коллекции (например, от getElementsByClassName или childNodes) автоматически синхронизируются с DOM. Добавление или удаление элементов мгновенно отражается в коллекции, что удобно для динамических интерфейсов. Однако такая особенность может привести к неочевидным ошибкам, например, при одновременном переборе коллекции и её модификации. Кроме того, «живые» коллекции обычно не поддерживают методы массивов, в отличие от NodeList, который можно преобразовать в массив для удобной обработки.

6.15. Детальный анализ взаимодействия JavaScript с элементами формы

JavaScript предоставляет комплексные инструменты для работы с формами: от чтения значений полей до валидации и обработки отправки. Особое внимание стоит уделить объекту FormData, который упрощает сбор информации со всех полей формы. Для современных интерфейсов важно учитывать различные события форм - не только submit, но и input, change, что позволяет создавать более отзывчивые пользовательские интерфейсы с мгновенной реакцией на действия пользователя.

7. Browser. Events

  1. Опиши, как работают фазы перехвата и всплытия события, и как на этом основано делегирование событий. В каких случаях делегирование лучше прямых обработчиков и какие есть подводные камни (например, target/currentTarget, stopPropagation, preventDefault)?
    • Делегирование событий — это техника, при которой обработчик вешается не на каждый элемент, а на их общего родителя. Это работает благодаря всплытию событий и позволяет эффективно управлять динамическими списками, где элементы могут добавляться или удаляться. Такой подход снижает нагрузку на память и упрощает код, так как вместо множества обработчиков используется один, анализирующий целевой элемент через свойство event.target
    • События в DOM распространяются в трех фазах: перехват (от корня к целевому элементу), целевая фаза и всплытие (от элемента к корню). По умолчанию обработчики срабатывают на фазе всплытия, но можно зарегистрировать их на фазе перехвата, указав третий параметр в addEventListener.
    • Некоторые события имеют стандартные действия: отправка формы, переход по ссылке или выделение текста. Чтобы отменить эти действия, используется метод preventDefault объекта события.
  2. Объясни разницу между добавлением обработчика через атрибут в HTML, через свойство элемента (onclick) и через addEventListener: в чем плюсы/минусы и как удалять обработчики?
    • Для подписки на события используется метод addEventListener, который принимает тип события и функцию-обработчик. Этот метод позволяет добавлять несколько обработчиков на один элемент без перезаписи существующих. Удаление обработчика осуществляется через removeEventListener, куда передаются те же аргументы, что и при добавлении.
  3. Что такое DOM-событие в браузере и какие примеры типов событий (например, мышь, клавиатура, форма) ты можешь назвать?
    • DOM-события позволяют JavaScript реагировать на действия пользователя (клики, ввод текста) или системные изменения (загрузка страницы). Каждое событие содержит информацию о типе взаимодействия и целевом элементе, что дает возможность создавать интерактивные интерфейсы.
    • События в DOM делятся на категории: пользовательские (клики, наведение мыши), клавиатурные (нажатие клавиш), формы (отправка, изменение полей), медиа (воспроизведение видео) и другие. Каждый тип события имеет уникальные свойства: например, события мыши содержат координаты курсора, а клавиатурные — код нажатой клавы. Понимание особенностей разных типов событий помогает создавать более точную и удобную логику взаимодействия.

7.1. Основы DOM событий

DOM-события позволяют JavaScript реагировать на действия пользователя (клики, ввод текста) или системные изменения (загрузка страницы). Каждое событие содержит информацию о типе взаимодействия и целевом элементе, что дает возможность создавать интерактивные интерфейсы.

Обработчики событий — это функции, которые выполняются при наступлении события, например, при клике на кнопку или отправке формы. Современные браузеры поддерживают сотни типов событий, охватывающих все аспекты взаимодействия с веб-страницей. –>

7.2. Добавление и удаление обработчиков событий

Для подписки на события используется метод addEventListener, который принимает тип события и функцию-обработчик. Этот метод позволяет добавлять несколько обработчиков на один элемент без перезаписи существующих. Удаление обработчика осуществляется через removeEventListener, куда передаются те же аргументы, что и при добавлении. Важно помнить, что для удаления нужно указывать ту же самую функцию, а не её аналог, иначе подписка останется активной.

7.3. Типы DOM событий

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

7.4. Предотвращение стандартного поведения событий

Некоторые события имеют стандартные действия: отправка формы, переход по ссылке или выделение текста. Чтобы отменить эти действия, используется метод preventDefault объекта события. Это особенно полезно при валидации форм перед отправкой или создании одностраничных приложений, где переходы по ссылкам обрабатываются JavaScript. Важно применять этот метод осознанно, чтобы не нарушать ожидаемое поведение интерфейса.

7.5. Всплытие и перехват событий

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

7.6. Делегирование событий

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

  1. Опишите меры безопасности при использовании cookies в веб-приложении: какие риски (XSS, CSRF, перехват трафика) они помогают снизить флаги HttpOnly/Secure/SameSite, и какие компромиссы или ограничения при этом возникают? Для безопасности нужно:
    • Использовать Secure и HttpOnly для аутентификационных cookies,
    • Устанавливать SameSite=Lax (или Strict) для защиты от CSRF,
    • Регулярно обновлять сессионные идентификаторы,
    • Ограничивать области действия через Domain/Path,
    • Избегать хранения в cookies конфиденциальных данных,
    • Для дополнительной защиты можно подписывать или шифровать содержимое cookies.
  2. Какие основные атрибуты cookie (например, Expires/Max-Age, Path, Domain, SameSite, Secure, HttpOnly) и как каждый из них влияет на поведение cookie в браузере?
    • Secure (передача только по HTTPS)
    • HttpOnly (запрет доступа через JavaScript)
    • SameSite (защита от CSRF-атак)
    • Domain/Path (определение области действия)
    • Expires/Max-Age (определение срока жизни cookie)
  3. Что такое cookie в браузере и чем они в общих чертах отличаются от LocalStorage по назначению и способу отправки на сервер?
    • Cookies и Local Storage предоставляют разные способы хранения данных на стороне клиента. Cookies автоматически отправляются серверу с каждым HTTP-запросом и идеально подходят для хранения небольших объемов данных (максимальный размер около 4 КБ), таких как идентификаторы сессий. Local Storage позволяет сохранять значительно больше информации (до 5-10 МБ в зависимости от браузера) и не передает данные на сервер автоматически.

8.1. Основы работы с cookies и Local Storage

Cookies и Local Storage предоставляют разные способы хранения данных на стороне клиента. Cookies автоматически отправляются серверу с каждым HTTP-запросом и идеально подходят для хранения небольших объемов данных, таких как идентификаторы сессий. Local Storage позволяет сохранять значительно больше информации (до 5-10 МБ в зависимости от браузера) и не передает данные на сервер автоматически, что делает его удобным для кэширования. Оба механизма доступны через JavaScript, но требуют разного подхода к управлению данными и обеспечению безопасности.

8.2. Ограничения и атрибуты cookies

Cookies имеют ряд ограничений: максимальный размер около 4 КБ, ограниченное количество на домен (обычно 50-150) и привязка к конкретному домену/поддомену. Важные атрибуты включают Secure (передача только по HTTPS), HttpOnly (запрет доступа через JavaScript), SameSite (защита от CSRF-атак) и Domain/Path (определение области действия). Эти параметры критичны для баланса между функциональностью и безопасностью веб-приложений.

8.3. Удаление и установка cookies в JavaScript

Установка cookies через JavaScript выполняется присваиванием строки формата “ключ=значение” свойству document.cookie, с возможностью добавления атрибутов типа max-age или expires. Для удаления достаточно установить срок жизни в прошлое, что заставит браузер немедленно удалить cookie. Важно учитывать, что document.cookie возвращает все cookies для текущего пути в виде одной строки, что усложняет их парсинг по сравнению с современными API хранения данных.

8.4. Безопасное использование cookies

Безопасность cookies обеспечивается комбинацией атрибутов: HttpOnly защищает от XSS-атак, блокируя доступ через JavaScript, Secure гарантирует передачу только по зашифрованным соединениям HTTPS, а SameSite предотвращает отправку cookies при межсайтовых запросах. Для критичных данных (например, токенов аутентификации) следует устанавливать минимально необходимый срок жизни и избегать хранения чувствительной информации в cookies, где это возможно.

8.5. Максимальный срок жизни cookies и как его установить

Теоретически cookies могут иметь неограниченный срок жизни, если установить дату истечения далеко в будущем (например, через несколько лет). Однако на практике браузеры могут удалять cookies по истечении определенного времени (обычно несколько лет) или при превышении лимитов хранилища. Срок задается через атрибуты expires (конкретная дата) или max-age (в секундах), причем max-age имеет приоритет в современных браузерах. Для сессионных cookies (удаляемых при закрытии браузера) эти атрибуты не указываются.

8.6. Меры предосторожности при работе с cookies с точки зрения безопасности

Ключевые меры безопасности включают обязательное использование Secure и HttpOnly для аутентификационных cookies, установку SameSite=Lax (или Strict) для защиты от CSRF, регулярную ротацию сессионных идентификаторов и ограничение области действия через Domain/Path. Следует избегать хранения в cookies конфиденциальных данных (паролей, платежной информации), вместо этого используя серверные сессии с короткоживущими токенами. Для дополнительной защиты можно подписывать или шифровать содержимое cookies.

8.7. Виды/флаги куки

Основные флаги cookies включают Session (удаляются после закрытия браузера), Persistent (хранятся до истечения срока), Secure (HTTPS-only), HttpOnly (защита от XSS), SameSite (контроль межсайтовых запросов) и HostOnly (запрет доступа для поддоменов). Особую категорию составляют сторонние cookies (third-party), устанавливаемые с других доменов через iframe или скрипты, которые постепенно блокируются современными браузерами из-за проблем с конфиденциальностью. Выбор флагов зависит от конкретного сценария использования и требований безопасности.

9. Storage. LocalStorage, SessionStorage

  1. Как бы вы спроектировали отслеживание и синхронизацию изменений данных между вкладками при использовании LocalStorage/SessionStorage: какие события и ограничения нужно учитывать, как избежать гонок и неконсистентности, и что делать при ошибках/квотах хранения?
    • Браузеры предоставляют механизм отслеживания изменений в веб-хранилищах через событие storage, которое срабатывает при модификации данных из другого документа (вкладки или окна). Это событие содержит информацию об измененном ключе, старом и новом значениях, что позволяет синхронизировать состояние между несколькими экземплярами приложения.
  2. Объясните ключевые различия между LocalStorage и SessionStorage: срок жизни данных, область видимости (вкладки/окна), поведение при закрытии вкладки и перезапуске браузера.
    • Ключевое различие между LocalStorage и SessionStorage заключается в области видимости и продолжительности хранения данных. LocalStorage доступен во всех вкладках и окнах браузера, работающих с одним доменом, и сохраняет данные между сеансами. SessionStorage, напротив, ограничен текущей вкладкой и автоматически очищается при её закрытии.
  3. Что такое LocalStorage и SessionStorage в браузере и для каких типичных задач их используют в веб-приложениях?
    • LocalStorage и SessionStorage предоставляют механизмы для хранения данных на стороне клиента в виде пар ключ-значение. Оба хранилища доступны через глобальный объект window и используют одинаковый API, включая методы setItem, getItem и removeItem. Основное отличие заключается в времени жизни данных: SessionStorage очищается при закрытии вкладки браузера, тогда как LocalStorage сохраняет информацию до явного удаления. Имеют ограничения по объему (обычно 5-10 МБ на домен)

9.1. Основы LocalStorage и SessionStorage

LocalStorage и SessionStorage предоставляют механизмы для хранения данных на стороне клиента в виде пар ключ-значение. Оба хранилища доступны через глобальный объект window и используют одинаковый API, включая методы setItem, getItem и removeItem. Основное отличие заключается в времени жизни данных: SessionStorage очищается при закрытии вкладки браузера, тогда как LocalStorage сохраняет информацию до явного удаления. Эти технологии идеально подходят для кэширования данных, сохранения пользовательских настроек или работы в оффлайн-режиме, но имеют ограничения по объему (обычно 5-10 МБ на домен).

9.2. Различия между LocalStorage и SessionStorage

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

9.3. Основные операции с LocalStorage

Работа с LocalStorage включает четыре основные операции: запись данных через setItem, чтение через getItem, удаление отдельных записей через removeItem и полную очистку хранилища с помощью clear. Все данные хранятся в виде строк, поэтому при работе с числами или булевыми значениями требуется явное преобразование типов. Важно учитывать, что операции с LocalStorage синхронны и могут блокировать основной поток при работе с большими объемами данных, что потенциально влияет на производительность интерфейса.

9.4. Основные операции с SessionStorage

SessionStorage использует идентичный LocalStorage API, что обеспечивает согласованность в работе с обоими хранилищами. Однако из-за особенностей жизненного цикла SessionStorage особенно полезен для временного сохранения состояния интерфейса, например, ввода формы при случайном обновлении страницы. Как и в случае с LocalStorage, данные автоматически преобразуются в строки, что требует дополнительной обработки при хранении сложных структур. Особенностью SessionStorage является его изолированность между вкладками, даже если они открыты на одном домене.

9.5. Хранение комплексных данных (объектов, массивов) в LocalStorage и SessionStorage

Для хранения объектов или массивов в веб-хранилищах необходимо предварительно преобразовывать их в строку с помощью JSON.stringify, а при чтении - восстанавливать исходную структуру через JSON.parse. Этот подход позволяет сохранять сложные данные, включая вложенные объекты, но требует обработки возможных ошибок парсинга. Важно учитывать ограничение на максимальный размер записи (обычно несколько мегабайт), что делает веб-хранилища непригодными для хранения больших объемов структурированных данных. Для сложных сценариев лучше рассмотреть IndexedDB.

9.6. Использование JSON с LocalStorage

Интеграция JSON с LocalStorage стала стандартным подходом для работы со структурированными данными. Преобразование объектов в JSON-строки перед сохранением позволяет сохранять их целостность и восстанавливать при последующих сеансах работы с приложением. Однако этот метод требует аккуратной обработки исключений, особенно при работе с устаревшими или поврежденными данными. Для обеспечения стабильности рекомендуется реализовывать валидацию структуры данных после их извлечения из хранилища, а также предусматривать механизмы миграции данных при изменении их формата.

9.7. События изменения данных в LocalStorage и SessionStorage

Браузеры предоставляют механизм отслеживания изменений в веб-хранилищах через событие storage, которое срабатывает при модификации данных из другого документа (вкладки или окна). Это событие содержит информацию об измененном ключе, старом и новом значениях, что позволяет синхронизировать состояние между несколькими экземплярами приложения. Важно отметить, что событие не срабатывает в документе, который инициировал изменение, и не работает для SessionStorage из-за его ограниченной области видимости. Для сложных сценариев синхронизации часто используют комбинацию событий storage и BroadcastChannel API.

9.8. Обработка исключений при использовании LocalStorage и SessionStorage

При работе с веб-хранилищами важно учитывать возможные ошибки, такие как переполнение хранилища или недоступность API в приватном режиме браузера. Обработка исключений через try-catch блоки позволяет корректно реагировать на ситуации, когда запись данных невозможна, предотвращая аварийное завершение скрипта. Особое внимание стоит уделять ошибкам квотирования, проверяя размер данных перед записью с помощью метода JSON.stringify. Для улучшения пользовательского опыта рекомендуется реализовывать альтернативные механизмы хранения при обнаружении проблем с веб-хранилищами.

9.9. Синхронизация данных между LocalStorage и SessionStorage

Синхронизация между разными типами хранилищ требует реализации специальных механизмов, так как браузер не предоставляет встроенных средств для этой задачи. Наиболее распространенный подход предполагает создание обработчиков событий, которые дублируют изменения между хранилищами при обновлении данных. Для сложных сценариев можно использовать промежуточный слой абстракции, который автоматически поддерживает идентичность данных в обоих хранилищах. Важно учитывать различия в жизненном цикле данных - информация в SessionStorage будет потеряна при закрытии вкладки, даже если она сохранена в LocalStorage.

9.10. Оптимизация использования LocalStorage и SessionStorage

Эффективное использование веб-хранилищ предполагает минимизацию количества операций записи и чтения, а также оптимизацию структуры хранимых данных. Рекомендуется объединять связанные данные в единые объекты вместо множества отдельных записей, что снижает накладные расходы на сериализацию. Для часто изменяемых данных полезно реализовать механизм кэширования в памяти с периодической синхронизацией с хранилищем. Также важно регулярно очищать устаревшие или неиспользуемые данные, особенно в LocalStorage, где они могут накапливаться в течение длительного времени.

9.11. Паттерн обсервер для отслеживания изменений

Реализация паттерна Наблюдатель позволяет организовать эффективную систему реагирования на изменения в веб-хранилищах. Этот подход особенно полезен в сложных приложениях, где несколько компонентов зависят от одних и тех же данных. Классическая реализация предполагает создание централизованного сервиса, который регистрирует подписчиков и уведомляет их при обнаружении изменений. Для LocalStorage можно комбинировать этот паттерн со стандартным событием storage, что обеспечивает реактивность даже при изменениях из других вкладок браузера.

9.12. Преодоление ограничений размера LocalStorage с помощью обходных путей

При достижении лимита хранилища можно применять различные стратегии для продолжения работы с данными. Наиболее простой подход - реализация ротации данных, когда старые записи автоматически удаляются при добавлении новых. Для хранения больших объемов структурированных данных стоит рассмотреть переход на IndexedDB, который поддерживает значительно большие размеры. Альтернативным решением может быть разделение данных между несколькими доменами или использование компрессии перед записью, хотя эти методы усложняют архитектуру приложения.

9.13. Использование Web Workers и Service Workers с LocalStorage и SessionStorage

Web Workers и Service Workers имеют ограниченный доступ к API веб-хранилищ из-за их асинхронной природы. Service Workers вообще не могут использовать синхронные API, включая LocalStorage, что требует применения альтернативных подходов, таких как IndexedDB или кэш API. Для Web Workers возможен обмен данными с основным потоком через postMessage, с последующей записью в хранилище. Эти ограничения важно учитывать при проектировании архитектуры приложений, рассчитывающих на работу в фоновом режиме или оффлайн.

9.14. Кросс-доменное и защищенное хранение данных

Политика одинакового происхождения строго ограничивает доступ к веб-хранилищам между разными доменами. Для обмена данными между поддоменами можно использовать специальные техники, такие как размещение скриптов на общем домене или использование postMessage для безопасной коммуникации. В вопросах безопасности рекомендуется всегда использовать HTTPS для защиты данных при передаче и применять дополнительные механизмы шифрования для особо чувствительной информации. Следует помнить, что веб-хранилища уязвимы для XSS-атак, поэтому не должны содержать критически важные данные без дополнительной защиты.

9.15. Использование альтернативных технологий хранения данных

В сложных сценариях, когда возможностей веб-хранилищ недостаточно, стоит рассмотреть альтернативные технологии. IndexedDB предоставляет асинхронное API для работы с большими объемами структурированных данных, включая поддержку транзакций и индексов. Cache API идеально подходит для хранения сетевых запросов и ресурсов в сервис-воркерах. Для временного хранения небольших объемов данных можно использовать cookies с соответствующими атрибутами безопасности. Выбор технологии зависит от конкретных требований к объему, структуре и времени жизни данных.

10. HTML. Semantic, Critical Rendering path, block/inline elements

  1. Опишите критический путь рендеринга в браузере: какие основные шаги происходят от получения HTML до первого отображения, какие ресурсы могут блокировать рендер (например, CSS и синхронные скрипты) и какие устные стратегии оптимизации вы бы предложили.
    • Критический путь рендеринга — это последовательность шагов, которые браузер выполняет для преобразования HTML, CSS и JavaScript в пиксели на экране. Оптимизация включает минификацию ресурсов, сокращение CSSOM и DOM-деревьев, а также приоритизацию загрузки критического CSS. Устранение блокирующих скриптов и использование async/defer ускоряет отображение контента.
  2. В чём ключевое различие между блочными и строчными элементами в HTML с точки зрения участия в потоке документа и влияния на размеры/переносы, и как это обычно проявляется в браузере?
    • Блочные элементы (<div>, <p>) занимают всю доступную ширину, создавая переносы, а строчные (<span>, <a>) — только необходимое пространство. Блочные элементы могут содержать другие блочные, а строчные — только текст и другие строчные.
  3. Что такое семантическая разметка HTML и зачем использовать теги вроде <header>, <main>, <nav>, <article> вместо нейтральных <div>?
    • Семантическая разметка — это использование HTML-тегов по их смысловому назначению. Это нужно для поисковой оптимизации (SEO) и для того, чтобы разработчику было проще читать структуру.

10.1. Базовая структура HTML документа

Каждый HTML-документ начинается с объявления типа документа <!DOCTYPE html>, за которым следует корневой элемент <html>, содержащий <head> и <body>. В <head> размещаются метаданные, заголовок страницы (), подключение стилей и скриптов, а в <body> — видимое содержимое страницы. Эта стандартная структура обеспечивает корректное отображение контента и обработку документа браузерами, а также служит основой для SEO-оптимизации и доступности.

10.2. Элементы форматирования текста

HTML предоставляет множество тегов для семантического и визуального форматирования текста: <p> для абзацев, <h1>-<h6> для заголовков, и для выделения важного текста, для стилизации фрагментов. Семантические теги вроде <article>, <section> и

10.3. Создание и работа с ссылками

Ссылки создаются тегом с атрибутом href, указывающим URL-адрес или якорь внутри страницы. Атрибут target=”_blank” открывает ссылку в новой вкладке, а rel=”noopener noreferrer” защищает от уязвимостей безопасности. Для навигации внутри страницы используют якоря (#section-id), а для почты и телефонов — специальные схемы (mailto:, tel:). Правильное использование ссылок улучшает юзабилити и SEO.

10.4. Создание таблиц

Таблицы (<table>) состоят из строк (<tr>), ячеек (<td>), заголовков (<th>) и могут включать семантические секции (<thead>, <tbody>, <tfoot>). Атрибуты colspan и rowspan позволяют объединять ячейки, а <caption> добавляет описание. Хотя таблицы идеальны для сеточных данных, для верстки макетов следует использовать CSS Grid или Flexbox, чтобы избежать проблем с адаптивностью.

10.5. Вставка скриптов и использование символов

Скрипты подключаются через <script>, атрибут async или defer контролирует порядок выполнения. Для специальных символов (©, →) применяют HTML-сущности (©, →), чтобы избежать конфликтов с разметкой. Вставка SVG и MathML поддерживается напрямую, а для динамического контента используют JavaScript-шаблоны или фреймворки.

10.6. Различие между блочными и строчными элементами

Блочные элементы (<div>, <p>) занимают всю доступную ширину, создавая переносы, а строчные (, ) — только необходимое пространство. CSS-свойство display позволяет изменять это поведение (например, inline-block). Понимание разницы критично для верстки: блочные элементы могут содержать другие блочные, а строчные — только текст и другие строчные.

10.7. Типы input в HTML

Элемент поддерживает множество типов: text, password, email, date, checkbox, radio, file и другие. Каждый тип обеспечивает специализированный ввод данных и валидацию (например, type=”email” проверяет формат адреса). Атрибуты placeholder, required и pattern расширяют функциональность, а предлагает подсказки при вводе.

10.8. Типы кнопок в HTML

Кнопки создаются тегами

10.9. Работа с формами в HTML

Формы (<form>) собирают данные через элементы ,

10.10. Критический путь рендеринга

Критический путь рендеринга — это последовательность шагов, которые браузер выполняет для преобразования HTML, CSS и JavaScript в пиксели на экране. Оптимизация включает минификацию ресурсов, сокращение CSSOM и DOM-деревьев, а также приоритизацию загрузки критического CSS. Устранение блокирующих скриптов и использование async/defer ускоряет отображение контента.

10.11. Блокирующие этапы и их оптимизация

Блокирующие ресурсы (CSS, синхронные скрипты) задерживают рендеринг страницы. Оптимизация включает:

  • Встраивание критического CSS в
  • Отложенную загрузку неиспользуемого CSS через media=”print”.
  • Разделение JavaScript-кода и динамический импорт.
  • Использование предзагрузки () для ключевых ресурсов.

Эти методы сокращают время до первого отображения контента (FCP) и улучшают производительность.

11. CSS. Selector types, Selector weight, styles that are inherited

  1. Представь, что на элемент одновременно влияют: наследуемые свойства от родителя, правило с более низкой специфичностью, но с !important, и инлайновый стиль без !important. Как будет определяться итоговое значение свойства и в каком порядке учитываются наследование, специфичность, порядок в файле и !important?

  2. Представить вложенную структуру элементов, где на родителе и на самом элементе заданы разные значения color, font-size и line-height, а также есть правило с более высокой специфичностью, которое меняет color. Какие из этих свойств будут унаследованы, какие — нет, и как ты пошагово объяснишь итоговые вычисленные значения с учётом каскада, наследования и специфичности?
    • Элемент получит color от правила с самой высокой специфичностью, а font-size и line-height — из тех правил, что прописаны непосредственно для него, полностью игнорируя значения родителя.
  3. Объясни, как браузер определяет, какое правило применить, если несколько селекторов подходят к одному элементу: что такое специфичность (вес селектора), как сравниваются разные типы селекторов и какую роль играют порядок в файле и !important?

    От сильного к слабому:

    1. !important (Вне очереди)
    2. Инлайновый стиль (в теге)
    3. ID
    4. Класс / Атрибут / Псевдокласс
    5. Тег / Псевдоэлемент
    6. Порядок в коде (при равенстве весов)
  4. Какие основные типы CSS-селекторов ты знаешь (по тегу, классу, id, атрибуту, псевдоклассы/псевдоэлементы) и в каких ситуациях каждый обычно применяют?
    1. По тегу - h1 { ... }, p { ... } - для базовых стилей сайта
    2. По классу - .button { ... } - для создания многоразовых компонентов (кнопки, карточки, сетки)
    3. По ID - #main-nav { ... } - для уникальных элементов, которые встречаются на странице один раз
    4. По атрибуту - input[type="text"] { ... } - для стилизации элементов по их свойствам
    5. Псевдоклассы - :hover, :focus, - для описания состояния
    6. Псевдоэлементы - ::before, ::after - для оформления «виртуальных» частей элемента

11.1. Основы CSS: селекторы, включение стилей в HTML, базовые свойства

CSS (Cascading Style Sheets) определяет оформление HTML-элементов. Селекторы выбирают элементы для стилизации (например, по тегу, классу или ID). Стили можно подключать через

11.2. Блочные и строчные элементы, управление видимостью и позиционированием

Блочные элементы (<div>, <p>) занимают всю ширину и начинаются с новой строки, строчные (, ) — только необходимую. Видимость управляется через display: none (полное скрытие) или visibility: hidden (скрытие с сохранением места). Позиционирование задаётся position: static|relative|absolute|fixed|sticky.

При absolute элемент вырывается из потока и позиционируется относительно ближайшего родителя с relative, absolute или fixed. Если такого родителя нет, отсчёт идёт от границ документа. fixed фиксирует элемент относительно окна браузера, что полезно для создания прилипающих меню или модальных окон. Значение sticky сочетает поведение relative и fixed: элемент остаётся в потоке, пока не достигнет заданного порога (например, top: 0), после чего прилипает к указанной границе экрана. Это удобно для навигационных панелей, которые должны оставаться видимыми при скролле.

11.3. Размеры элементов, маргины и паддинги

Ширина и высота элементов регулируются width и height. Margin — внешние отступы, padding — внутренние. Они влияют на расстояние между элементами и их содержимым. box-sizing: border-box включает паддинги и бордеры в общий размер элемента.

11.4. Способы скрытия элементов в CSS

Элементы можно скрыть через: display: none — удаляет из потока, освобождая место. visibility: hidden — оставляет место, но делает невидимым. opacity: 0 — прозрачность 0%, элемент остаётся кликабельным. position: absolute + left: -9999px — вынос за экран.

11.5. Добавление и использование шрифтов в CSS

Шрифты подключаются через @font-face (свои файлы) или сервисы (Google Fonts). Свойство font-family задаёт семейство, font-weight — насыщенность, font-size — размер. Лучше указывать fallback-шрифты (serif, sans-serif) на случай недоступности основного.

11.6. Продвинутое позиционирование: z-index, типы display

z-index контролирует порядок наложения элементов (работает с position). display определяет тип элемента: block, inline, inline-block, flex, grid. flex и grid используются для сложных макетов, inline-block совмещает свойства строчных и блочных.

11.7. CSS Box Model и его компоненты

Box Model описывает структуру элемента: контент (content), padding, border и margin. По умолчанию width/height задают размер контента, но box-sizing: border-box включает паддинги и бордер в расчёт.

11.8. Адаптивный дизайн и медиазапросы

Адаптивный дизайн подстраивает вёрстку под разные устройства. Медиазапросы (@media) меняют стили при определённых условиях (ширина экрана, ориентация).

11.9. Селекторы атрибутов и стековый порядок элементов

Селекторы атрибутов ([type=”text”]) стилизуют элементы по атрибутам. Стековый порядок (z-index) управляет наложением элементов. Чем выше значение, тем ближе элемент к пользователю (работает с position).

11.10. Правила приоритетности CSS и альтернативные макеты

Приоритетность стилей в CSS определяется специфичностью селекторов. Инлайн-стили имеют наивысший приоритет, за ними следуют ID, классы, атрибуты и, наконец, теги. Важно помнить, что !important переопределяет все остальные правила, но его использование затрудняет поддержку кода.

// Для создания макетов помимо традиционных методов вроде float (который сейчас считается устаревшим) применяются более современные подходы. Flexbox позволяет легко выравнивать элементы вдоль одной оси, а CSS Grid даёт полный контроль над двумерными сетками. Выбор между ними зависит от задачи: Flexbox лучше подходит для динамически изменяющихся интерфейсов, а Grid — для сложных, предсказуемых макетов.

11.11. Flexbox: свойства, центрирование и сложные макеты

Flexbox значительно упрощает создание гибких макетов. Основное свойство display: flex превращает контейнер в flex-блок, внутри которого элементы можно выравнивать по главной и поперечной осям. Например, justify-content управляет распределением пространства по горизонтали, а align-items — по вертикали. Для центрирования достаточно задать justify-content: center и align-items: center, что делает Flexbox идеальным инструментом для выравнивания содержимого. Более сложные макеты строятся с помощью вложенных flex-контейнеров и свойств вроде flex-grow, которое определяет, как элементы заполняют свободное место.

11.12. Responsive дизайн: принципы и техники

Адаптивный дизайн предполагает, что вёрстка должна корректно отображаться на устройствах с разными размерами экранов. Один из ключевых принципов — mobile-first, когда стили сначала пишутся для мобильных устройств, а затем дополняются медиазапросами для более широких экранов. Медиазапросы позволяют применять разные стили в зависимости от разрешения, ориентации устройства или даже типа дисплея. Гибкие сетки на основе процентов или fr в CSS Grid, а также относительные единицы измерения (rem, vw) помогают создавать масштабируемые интерфейсы.

11.13. Техники оптимизации CSS для производительности

Оптимизация CSS ускоряет загрузку страницы и улучшает пользовательский опыт. Минификация кода удаляет лишние пробелы и комментарии, уменьшая размер файла. Инструменты вроде PurgeCSS автоматически удаляют неиспользуемые стили. Стоит избегать глубокой вложенности селекторов, так как это замедляет обработку стилей. Для анимаций лучше использовать свойства transform и opacity, которые не вызывают перерасчёт макета. Также полезно объединять CSS-файлы и использовать современные форматы сжатия.

11.14. Взаимодействие CSS и модели рендеринга браузера

Когда браузер загружает страницу, он сначала строит DOM и CSSOM, а затем объединяет их в Render Tree. Изменения стилей могут вызвать reflow (перерасчёт макета) или repaint (перерисовку). Оптимизация рендеринга включает минимизацию операций, вызывающих reflow, например, чтение геометрических свойств элементов в цикле. Анимации на основе transform и opacity выполняются на GPU, что делает их более плавными.

11.15. Продвинутые техники и свойства для анимаций, CSS Grid vs Flexbox

Современные анимации в CSS создаются с помощью transition для плавных изменений и keyframes для сложных последовательностей. Свойство will-change подсказывает браузеру, какие элементы будут анимироваться, что улучшает производительность. Выбор между CSS Grid и Flexbox зависит от задачи. Flexbox лучше подходит для выравнивания элементов вдоль одной оси, например, в навигационных меню. CSS Grid, в свою очередь, предоставляет больше контроля над двумерными макетами, такими как сложные сетки или карточные интерфейсы. Оба инструмента могут комбинироваться для достижения наилучшего результата.

This post is licensed under CC BY 4.0 by the author.