Все специализации

Frontend-разработчик: вопросы на собеседовании

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

Инверсия контроля (IoC) — это парадигма проектирования, при которой управление потоком программы передается из пользовательского кода во фреймворк или контейнер. Вместо того, чтобы код активно вызывал методы библиотек, фреймворк вызывает методы, предоставленные пользовательским кодом, в нужный момент.

Основные аспекты:

Разделение ответственностей: Компоненты становятся менее связанными друг с другом.

Улучшенная тестируемость: Зависимости легко заменять моками или заглушками.

Более гибкий дизайн: Компоненты проще изменять и расширять.

Примеры реализации:

DI (Dependency Injection): Внедрение зависимостей в компоненты (через конструктор, сеттеры или поле).

Service Locator: Компонент активно запрашивает зависимости из центрального реестра.

Шаблонные методы: Алгоритм реализован в базовом классе, но шаги могут быть переопределены в подклассах.

Event-driven programming: Код реагирует на внешние события, а не активно их опрашивает.

IoC часто используется в фреймворках (например, React, Angular) для управления жизненным циклом компонентов и их взаимодействием.

Архитектура портов и адаптеров, также известная как гексагональная архитектура (Hexagonal Architecture), отделяет логику предметной области приложения (Core) от внешних зависимостей (интерфейсов пользователя, баз данных, внешних сервисов).

Основные концепции:

Порты: Определяют интерфейсы, через которые Core взаимодействует с внешним миром. Порты бывают двух типов:

Входящие (Driving Ports): Используются внешними сущностями для вызова функциональности Core (например, API-интерфейс).

Исходящие (Driven Ports): Используются Core для вызова функциональности внешних сущностей (например, интерфейс репозитория для доступа к данным).

Адаптеры: Реализуют порты, подключая Core к конкретным внешним технологиям или фреймворкам.

Входящие (Driving Adapters): Реализуют входящие порты и преобразуют входящие запросы из внешней среды в вызовы Core (например, REST-контроллер).

Исходящие (Driven Adapters): Реализуют исходящие порты и преобразуют вызовы из Core для взаимодействия с внешней средой (например, реализация репозитория для работы с конкретной базой данных).

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

Преимущества:

Тестируемость: Изоляция Core облегчает юнит-тестирование бизнес-логики без необходимости мокировать сложные внешние зависимости.

Гибкость: Позволяет легко заменять или добавлять новые внешние технологии (базы данных, фреймворки) без изменения Core. Достаточно написать новый адаптер для существующего порта.

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

Разделение ответственности: Четко разделяет зоны ответственности.

Связь с Frontend:

На Frontend архитектура портов и адаптеров применима для:

Отделения бизнес-логики компонента от источников данных: Компонент взаимодействует с "портом" (интерфейсом), который может быть реализован разными "адаптерами" (получающими данные из API, Local Storage, тестовых моков и т.д.).

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

Инкапсуляции взаимодействия с внешними библиотеками: Адаптеры могут оборачивать обращения к сторонним библиотекам (например, библиотеке для работы с картами), предоставляя Core простой и стабильный интерфейс.

Пример на JavaScript (схема):

HTTP-only куки — это тип кук, которые имеют флаг HttpOnly. Этот флаг запрещает доступ к куке из JavaScript через document.cookie.

Преимущества:

Усиление безопасности: Защита от XSS-атак. Если злоумышленник сможет внедрить вредоносный JavaScript-код на страницу, он не сможет получить доступ к HTTP-only кукам с помощью document.cookie.

Недостатки:

Ограниченный доступ из JavaScript: Невозможно чтение или изменение куки клиентским скриптом.

Применение:

Идеально подходит для хранения конфиденциальной информации, такой как идентификаторы сессий или токены. Браузер автоматически отправляет такую куку с каждым HTTP-запросом к серверу, но она не доступна на стороне клиента через JavaScript.

Установка HTTP-only куки на сервере (пример на Node.js с Express):

Критический путь рендеринга (Critical Rendering Path - CRP) — это последовательность шагов, которые браузер выполняет для преобразования HTML, CSS и JavaScript в отображаемые на экране пиксели. Оптимизация CRP позволяет ускорить отрисовку контента и улучшить время до интерактивности страницы.

Основные этапы CRP:

Построение DOM (Document Object Model): Браузер парсит HTML-код и создает древовидную структуру, представляющую содержимое страницы.

Построение CSSOM (CSS Object Model): Браузер парсит CSS-правила (из <style> тегов или внешних CSS-файлов) и создает древовидную структуру, представляющую стили для каждого элемента DOM. CSS является блокирующим ресурсом для рендеринга, так как браузер не может построить дерево Render Tree без информации о стилях.

Построение Render Tree (Дерево рендеринга): Объединение DOM (только видимые узлы) и CSSOM. Это дерево содержит все видимые элементы на странице и их соответствующие стили ready to be rendered.

Layout (Компоновка/Расчет): Браузер рассчитывает точное положение и размеры каждого элемента на странице.

Paint (Отрисовка): Браузер преобразует каждый элемент Render Tree в фактические пиксели на экране.

Composite (Композитинг): Браузер объединяет отрисованные слои в окончательное изображение.

Ресурсы, блокирующие рендеринг (такие как CSS и синхронный JavaScript в заголовке <head>), увеличивают время построения CRP. Оптимизация CRP включает в себя минимизацию или асинхронную загрузку блокирующих ресурсов, а также приоритизацию загрузки критически важного контента (выше "линии сгиба").

В HTML5 нет запрещенных тегов в абсолютном смысле. Однако, есть теги, которые считаются устаревшими (deprecated) или не рекомендованными к использованию, поскольку их функциональность либо утратила актуальность, либо была заменена более современными подходами (например, с помощью CSS).

Основные категории таких тегов:

Теги для форматирования, не имеющие семантического значения: Их использование сильно не рекомендуется, так как стилизация должна выполняться средствами CSS. Примеры:

<basefont>

<big>

<center>

<font>

<strike>

<tt>

<basefont>

<big>

<center>

<font>

<strike>

<tt>

Теги для управления фреймами: Устарели в пользу <iframe>. Примеры:

<frame>

<frameset>

<noframes>

<frame>

<frameset>

<noframes>

Другие устаревшие/нерекомендованные теги: Теги, которые либо дублировали функциональность других, либо имели специфическое применение, утратившее актуальность. Примеры:

<acronym> (заменён на <abbr>)

<applet> (заменён на <object>)

<dir> (заменён на <ul>)

<isindex> (функциональность реализована с помощью других форм)

<listing> (использовался для вывода листингов кода, заменён на <pre>)

<xmp> (аналог <listing>)

<plaintext> (игнорировался браузерами)

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

Движок браузера состоит из нескольких ключевых компонентов:

JavaScript-движок — отвечает за парсинг и выполнение JS-кода. Например, V8 в Chrome, SpiderMonkey в Firefox. Он компилирует JS в машинный код и оптимизирует выполнение.

Движок рендеринга (layout engine) — отвечает за разбор HTML и CSS, построение DOM и CSSOM, вычисление стилей, layout (размещение элементов) и отрисовку страницы. Пример — Blink в Chrome, Gecko в Firefox.

Сетевой слой — обрабатывает HTTP-запросы, загрузку ресурсов (HTML, CSS, JS, изображения).

Безопасность — включает политику того же источника (Same-Origin Policy), sandboxing, CORS, защиту от XSS и других атак.

Работа движка начинается с загрузки HTML, построения DOM, параллельно загружаются CSS и JS. После построения DOM и CSSOM происходит layout и paint. JS-движок может влиять на DOM, поэтому иногда происходит повторный layout.

Пример упрощенного жизненного цикла:

Изучал также оптимизации, например, как минимизировать reflow и repaint, lazy loading, работу с кэшом и асинхронную загрузку ресурсов.

Понимание алгоритмов и их оценки сложности (Big O) критически важно для фронтенд-разработчика, потому что:

Производительность: Фронтенд напрямую влияет на пользовательский опыт. Медленные или неоптимальные алгоритмы в рендеринге, обработке данных или интерактивности могут привести к зависаниям, долгой загрузке и общему негативному впечатлению. Знание алгоритмов помогает выбирать наиболее эффективные подходы для конкретных задач, минимизируя время выполнения и потребление ресурсов.

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

Реактивность и плавность интерфейса: Плавная анимация, быстрая реакция на действия пользователя, динамическое обновление контента – все это требует эффективной обработки данных и выполнения логики. Неэффективные алгоритмы могут вызывать "тормоза" и лаги в интерфейсе.

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

Улучшение качества кода: Знание алгоритмов ведет к написанию более чистого, поддерживаемого и легко масштабируемого кода. Разработчик способен предвидеть узкие места и писать решения, оптимальные не только сейчас, но и в будущем.

Решение сложных задач: Многие сложные фронтенд-задачи (например, построение сложных графиков, работа с виртуальным скроллингом, реализация drag-and-drop с пересортировкой) требуют применения нестандартных или оптимизированных алгоритмов.

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

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

Оценка сложности (Big O) позволяет:

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

Прогнозировать производительность: Оценивать, как изменится время выполнения или потребление памяти по мере роста объема данных.

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

Пример:

В приведенном примере, для поиска дубликатов в массиве, первый подход имеет квадратичную сложность O(n^2), что становится очень медленным для больших массивов. Второй подход, использующий Set, имеет линейную сложность O(n), что значительно эффективнее. Фронтенд-разработчику важно уметь выбирать или создавать более эффективные решения.

Протокол SOAP (Simple Object Access Protocol) — это основанный на XML протокол для обмена структурированной информацией в распределенных средах. Он используется для вызова процедур (удаленных методов) через HTTP, SMTP или другие транспортные протоколы.

Основные компоненты SOAP-сообщения:

Envelope: Корневой элемент XML-документа, определяет сообщение как SOAP.

Header: Необязательный элемент, может содержать информацию о маршрутизации, безопасности или транзакциях.

Body: Содержит данные вызова удаленной процедуры или ответа на него.

Fault: Необязательный элемент в теле сообщения, содержит информацию об ошибках.

Преимущества SOAP:

Независимость от платформы и языка: SOAP основан на XML, что делает его совместимым между различными технологиями.

Расширяемость: SOAP может быть расширен для добавления специфической функциональности (например, безопасность с WS-Security).

Строгая типизация: XML-схемы используются для определения структуры данных и типов параметров.

Надежность: Благодаря использованию транспортных протоколов с гарантированной доставкой (например, HTTP с подтверждениями).

Недостатки SOAP:

Сложность: XML-формат и накладные расходы на парсинг делают сообщения более громоздкими и ресурсоемкими по сравнению с более легковесными протоколами, такими как REST.

Низкая производительность: Из-за большого объема XML-сообщений и необходимости парсинга.

Зависимость от инструментария: Разработка с SOAP часто требует использования специализированных библиотек и инструментов.

Пример SOAP-сообщения (запрос):

Применение:

SOAP чаще всего используется в корпоративных приложениях, где важны формальные контракты, надежность и стандартизация. В веб-разработке для публичных API чаще применяется REST из-за его простоты и легковесности.

HttpOnly — это атрибут флага для файла cookie, который защищает его от доступа через клиентские скрипты (например, JavaScript с помощью document.cookie).

Основные преимущества:

Защита от XSS (Cross-Site Scripting): Злоумышленник, внедривший вредоносный скрипт на страницу, не сможет получить доступ к содержимому cookie с флагом HttpOnly и, например, украсть сессионный идентификатор пользователя.

Недостатки:

Не препятствует атакам CSRF (Cross-Site Request Forgery).

Не защищает от сетевых атак (например, man-in-the-middle), если соединение не зашифровано (HTTP вместо HTTPS).

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

Семантическое версионирование (Semantic Versioning, SemVer) — это соглашение для нумерации версий программного обеспечения. Формат версии: MAJOR.MINOR.PATCH.

MAJOR: Увеличивается при внесении обратно несовместимых изменений.

MINOR: Увеличивается при добавлении новой функциональности, которая обратно совместима. Всегда сбрасывает PATCH до 0.

PATCH: Увеличивается при внесении обратно совместимых исправлений (багфиксов).

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

Пример: 1.2.3-alpha.1+build.20231027.

Цель SemVer — помочь разработчикам предсказывать, как изменения в новой версии повлияют на их код, использующий этот пакет.

DCI (Data-Context-Interaction) — это паттерн проектирования, который разделяет бизнес-логику на три составляющие: Данные (Data), Контекст (Context) и Взаимодействие (Interaction), чтобы улучшить читаемость и поддерживаемость ООП-кода, особенно при работе с изменяющимися требованиями.

Данные (Data): Объекты, представляющие предметную область, содержащие только состояние (атрибуты). Они не содержат бизнес-логики.

Контекст (Context): Точка входа для сценария использования. Он связывает объекты данных с их ролями в рамках конкретного взаимодействия, используя их "методы ролей".

Взаимодействие (Interaction): Бизнес-логика сценария использования, реализованная как набор ролей и их методов. Это поведенческий аспект системы, который "накладывается" на объекты данных. Часто реализуется через примеси (mixins) или трейты (traits), которые динамически добавляют поведение объектам данных в рамках контекста.

Главная идея DCI — сделать намерения разработчика, связанные с выполнением конкретного сценария использования, явными в коде. Бизнес-логика не привязана жестко к классам данных, что позволяет одному и тому же объекту играть разные роли в разных сценариях.

Преимущества:

Улучшенная читаемость и понимание бизнес-логики сценария.

Уменьшение связанности между классами данных и поведением.

Более легкая поддержка и изменение существующих сценариев.

Сохранение чистоты объектов данных (Model в MVC).

Пример концепции: Объект Аккаунт может играть роль Источник при переводе денег и роль Получатель при зачислении. Логика перевода (вычесть из источника, добавить получателю) находится в контексте перевода, а не жестко внутри класса Аккаунт.

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

Существует несколько способов обмена данными:

PostMessage API:

Основной и безопасный способ.

Позволяет отправлять сообщения между окнами (iframe/родительским, разными окнами/вкладками) из разных доменов.

Важно указывать ожидаемый origin для предотвращения XSS-атак.

// Отправка из родительского окна в iframe

const iframeEl = document.getElementById('my-iframe');

iframeEl.contentWindow.postMessage('Привет из родителя!', 'https://trusted-domain.com');

// Прием в iframe

window.addEventListener('message', function(event) {

if (event.origin !== 'https://parent-domain.com') {

return; // Проверяем отправителя

}

console.log('Получено сообщение в iframe:', event.data);

});

// Отправка из iframe в родительское окно

window.parent.postMessage('Привет из iframe!', 'https://parent-domain.com');

// Прием в родительском окне

if (event.origin !== 'https://iframe-domain.com') {

}

console.log('Получено сообщение в родителе:', event.data);

});

PostMessage API:

Доступ к window объекту (только для Same-Origin):

Если iframe и родительская страница находятся на одном домене (Same-Origin Policy), они могут напрямую получить доступ к объектам window друг друга.

// В родительском окне

const iframeWindow = iframeEl.contentWindow;

iframeWindow.someVariable = 'Значение из родителя';

iframeWindow.someFunction();

// В iframe

const parentWindow = window.parent;

console.log(parentWindow.someVariable);

parentWindow.someOtherFunction();

URL хеш (#):

Простой, но ограниченный способ.

Можно передавать небольшие данные, изменяя хеш в URL iframe.

Изменения хеша не перезагружают страницу.

Требует отслеживания изменения хеша (например, через hashchange событие).

// В родительском окне, для изменения хеша iframe

iframeEl.contentWindow.location.hash = 'data=some_value';

// В iframe, для чтения хеша

window.addEventListener('hashchange', function() {

const hash = window.location.hash;

console.log('Хеш изменился:', hash);

// Парсинг данных из хеша

});

URL хеш (#):

Изменение location.href (менее предпочтительно):

Передача данных путем изменения URL iframe и его последующей перезагрузки.

Плохо подходит для частого обмена.

Разделяемые хранилища (Same-Origin):

localStorage и sessionStorage.

Если iframe и родитель находятся на одном домене, они могут использовать одни и те же хранилища.

localStorage.setItem('sharedData', JSON.stringify({ value: 'Пример' }));

// В iframe

const data = JSON.parse(localStorage.getItem('sharedData'));

console.log('Данные из localStorage:', data);

Выбор метода зависит от политики безопасности (Same-Origin Policy) и объема/типа передаваемых данных. PostMessage является наиболее гибким и безопасным решением для кросс-доменного обмена.

REST и GraphQL — это разные подходы к построению API, каждый со своими преимуществами и недостатками. Выбор зависит от специфики проекта.

REST

Преимущества: Прост в понимании и реализации, широко используется, хорошо подходит для простых API с четко определенными ресурсами, поддерживает кэширование на уровне HTTP.

Недостатки: Может приводить к "over-fetching" (получение избыточных данных) или "under-fetching" (недостаток данных), требует множества запросов для получения связанных ресурсов, сложность в управлении версиями API.

GraphQL

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

Недостатки: Более сложен в освоении и реализации на бэкенде, требует дополнительной инфраструктуры (сервер GraphQL), может быть менее эффективен для простых запросов, кэширование менее тривиально по сравнению с REST.

Сравнительная таблица

Примеры кода

REST - Получение пользователя и его постов:

// Получение пользователя

fetch('/users/1')

.then(response => response.json())

.then(user => {

console.log(user);

// Получение постов пользователя

fetch(`/users/${user.id}/posts`)

.then(posts => {

console.log(posts);

});

});

GraphQL - Получение пользователя и его постов в одном запросе:

query {

user(id: "1") {

id

name

posts {

id

title

}

}

}

Выводы:

Для простых API, статических данных, или если важна простота и скорость разработки, REST может быть лучшим выбором.

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

Часто в реальных проектах используется комбинация обоих подходов, где для одних задач используется REST, а для других — GraphQL.

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

Типичный формат коммита:

<тип>: Обязательный. Определяет тип изменений (например, feat для новой функциональности, fix для исправления ошибки, docs для изменений в документации, style для форматирования, refactor для рефакторинга, test для добавления тестов, ci для изменений в CI/CD).

(область): Необязательно. Указывает область приложения, на которую влияют изменения (например, (компонент: кнопку), (сервис: авторизация)).

<описание>: Обязательное. Краткое и точное описание изменений в императивном наклонении (например, "добавить кнопку", "исправить ошибку валидации").

<тело>: Необязательное. Более подробное объяснение изменений, причин их внесения и контекста. Разделяется пустой строкой от описания.

<сноска>: Необязательная. Обычно используется для указания BREAKING CHANGE с описанием, почему это изменение ломает обратную совместимость, или ссылок на задачи (например, Closes #123).

Примеры:

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

В браузере основным движком Event Loop является движок JavaScript (например, V8). Обработка задач происходит в главном, единственном потоке. Задачи делятся на макротаски (DOM события, таймеры, I/O) и микротаски (Promise, MutationObserver). Цикл Event Loop сначала выполняет все доступные микротаски после каждой макротаски.

На сервере (Node.js) Event Loop реализован библиотекой libuv. Он многопоточный и использует пул потоков для выполнения блокирующих операций ввода/вывода (файловые операции, сетевые запросы к другим хостам). Event Loop в Node.js также имеет фазы (таймеры, ожидающие колбэки, poll, check, close callbacks), которые обрабатываются в определенном порядке. Микротаски обрабатываются после каждой фазы.

Ключевые отличия:

Поточность: Браузер - однопоточный главный поток. Node.js - однопоточный цикл Event Loop + пул потоков для I/O.

Источники задач: В браузере - DOM события, таймеры, сетевые запросы из браузерного API. В Node.js - файловые операции, сетевые запросы, таймеры.

Фазы: В Node.js Event Loop имеет явно выраженные фазы, в браузере модель более абстрактна и фокусируется на макро/микротасках.

Остановить можно, вызвав метод terminate() у экземпляра worker'а. Это немедленно завершит его выполнение, без ожидания завершения текущих задач.

Уничтожение объекта web worker'а происходит автоматически сборщиком мусора JavaScript после того, как на него не останется ссылок и его ресурсы будут освобождены.

Пример остановки:

Важно помнить, что после вызова terminate(), worker перестает реагировать на любые сообщения и не может быть перезапущен. Для новой задачи необходимо создать новый экземпляр Worker.

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

Основные концепции и механизмы:

Event Loop: Сердце асинхронности в JavaScript. Он постоянно проверяет очередь сообщений (Message Queue) и, если стек вызовов (Call Stack) пуст, перемещает задачи из очереди в стек для выполнения.

Callback Functions: Функции, которые передаются в качестве аргументов другим функциям и вызываются после завершения асинхронной операции.

Promises: Объект, представляющий конечное завершение (или сбой) асинхронной операции и ее результирующее значение. Позволяют строить более читаемый асинхронный код, избегая "callback hell".

// Пример промиса

const myPromise = new Promise((resolve, reject) => {

setTimeout(() => {

resolve("Операция завершена успешно!");

}, 1000);

});

myPromise

.then(result => {

console.log(result); // Выведется через 1 секунду

})

.catch(error => {

console.error(error);

});

Async/Await: Синтаксический сахар над промисами, делающий асинхронный код более похожим на синхронный, что улучшает читаемость и упрощает обработку ошибок.

// Пример с async/await

async function fetchData() {

try {

const response = await fetch('https://api.example.com/data');

const data = await response.json();

console.log(data);

} catch (error) {

console.error('Ошибка при получении данных:', error);

}

}

fetchData();

Сравнение механизмов:

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

Существует несколько способов клонирования объектов в JavaScript:

Поверхностное копирование (shallow copy) — копирует только верхний уровень свойств, вложенные объекты остаются ссылками.

Использование Object.assign():const clone = Object.assign({}, original);

Оператор расширения:const clone = {...original};

Глубокое копирование (deep copy) — создает полную копию объекта со всеми вложенными объектами.

Сериализация и десериализация через JSON (работает только с простыми объектами без функций, дат и т.п.):const deepClone = JSON.parse(JSON.stringify(original));

Использование специализированных библиотек, например lodash:const _ = require('lodash');

const deepClone = _.cloneDeep(original);

Реализация собственного метода клонирования — если объект содержит методы или сложные типы, можно реализовать метод clone() внутри класса.

Выбор способа зависит от структуры объекта и требований к копированию. Для простых объектов достаточно поверхностного копирования, для сложных — глубокого.

Сеть доставки контента (Content Delivery Network). Распределенная по всему миру сеть серверов, предназначенная для ускорения загрузки веб-ресурсов (статических файлов: изображений, видео, CSS, JavaScript и т.д.) для конечных пользователей.

Принцип работы:

Контент копируется на множество серверов, расположенных в разных географических точках.

Когда пользователь запрашивает контент, CDN определяет ближайший к нему сервер.

Контент доставляется пользователю с ближайшего сервера, что сокращает время задержки и ускоряет загрузку.

Преимущества использования CDN:

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

Снижение нагрузки на основной сервер: Часть трафика обрабатывается серверами CDN.

Повышение доступности: Если один сервер CDN недоступен, запросы перенаправляются на другие.

Улучшение SEO: Скорость загрузки сайта является фактором ранжирования в поисковых системах.

Пример использования CDN для подключения библиотеки:

Стандартный <script> останавливает парсинг HTML, загружает скрипт и исполняет его, затем парсинг продолжается.

<script async> загружает скрипт асинхронно, пока парсинг HTML продолжается. Скрипт исполняется сразу после загрузки. Порядок исполнения не гарантирован.

<script defer> загружает скрипт асинхронно, пока парсинг HTML продолжается. Скрипт исполняется только после завершения парсинга HTML, но до срабатывания события DOMContentLoaded. Порядок исполнения скриптов с defer сохраняется в соответствии с их появлением в HTML.

Сравнительная таблица:

Пример:`

Graceful shutdown — это процесс корректного завершения работы приложения, который позволяет ему перед остановкой завершить текущие операции, освободить ресурсы и сохранить состояние.

Применение graceful shutdown:

Выкатка новых версий приложения: Позволяет плавно заменить старую версию на новую без потери данных или прерывания пользовательских сессий.

Плановые перезагрузки или техническое обслуживание: Гарантирует, что все открытые соединения будут закрыты, а задачи завершены до остановки сервиса.

Реагирование на сбои или масштабирование: При снижении нагрузки или возникновении проблем, сервисы могут быть корректно остановлены.

Пример реализации на Node.js:

В примере, при получении сигнала SIGTERM, сервер перестает принимать новые соединения, но позволяет уже установленным завершиться. Через заданное время происходит принудительное завершение процесса на случай, если закрытие затягивается.

Полный URL-адрес ресурса, включающий протокол (например, http, https), доменное имя, путь к файлу и опционально порт и якорь. Указывает на ресурс независимо от текущего местоположения пользователя.

React не является реактивным в классическом понимании реактивного программирования, потому что он не отслеживает изменения данных автоматически и не обновляет UI в ответ на любые изменения состояния без явного вызова.

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

В реактивных фреймворках (например, Vue, Svelte, MobX) изменения данных автоматически отслеживаются и приводят к обновлению UI без необходимости вручную вызывать обновления.

Пример в React:

Здесь React не «реагирует» на изменение переменной count, а обновляет UI только после вызова setCount, что отличается от реактивных систем, где изменение переменной автоматически отражается в UI.

Веб-воркеры — это способ выполнения скриптов в фоновых потоках, отдельные от основного потока UI.

Их основное назначение:

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

Параллелизм: Возможность выполнять несколько задач одновременно, используя многоядерные процессоры более эффективно.

Существуют три типа веб-воркеров:

Dedicated Workers: Связаны с конкретной страницей и доступны только ей.

Shared Workers: Могут быть доступны из нескольких страниц или вкладок одного домена.

Service Workers: Работают как сетевые прокси, перехватывая сетевые запросы и позволяя реализовать офлайн-функциональность, пуш-уведомления и другие возможности прогрессивных веб-приложений (PWA).

Принципы работы:

Отдельный контекст: У воркера свой глобальный объект и доступ к подмножеству API браузера (нет доступа к DOM, window, document, parent).

Обмен сообщениями: Взаимодействие между основным потоком и воркером происходит через сообщения с помощью postMessage() и обработчика событий onmessage. Данные передаются по значению (копируются) или по ссылке (Transferable Objects).

Пример использования Dedicated Worker:

Преимущества:

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

Изоляция изменений: Изменения в API бэкенда не обязательно влияют на фронтенд, так как BFF может адаптировать ответы под потребности клиента.

Гибкость в работе с разными источниками данных: BFF может агрегировать данные из нескольких бэкенд-сервисов, предоставляя фронтенду удобный унифицированный API.

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

Логика специфичная для клиента: Позволяет размещать логику, специфичную только для одного типа клиентов, изолируя ее от основной бизнес-логики.

Недостатки:

Усложнение архитектуры: Введение дополнительного слоя (BFF) увеличивает сложность системы и требует дополнительных усилий на разработку, развертывание и поддержку.

Дублирование логики: Некоторая логика может быть дублирована в BFF и в основном бэкенде, если не продумать четкое разделение ответственности.

Увеличение задержки: Дополнительный сетевой прыжок между фронтендом и основным бэкендом через BFF может незначительно увеличить задержку.

Необходимость дополнительных ресурсов: Каждый BFF инстанс требует собственных вычислительных ресурсов.

Порог вхождения: Разработка и поддержка BFF требует специфических навыков для работы на серверной стороне.

HMAC (Hash-based Message Authentication Code) — это механизм для проверки подлинности и целостности сообщения с использованием хеш-функции и секретного ключа.

Работает он следующим образом:

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

В результате получается уникальный хеш-код (HMAC).

Этот HMAC отправляется вместе с сообщением.

Получатель, имея то же сообщение и секретный ключ, заново вычисляет HMAC.

Если вычисленный HMAC совпадает с полученным, считается, что сообщение не было изменено и пришло от источника, владеющего секретным ключом.

HMAC_K(m) = Hash((K ⊕ opad) || Hash((K ⊕ ipad) || m))

Где:

K — секретный ключ.

m — сообщение.

Hash — используемая хеш-функция (например, SHA-256).

|| — конкатенация.

⊕ — операция XOR.

ipad (inner pad) и opad (outer pad) — константы, используемые в алгоритме.

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

Применение в веб-разработке:

Bearer tokens: Для создания и верификации токенов аутентификации.

Подпись запросов: Для проверки подлинности запросов к API.

Верификация данных: Гарантия того, что данные не были изменены при передаче.

Преимущества:

Высокая криптографическая стойкость.

Совместим с различными хеш-функциями.

Прост в реализации.

В контексте веб-разработки, особенно во фреймворках типа Angular, пайп (pipe) — это функция, преобразующая входные данные в желаемый формат для отображения в шаблоне. Пайпы используются для фильтрации, форматирования или сортировки данных без изменения самой модели данных. Они повышают переиспользуемость кода и улучшают читаемость шаблона.

Примеры использования:

Форматирование дат: превращение объекта Date в строку определенного формата (MM/dd/yyyy).

Форматирование валюты: добавление знака валюты и форматирование числа.

Изменение регистра текста: преобразование текста в верхний или нижний регистр.

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

Сортировка списков: упорядочивание элементов списка.

Синтаксис использования в шаблоне Angular:

В других контекстах веб-разработки (например, в серверной части Node.js или при работе с потоками данных) "pipe" может относиться к механизму передачи вывода одной команды или функции на вход другой, что позволяет строить цепочки операций. Например, в Node.js stream.pipe() используется для передачи данных из потока чтения в поток записи.

Однако, наиболее распространенное значение термина "pipe" в контексте фронтенд-разработки, особенно в рамках современных фреймворков, связано именно с преобразованием данных для отображения в шаблонах.

Существует два основных способа реализации роутинга на фронтенде:

Клиентский роутинг: Роутинг обрабатывается непосредственно в браузере с помощью JavaScript.

Принцип работы: При навигации страница не перезагружается полностью. JavaScript изменяет URL в адресной строке браузера (используя History API) и динамически подгружает или перерисовывает необходимые компоненты или части страницы.

Состояние URL:

Использует History API (pushState, replaceState). URL чистый, без #.

Может использовать хэш (#) в URL (устаревший подход с #).

Преимущества:

Улучшенный пользовательский опыт (быстрее переходы, без полной перезагрузки).

Подходит для одностраничных приложений (SPA).

Недостатки:

Требует дополнительной настройки сервера для корректной обработки прямых заходов по URL (например, настроить fallback на index.html).

Могут быть проблемы с SEO, если контент рендерится только на клиенте (решается SSR или пререндерингом).

Популярные библиотеки/фреймворки: React Router, Vue Router, Angular Router.

Состояние URL:

Преимущества:

Недостатки:

Серверный роутинг: Роутинг полностью обрабатывается на стороне сервера.

Принцип работы: При каждом запросе на новый URL браузер отправляет запрос на сервер. Сервер обрабатывает запрос, определяет, какой контент нужно вернуть, и отправляет полноценную HTML-страницу клиенту. Браузер полностью перезагружает страницу.

Состояние URL: URL соответствует реальному ресурсу на сервере.

Преимущества:

Простота реализации для многостраничных приложений (MPA).

Отличная индексация поисковыми системами (SEO), так как контент готов сразу.

Не требует JavaScript для базовой навигации.

Недостатки:

Медленнее переходы между страницами из-за полной перезагрузки.

Менее интерактивный пользовательский опыт по сравнению с SPA.

Примеры: Наследование от традиционных веб-приложений, где каждый URL соответствует отдельному файлу или маршруту на сервере (например, PHP, Python с фреймворками типа Django/Flask, Node.js с Express и т.д.).

Преимущества:

Недостатки:

В современных фронтенд-приложениях, особенно в SPA, клиентский роутинг является стандартом. Серверный роутинг чаще используется для многостраничных сайтов или в сочетании с клиентским роутингом для улучшенного SEO (например, SSR).

User agent стили — это набор стилей CSS по умолчанию, которые браузеры применяют к HTML-элементам до загрузки любых авторских таблиц стилей или таблиц стилей пользователя. Они обеспечивают базовый вид элементов (например, отступы у абзацев, жирный текст у <strong>).

Работать с ними можно следующими способами:

Переопределение стилей: Авторские стили с более высокой специфичностью или после в коде CSS переопределяют user agent стили.

p {

margin: 0; /* Переопределяет стандартный margin */

}

Использование CSS Reset или Normalize.css: Эти библиотеки обнуляют или унифицируют базовые стили элементов, нейтрализуя различия в user agent стилях разных браузеров.

Reset CSS: Полностью удаляет большинство user agent стилей. Пример:/* Пример из Eric Meyer's Reset CSS */

html, body, div, span, applet, object, iframe,

h1, h2, h3, h4, h5, h6, p, blockquote, pre,

a, abbr, acronym, address, big, cite, code,

del, dfn, em, img, ins, kbd, q, s, samp,

small, strike, strong, sub, sup, tt, var,

b, u, i, center,

dl, dt, dd, ol, ul, li,

fieldset, form, label, legend,

table, caption, tbody, tfoot, thead, tr, th, td,

article, aside, canvas, details, embed,

figure, figcaption, footer, header, hgroup,

menu, nav, output, ruby, section, summary,

time, mark, audio, video {

margin: 0;

padding: 0;

border: 0;

font-size: 100%;

font: inherit;

vertical-align: baseline;

}

/* Остальная часть reset стилей */

Normalize.css: Корректирует user agent стили, чтобы сделать их более согласованными между браузерами, сохраняя при этом полезные базовые стили./* Пример из Normalize.css */

html {

line-height: 1.15; /* 1 */

-webkit-text-size-adjust: 100%; /* 2 */

}

body {

margin: 0;

}

/* Остальная часть normalize стилей */

b, u, i, center,

margin: 0;

padding: 0;

border: 0;

font-size: 100%;

font: inherit;

}

html {

}

body {

margin: 0;

}

Проверка в DevTools: Инструменты разработчика в браузере позволяют увидеть, какие user agent стили применяются к конкретному элементу.

Понимание user agent стилей важно для предсказуемого отображения элементов и обеспечения кроссбраузерности.

Реактивность во Vue.js основана на паттерне "наблюдатель" (Observer).

Когда создается экземпляр Vue, он проходит по всем свойствам в объекте data и преобразует их в геттеры и сеттеры с использованием Object.defineProperty в Vue 2 или Proxy в Vue 3.

Vue 2:

При доступе к свойству (геттер) Vue отслеживает, какой компонент его использует.

При изменении свойства (сеттер) Vue уведомляет все компоненты, которые его отслеживали, о необходимости обновления (перерисовать шаблон).

Vue 3:

Использует Proxy для перехвата всех операций с объектом (добавление, удаление свойств, изменение значений и т.д.), обеспечивая более полную реактивность по сравнению с Vue 2.

Благодаря Proxy, реактивность Vue 3 не имеет ограничений Vue 2 (не требуется $set для добавления новых свойств).

Ключевые компоненты реактивности:

Observer: Преобразует свойства данных в геттеры/сеттеры (Vue 2) или обрабатывает их с помощью Proxy (Vue 3).

Dep (Dependency): Объект, ассоциированный с каждым реактивным свойством, который хранит список зависимых Watcher'ов.

Watcher: Представляет компонент, вычисляемое свойство или watchers, которые зависят от реактивных данных. Когда данные меняются, Watcher уведомляется и запускает соответствующее действие (например, перерисовку компонента).

Пример (Vue 2):

ES-модули — это стандартный способ организации кода в JavaScript, основанный на синтаксисе import и export.

Основные особенности:

Стандарт: Часть спецификации ECMAScript, поддерживаемая большинством современных браузеров и сред выполнения, таких как Node.js.

Явное управление зависимостями: Модули явно указывают, какие значения они экспортируют и какие значения импортируют из других модулей.

Статическая структура: Зависимости модулей определяются на этапе компиляции (или парсинга), а не выполнения. Это позволяет инструментам статического анализа, таким как бандлеры (Webpack, Rollup), оптимизировать код.

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

Поддержка асинхронности: Модули могут загружаться асинхронно в браузерах, не блокируя загрузку страницы.

Синтаксис:

Экспорт:// app.js

export const myVariable = 123; // Именованный экспорт

export function myFunction() { // Именованный экспорт

console.log('Hello from module!');

}

export default class MyClass { // Экспорт по умолчанию

constructor() {

console.log('MyClass instance created');

}

}

Импорт:// main.js

import { myVariable, myFunction } from './app.js'; // Именованный импорт

import MyDefaultClass from './app.js'; // Импорт по умолчанию

console.log(myVariable); // 123

myFunction(); // Hello from module!

const instance = new MyDefaultClass(); // MyClass instance created

Сравнение с CommonJS (Node.js):

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

До появления Promise широко использовались колбэки (callbacks).

Суть колбэков:

Функция передается в другую функцию в качестве аргумента и выполняется после завершения определенной операции.

Пример:

Недостатки использования только колбэков:

Callback Hell (Ад колбэков): Череда вложенных асинхронных операций приводит к сильно вложенному и трудночитаемому коду.

Сложности с обработкой ошибок: Требуется передавать и проверять ошибки на каждом уровне вложенности.

Отсутствие стандартизированного подхода: Разные библиотеки могли использовать разные соглашения для колбэков.

Альтернативные подходы до Promise:

До появления Promise, кроме "голых" колбэков, использовались библиотеки и паттерны для управления асинхронностью, такие как:

Асинхронные библиотеки: Например, async (для Node.js, но могла использоваться и в браузере). Предоставляли утилиты для управления асинхронными потоками (параллельное выполнение, последовательное выполнение).

Publisher/Subscriber (Pub/Sub) паттерн: Для более сложных сценариев взаимодействия.

Событийные эми

ттеры: В Node.js и браузерном окружении (например, EventEmitter).

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

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

Пример Observable:

Пример Promise:

Глобальный контекст, контекст функции и контекст eval. С появлением let и const также возникает блочный контекст области видимости (lexical environment block).

Шаблонные литералы (или шаблонные строки) — это синтаксис для создания строк в JavaScript, который обеспечивает ряд преимуществ по сравнению с обычными строковыми литералами (с одинарными или двойными кавычками).

Ключевые особенности шаблонных литералов:

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

// Обычная строка с переносом

const classicString = 'Строка 1\nСтрока 2';

// Шаблонная строка с переносом

const templateString = `Строка 1

Строка 2`;

Вложенные выражения: Позволяют включать выражения JavaScript непосредственно в строку с помощью синтаксиса ${выражение}. Результат выражения будет интерполирован (вставлен) в строку.

const name = 'Мир';

const greeting = `Привет, ${name}!`; // greeting будет "Привет, Мир!"

const sum = `Сумма 2 + 3 равна ${2 + 3}.`; // sum будет "Сумма 2 + 3 равна 5."

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

function highlight(strings, ...values) {

let result = '';

strings.forEach((str, i) => {

result += str;

if (values[i]) {

result += `**${values[i]}**`; // Добавляем подсветку

}

});

return result;

}

const item = 'JavaScript';

const description = highlight`Изучаем ${item} сегодня!`;

// description будет "Изучаем **JavaScript** сегодня!"

Обычные строковые литералы используют одинарные ('...') или двойные ("...") кавычки. Они просты и прямолинейны, но требуют использования маскирующих символов (\n для переноса строки, \' или \" для включения кавычек того же типа внутри строки) и конкатенации (+) для объединения переменных или выражений со строками.

Шаблонные литералы обозначаются обратными кавычками (`...`). Они значительно повышают читаемость кода при работе со строками, особенно при необходимости включения переменных или создании многострочного текста.

Необъявленные переменные в JavaScript — это переменные, которые не были объявлены с использованием ключевых слов var, let или const, но которым было присвоено значение.

Поведение необъявленных переменных зависит от режима исполнения кода:

Нестрогий режим: Присваивание значения необъявленной переменной создает глобальную переменную с таким именем в Window (для браузеров). Это считается плохой практикой, так как загрязняет глобальную область видимости и усложняет отладку.

Строгий режим ('use strict'): Присваивание значения необъявленной переменной вызывает ошибку ReferenceError. Это предпочтительное поведение, которое предотвращает случайное создание глобальных переменных и способствует более предсказуемому коду.

Проверка на необъявленную переменную может быть выполнена с помощью typeof. Необъявленная переменная в нестрогом режиме будет иметь тип 'undefined', как и переменная, объявленная, но не инициализированная.

Использование необъявленных переменных является источником потенциальных ошибок и затрудняет поддержку кода. Всегда рекомендуется явно объявлять переменные с помощью let, const или var (хотя var имеет свои особенности и в современном коде чаще используются let и const). Использование строгого режима ('use strict') является стандартной практикой для предотвращения такого рода проблем.

Для последнего коммита:

Это изменит сообщение последнего коммита.

Для более старого коммита:

Найти хеш коммита, который нужно переименовать:git log

Запустить интерактивный ребейз, указав коммит перед тем, который нужно изменить:git rebase -i <хеш_коммита_перед_нужным>

В открывшемся редакторе изменить слово pick на reword (или r) у нужного коммита. Сохранить и закрыть файл.

Git откроет новый редактор для изменения сообщения выбранного коммита. Изменить сообщение, сохранить и закрыть.

Если коммит уже был отправлен в удаленный репозиторий, потребуется force push (использовать с осторожностью):git push --force

IIFE — это функция, которая объявляется и сразу же выполняется.

Объявление и вызов: Сразу после объявления (внутри круглых скобок) следует оператор (), который вызывает эту функцию.

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

Изоляция кода: Позволяет избежать конфликтов имен при использовании нескольких JavaScript-библиотек или скриптов.

Решение проблем с замыканиями в циклах: Помогает "захватить" правильное значение переменной в каждой итерации.

Angular работает на основе архитектуры MVC/MVVM. Он использует компоненты, привязку данных и внедрение зависимостей для построения интерактивных веб-приложений.

Основные концепции:

Компоненты: Строительные блоки приложения, состоящие из шаблона, класса и стилей.

Модули (NgModules): Объединяют связанные компоненты, сервисы и другие элементы.

Привязка данных: Синхронизирует данные между моделью и представлением.

Односторонняя привязка: Данные текут в одном направлении (например, от модели к представлению).

Двусторонняя привязка [()]: Изменения в модели влияют на представление и наоборот.

Сервисы: Предоставляют функциональность, которая может быть использована разными компонентами (например, работа с HTTP-запросами).

Внедрение зависимостей (DI): Механизм предоставления зависимостей (например, сервисов) компонентам.

Шаблоны: HTML с расширениями Angular-синтаксиса для отображения данных и обработки событий.

Директивы: Изменяют поведение или внешний вид элементов DOM.

Структурные: Добавляют, удаляют или манипулируют элементами DOM (например, *ngIf, *ngFor).

Атрибутные: Изменяют внешний вид или поведение существующего элемента (например, [ngClass], [ngStyle]).

Жизненный цикл компонентов: Набор хуков, позволяющих выполнять код на различных этапах жизни компонента (например, OnInit, OnDestroy).

Зоны (NgZone): Обеспечивают автоматическое обнаружение изменений и обновление представления при асинхронных операциях.

Процесс работы:

Загрузка: Приложение загружается, Bootstrap-модуль запускает корневой компонент.

Рендеринг: Angular компилирует шаблоны и создает структуру DOM.

Привязка данных: Устанавливается связь между данными и представлением.

Обработка событий: Слушатели событий реагируют на действия пользователя.

Обнаружение изменений: Зона отслеживает асинхронные операции.

Обновление представления: При обнаружении изменений Angular обновляет соответствующие части DOM.

Пример двусторонней привязки данных:

Проверить правильность иерархии заголовков можно несколькими способами:

Визуальный анализ кода: Простой просмотр HTML-структуры позволяет быстро выявить очевидные ошибки, например, пропуск уровней (<h1> сразу после <h3>).

<!-- Неправильная иерархия (нет <h2>) -->

<h1>Главный заголовок</h1>

<h3>Подзаголовок</h3>

<!-- Правильная иерархия -->

<h2>Подзаголовок</h2>

<h3>Под-подзаголовок</h3>

Использование инструментов разработчика (DevTools): В браузере можно просмотреть структуру DOM и явно увидеть порядок следования тегов заголовков. Во многих браузерах есть вкладки для просмотра DOM-дерева.

Применение валидаторов HTML: Онлайн-валидаторы (например, от W3C) анализируют весь HTML-документ и могут указать на проблемы с семантикой и структурой, включая неправильное использование заголовков. Однако они не гарантируют проверку именно иерархии в контексте всего документа.

Использование расширений для браузера и специальных инструментов: Существуют расширения (например, SEO-анализаторы) и отдельные программы, которые сканируют веб-страницу и предоставляют отчет о структуре заголовков, их уровнях и вложенности. Некоторые из них визуализируют иерархию.

Написание скриптов: Можно использовать скрипты (например, на JavaScript) для извлечения всех элементов заголовков (<h1> - <h6>) и анализа их порядка в документе.

// Получаем все элементы заголовков

const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');

let currentLevel = 0;

let isCorrect = true;

headings.forEach(heading => {

const level = parseInt(heading.tagName.substring(1)); // Извлекаем уровень (1-6)

if (currentLevel === 0) {

// Первый заголовок должен быть h1

if (level !== 1) {

isCorrect = false;

console.error('Первый заголовок не h1:', heading);

}

} else {

// Следующий уровень не должен быть больше текущего+1

if (level > currentLevel + 1) {

isCorrect = false;

console.error('Найден пропуск уровня заголовка:', heading, 'после уровня', currentLevel);

}

}

currentLevel = level;

});

if (isCorrect) {

console.log('Иерархия заголовков, вероятно, правильная.');

} else {

console.error('Иерархия заголовков нарушена.');

}

Ручное тестирование с использованием инструментов доступности (Accessibility Tools): Некоторые инструменты для проверки доступности (например, Lighthouse в Chrome DevTools) анализируют структуру заголовков и могут давать рекомендации.

Просмотр структуры документа в специальных SEO-инструментах: Многие SEO-платформы имеют функции анализа структуры заголовков на странице.

Важно помнить, что "правильная" иерархия означает последовательное использование уровней (h1, h2, h3...) без пропусков, за исключением случаев, когда более низкий уровень (например, h4) встречается после более высокого (например, h2 без h3 между ними), но вложенность в этом случае должна быть логичной. Обычно рекомендуется начинать с <h1> и переходить к <h2>, затем <h3> и так далее, не пропуская уровни. И на странице должен быть только один <h1>.

Данные сессии хранятся на сервере.

Сопоставление HTTP-запроса с данными сессии происходит через идентификатор сессии (Session ID).

Создание сессии: При первом запросе клиента сервер создает новую сессию, генерирует уникальный Session ID и отправляет его клиенту в виде Cookie (чаще всего) или включается в URL.

Хранение Session ID у клиента: Браузер клиента сохраняет Session ID (в Cookie).

Последующие запросы: При каждом последующем запросе на тот же домен браузер автоматически отправляет Session ID обратно на сервер в заголовке Cookie.

Сопоставление на сервере: Сервер получает Session ID из запроса, ищет соответствующие данные сессии в своем хранилище (память, файловая система, база данных, Redis и т.д.) и использует их для обработки запроса.

Пример заголовка запроса с Session ID в Cookie:

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

Основные подходы:

Ленивая загрузка (Lazy Loading): Загрузка изображений, видео или других ресурсоемких элементов только тогда, когда они становятся видимыми во viewport пользователя.

<!-- изображение с lazy loading -->

<img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="Описание изображения">

Потоковый рендеринг (Streaming Rendering): Отправка HTML-кода частями, позволяя браузеру начать парсинг и рендеринг до получения всей страницы.

Рендеринг на стороне сервера (SSR) или Гидратация (Hydration): Генерация начального HTML на сервере для быстрого отображения контента, затем "оживление" статической разметки на клиенте с помощью JavaScript для интерактивности.

// Пример концепции гидратации

// На клиенте

const appElement = document.getElementById('app');

// Монтируем приложение Vue/React/etc. на уже существующую DOM-структуру

Vue.createApp(App).mount(appElement);

// или React.hydrate(<App />, appElement);

Prioritizing visible content (Above-the-fold rendering): Загрузка и рендеринг контента, видимого в первой части страницы (не прокручивая), с отложенной загрузкой остального.

Применение прогрессивного рендеринга улучшает основные метрики Web Vitals:

Largest Contentful Paint (LCP): Ускоряется отображение самого крупного элемента.

First Contentful Paint (FCP): Уменьшается время до появления первого контента.

Cumulative Layout Shift (CLS): Хотя не напрямую, грамотное использование placeholders и lazy loading может помочь уменьшить неожиданные сдвиги контента.

Взаимодействие осуществляется через протокол HTTP(S) с использованием различных методов (GET, POST, PUT, DELETE и т.д.). Frontend отправляет запросы на backend для получения или отправки данных. Backend обрабатывает эти запросы, взаимодействует с базой данных при необходимости и отправляет ответ.

Основные способы взаимодействия:

REST API: Наиболее распространенный подход. Backend предоставляет набор конечных точек (endpoints), к которым frontend может обращаться для выполнения операций. Данные обычно передаются в формате JSON.

// Пример JSON ответа от backend

{

"users": [

{

"id": 1,

"name": "Alex"

},

{

"id": 2,

"name": "Elena"

}

]

}

GraphQL: Альтернатива REST. Frontend запрашивает только те данные, которые ему нужны, в одном запросе. Это позволяет уменьшить количество запросов и объем передаваемых данных.

WebSockets: Позволяют установить двустороннее постоянное соединение между frontend и backend. Используется для приложений, требующих обмена данными в реальном времени (чаты, уведомления).

Server-Sent Events (SSE): Позволяют backend отправлять данные frontend'у в одностороннем порядке по HTTP-соединению. Используется для получения обновлений от сервера без постоянных запросов от клиента.

Типичный сценарий взаимодействия:

Пользователь совершает действие на frontend (например, нажимает кнопку).

Frontend отправляет HTTP-запрос на соответствующую конечную точку backend'а.

Backend получает запрос, обрабатывает его (например, выполняет запрос к базе данных).

Backend формирует ответ (обычно в формате JSON) и отправляет его frontend'у.

Frontend получает ответ, обрабатывает данные и обновляет пользовательский интерфейс.

Пример AJAX-запроса на frontend с использованием JavaScript:

Метод $emit используется для генерации пользовательских событий из дочернего компонента. Это основной способ взаимодействия дочернего компонента с родительским в однонаправленном потоке данных Vue. Он позволяет дочернему компоненту уведомить родителя о том, что произошло какое-то событие, и, при необходимости, передать ему данные.

Принцип работы:

В дочернем компоненте вызывается $emit('eventName', payload).

eventName - строка, название события, которое будет сгенерировано.

payload (необязательно) - любые данные, которые нужно передать родительскому компоненту.

В родительском компоненте, при использовании дочернего компонента, слушается это событие с помощью директивы v-on или краткой формы @.

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

Пример:

Дочерний компонент (ChildComponent.vue):

Родительский компонент (ParentComponent.vue):

$emit позволяет сохранять принцип однонаправленного потока данных, предотвращая прямое изменение состояния родителя дочерним компонентом. Дочерний компонент лишь сигнализирует о происходящем, а обработка и изменение состояния происходят в родительском компоненте.

Суть инкапсуляции в объединении данных и методов, работающих с этими данными, в единую сущность (объект) и сокрытии внутренней реализации от внешнего мира.

Основные принципы:

Сокрытие данных: Доступ к данным объекта осуществляется только через его публичный интерфейс (методы). Прямой доступ к внутренним полям может быть ограничен (например, с помощью ключевых слов private или #).

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

Защита от некорректного использования: Сокрытие деталей реализации позволяет контролировать, как данные объекта изменяются, предотвращая их некорректное состояние.

Пример на JavaScript:

Преимущества:

Модульность: Код становится более организованным и легче поддерживается.

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

Безопасность: Предотвращает несанкционированный или некорректный доступ к данным.

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

watch (наблюдатели)

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

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

Настраивается с помощью опции watch внутри компонента.

Может иметь параметры handler (функция-обработчик), deep (глубокое наблюдение за вложенными объектами/массивами) и immediate (выполнение обработчика при создании компонента).

Пример:

computed (вычисляемые свойства)

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

Результат кэшируется до тех пор, пока не изменятся его реактивные зависимости.

Вычисляется лениво, только когда к нему обращаются.

Настраивается с помощью опции computed внутри компонента.

Должен возвращать значение.

Пример:

Основные отличия

Используйте computed для получения реактивных данных, являющихся производными от других данных. Используйте watch для выполнения побочных эффектов (например, вызовов API, изменения DOM), когда значение реактивного свойства изменяется, или для выполнения более сложных логических реакций.

Минификация и сжатие: Уменьшение размера CSS, JavaScript и HTML файлов через удаление пробелов, переносов строк и комментариев, а также использование алгоритмов сжатия, таких как Gzip или Brotli.

Ленивая загрузка (Lazy Loading): Загрузка изображений, видео и других ресурсов только тогда, когда они попадают в видимую область экрана. Это можно реализовать с помощью атрибута loading="lazy" или JavaScript.

<img src="image.jpg" loading="lazy" alt="Описание изображения">

Асинхронная и отложенная загрузка скриптов: Использование атрибутов async и defer для тегов <script>.

[async]: Скрипт загружается асинхронно и выполняется как только будет доступен, не блокируя парсинг HTML.

[defer]: Скрипт загружается асинхронно, но выполняется только после того, как весь HTML документ будет разобран.

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

<script src="another-script.js" defer></script>

Оптимизация изображений: Использование современных форматов изображений (WebP), сжатие без потери качества, выбор правильного разрешения и использование атрибутов srcset и <picture>.

<picture>

<source srcset="image.webp" type="image/webp">

<img src="image.jpg" alt="Описание изображения">

</picture>

HTTP/2 и HTTP/3: Использование более новых версий протокола HTTP, которые поддерживают мультиплексирование (одновременная загрузка нескольких ресурсов через одно соединение) и сжатие заголовков.

Кэширование браузером: Настройка HTTP-заголовков Cache-Control и Expires для указания браузеру, как долго он должен хранить ресурсы в кэше.

CDN (Content Delivery Network): Распределение ресурсов по серверам, расположенным географически ближе к пользователю, для сокращения задержки.

Предварительная загрузка и рендеринг (Preload и Preconnect): Использование rel="preload" для ресурсов, которые будут необходимы в ближайшее время, и rel="preconnect" для установления соединения с внешним доменом заранее.

<link rel="preload" href="style.css" as="style">

<link rel="preconnect" href="https://fonts.googleapis.com">

Сокращение количества HTTP-запросов: Объединение CSS и JavaScript файлов, использование спрайтов для изображений.

Устранение блокирующих рендеринг ресурсов: Использование атрибута media для <link> тегов, чтобы стили применялись только при определенных условиях.

<link rel="stylesheet" href="print.css" media="print">

Оптимизация рендеринга в React направлена на уменьшение количества перерисовок и ускорение их выполнения.

Основные подходы:

Memoization (React.memo, useMemo, useCallback):

React.memo(): Оборачивает функциональный компонент и предотвращает его перерисовку, если пропсы не изменились (сравниваются поверхностно).// Пример с React.memo

const MyComponent = React.memo(function MyComponent(props) {

// ...рендер содержимого

});

useMemo(): Мемоизирует значение (результат вычисления), пересчитывая его только при изменении зависимостей.// Пример с useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useCallback(): Мемоизирует функцию-колбэк, предотвращая ее пересоздание при каждом рендере родительского компонента. Полезно для передачи колбэков в мемоизированные дочерние компоненты.// Пример с useCallback

const handleClick = useCallback(() => {

// ...логика обработчика

}, [dependency1, dependency2]);

});

Оптимизация списков (Ключи): Использование уникальных key для элементов списка помогает React эффективно обновлять DOM, идентифицируя, какие элементы добавились, удалились или изменились.

// Пример с ключами

<ul>

{items.map(item => (

<li key={item.id}>{item.text}</li>

))}

</ul>

Ленивая загрузка компонентов (React.lazy, Suspense): Разделение кода (code splitting) позволяет загружать компоненты только при необходимости, уменьшая размер первоначального бандла. React.lazy используется для динамического импорта, а Suspense для отображения fallback UI во время загрузки.

// Пример с React.lazy и Suspense

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyPage() {

return (

<Suspense fallback={<div>Загрузка...</div>}>

<OtherComponent />

</Suspense>

);

}

Виртуализация списков (React-Window, React-Virtualized): Для очень длинных списков отображается только видимая часть, а не весь список сразу, что значительно улучшает производительность рендеринга.

Избегание ненужных обновлений состояния: Объединение обновлений состояния, использование функциональной формы setState в классовых компонентах или прямое использование актуального состояния в хуках (setState(prevState => ...)) помогают избежать лишних перерисовок.

Использование PureComponent (для классовых компонентов): Аналогично React.memo для функциональных компонентов, PureComponent выполняет поверхностное сравнение пропсов и стейта, предотвращая лишние перерисовки.

Минимизация логики в методе render/теле функционального компонента: Сложные вычисления лучше выносить в useMemo или за пределы рендера.

Профилирование: Использование React Developer Tools Profiler помогает выявить узкие места в рендеринге.

HTML (HyperText Markup Language) — это язык разметки для создания веб-страниц. XHTML (eXtensible HyperText Markup Language) — это язык разметки, который является переформулировкой HTML в терминах XML.

Основные отличия:

Синтаксис: XHTML строже, чем HTML. Требуется корректное закрытие всех тегов, использование нижнего регистра для имен тегов и атрибутов, обязательное наличие корневого элемента (в основном, <html>).

Парсинг: HTML парсится более гибко, браузеры часто пытаются исправить ошибки. XHTML требует парсинга как XML, при ошибке парсер прекращает работу.

XML-совместимость: XHTML полностью совместим с XML-инструментами и расширяем за счет пространств имен. HTML не является XML.

Типы документов (DOCTYPE): В XHTML DOCTYPE обязателен и более строг, указывая на используемую версию. В HTML5 DOCTYPE значительно упрощен.

Пример синтаксиса:

HTML:

XHTML:

Исторически XHTML задумывался как замена HTML в эпоху XML, но с появлением HTML5 его популярность снизилась. HTML5 стал более гибким и сохранил обратную совместимость, в то время как XHTML требовал миграции.

Для построения слоистой архитектуры и DDD во фронтенде необходимы следующие инструменты и технологии:

Язык программирования с поддержкой ООП и модульности: JavaScript с использованием современных стандартов (ES6+) или TypeScript. Это позволяет создавать четкие классы, интерфейсы и модули, инкапсулировать логику и управлять зависимостями между слоями. TypeScript особенно полезен для статической типизации, что повышает надежность и уменьшает количество ошибок в больших проектах.

Фреймворк: React, Angular или Vue.js. Эти фреймворки предоставляют структуру для построения пользовательских интерфейсов, управления состоянием и обработки событий. Они помогают разделить UI-логику от бизнес-логики, что соответствует принципам слоистой архитектуры. Angular, благодаря своей структуре и поддержке TypeScript, часто хорошо подходит для DDD.

Библиотеки для управления состоянием: Redux (с Redux Toolkit), MobX или Context API (в React). Управление состоянием критически важно в сложных приложениях. Эти библиотеки помогают организовать централизованное или децентрализованное хранилище данных, доступное разным слоям приложения (например, UI-слою и Application-слою).

Библиотеки для работы с данными и API: Axios, Fetch API. Используются для взаимодействия с бэкендом и слоем инфраструктуры.

Инструменты для сборки и управления зависимостями: Webpack, Parcel, Vite (для сборки), npm или yarn (для управления зависимостями). Обеспечивают модульность, оптимизацию кода, транспиляцию и позволяют структурировать проект по слоям.

Инструменты для тестирования: Jest, Testing Library, Cypress. Unit- и интеграционные тесты необходимы для поддержания качества кода в каждом слое. E2E-тесты проверяют взаимодействие слоев.

Линтеры и форматтеры кода: ESLint, Prettier. Поддерживают единообразный стиль кода и выявляют потенциальные ошибки, что важно для поддержания чистоты слоев и соблюдения архитектурных принципов.

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

DI-контейнеры или Dependency Injection: InversifyJS, Tsyringe. Позволяют инвертировать контроль и управлять зависимостями между классами и слоями, что упрощает тестирование и повышение гибкости архитектуры.

Структура файловой системы: Организация папок по слоям (например, domain, application, presentation, infrastructure) или по фичам с учетом слоев внутри фич.

Зачем они нужны:

Разделение ответственности: Инструменты и технологии позволяют четко разделить бизнес-логику (слой домена/приложения) от UI (слой представления) и внешней инфраструктуры (слой инфраструктуры).

Управляемость сложности: Разбиение на слои делает большое приложение более понятным и легким в сопровождении.

Тестируемость: Каждый слой можно тестировать независимо, что упрощает процесс и повышает надежность.

Гибкость: Изменения в одном слое (например, UI) минимально влияют на другие слои (например, домен). Это позволяет легче менять технологии или адаптироваться к новым требованиям.

Повторное использование: Логика домена и приложения может быть использована в разных UI или с разными адаптерами инфраструктуры.

Фокус на домене: DDD и слоистая архитектура помогают сосредоточиться на бизнес-домене, делая код более выразительным и менее зависимым от технических деталей.

HMAC (Hash-based Message Authentication Code) — это механизм для проверки целостности и аутентификации сообщения с использованием криптографической хэш-функции и секретного ключа.

Принцип работы:

Ключ расширяется до размера блока хэш-функции.

Ключ XORится с внутренней фиксированной константой (inner padding) и конкатенируется с сообщением.

Вычисляется хэш от результата.

Ключ XORится с внешней фиксированной константой (outer padding) и конкатенируется с хэшем из шага 3.

Окончательный HMAC вычисляется как хэш от результата шага 4.

Используется для:

Проверки целостности данных (удостовериться, что сообщение не было изменено).

Аутентификации сообщения (удостовериться, что сообщение отправлено известным источником, обладающим секретным ключом).

В протоколах безопасности, таких как TLS/SSL, IPsec, JWT.

Пример генерации HMAC (псевдокод):

где:

H — хэш-функция (например, SHA-256).

key — секретный ключ.

message — сообщение.

|| — операция конкатенации.

ipad — внутренняя фиксированная константа.

opad — внешняя фиксированная константа.

XOR — побитовая операция исключающее ИЛИ.

HMAC устойчив к атакам удлинением, в отличие от простого использования Hash(key || message) или Hash(message || key).

Безопасные куки — это куки, которые передаются на сервер только по защищенному HTTPS-соединению.

Они устанавливаются с флагом Secure, который указывает браузеру отправлять куки только при наличии SSL/TLS-шифрования.

Флаг включается при установке куки через HTTP-заголовок Set-Cookie или JavaScript.

Пример установки через HTTP-заголовок:

Пример установки через JavaScript:

Использование безопасных куки предотвращает их перехват злоумышленниками при передаче по незащищенным каналам, например, при использовании общедоступных Wi-Fi сетей. Это повышает безопасность пользовательских сессий и конфиденциальных данных, хранящихся в куки.

MVVM (Model-View-ViewModel) — это архитектурный паттерн, разделяющий приложение на три взаимосвязанных компонента:

Model: Представляет данные и бизнес-логику приложения. Он не зависит от View и ViewModel.

View: Представляет пользовательский интерфейс (HTML, CSS). View пассивен и не содержит логики. Он отображает данные из ViewModel и отправляет пользовательские действия.

ViewModel: Является посредником между Model и View. Он содержит логику представления данных и обрабатывает действия пользователя. ViewModel предоставляет данные для View в удобном для отображения формате и обновляет Model в ответ на действия пользователя. ViewModel не знает о существовании конкретного View.

Ключевая особенность MVVM — связывание данных (Data Binding). View и ViewModel связаны таким образом, что изменения в ViewModel автоматически отражаются во View, и наоборот.

Преимущества MVVM:

Разделение ответственности: Четко отделяет UI от бизнес-логики.

Тестируемость: ViewModel легко тестируется независимо от UI.

Поддерживаемость: Упрощает внесение изменений в UI или логику.

Переиспользуемость: ViewModel может использоваться с различными View.

MVVM широко используется в современных фронтенд-фреймворках, таких как Vue.js и Angular (где ViewModel часто соответствует контроллеру компонента).

Cookies хранят небольшие объемы текстовой информации (до 4KB) на стороне клиента, отправляются с каждым HTTP-запросом к домену, установившему их. Могут иметь срок истечения. Используются для авторизации, отслеживания пользователей, сохранения пользовательских предпочтений.

localStorage хранит данные без срока действия (пока пользователь не очистит хранилище или не удалит браузер) на стороне клиента (до 5-10MB в зависимости от браузера). Данные доступны между сессиями браузера, но только для скриптов того же происхождения (домена, протокола, порта). Не отправляются автоматически с HTTP-запросами. Используется для долговременного хранения данных, сохранения настроек приложения.

sessionStorage хранит данные в течение одной сессии браузера (пока вкладка или окно открыты). Данные удаляются при закрытии вкладки или окна. Объем хранения схож с localStorage (до 5-10MB). Данные также доступны только для скриптов того же происхождения. Не отправляются автоматически с HTTP-запросами. Используется для временного хранения данных, специфичных для текущей сессии.

Пример использования localStorage:

Пример использования sessionStorage:

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

Стрелочные функции (Arrow Functions): Сокращенный синтаксис для объявления функций. Lexical this.

Шаблонные литералы (Template Literals): Позволяют встраивать выражения и многострочные строки.const name = "World";

const greeting = `Hello, ${name}!`; // "Hello, World!"

Деструктурирующее присваивание (Destructuring Assignment): Удобный способ извлечения значений из массивов и объектов.const person = { firstName: "John", lastName: "Doe" };

const { firstName, lastName } = person; // firstName = "John", lastName = "Doe"

Spread-оператор (...) и Rest parameters (...): Spread для расширения и объединения массивов/объектов. Rest для сбора оставшихся аргументов функции в массив.const arr1 = [1, 2];

const arr2 = [...arr1, 3, 4]; // [1, 2, 3, 4]

function sum(...numbers) {

return numbers.reduce((acc, current) => acc + current, 0);

}

sum(1, 2, 3); // 6

Классы (Classes): Синтаксический сахар над прототипным наследованием.class Car {

constructor(brand) {

this.brand = brand;

}

drive() {

console.log(`${this.brand} is driving.`);

}

}

Модули (Modules): Средство для организации кода с помощью import и export.// module.js

export const PI = 3.14;

// main.js

import { PI } from './module.js';

Промисы (Promises): Упрощают работу с асинхронным кодом.new Promise((resolve, reject) => {

// Асинхронная операция

if (success) {

resolve("Успех!");

} else {

reject("Ошибка!");

}

})

.then(result => console.log(result))

.catch(error => console.error(error));

for...of цикл: Итерация по итерируемым объектам (массивы, строки, Map, Set, etc.).const arr = [1, 2, 3];

for (const value of arr) {

console.log(value); // 1, 2, 3

}

Set и Map: Новые структуры данных. Set для уникальных значений, Map для пар ключ-значение.

Нативные объекты — это встроенные объекты, определенные стандартом ECMAScript. Они являются частью самого языка JavaScript.

Хост-объекты — это объекты, предоставляемые средой выполнения JavaScript (браузером, Node.js). Они не являются частью стандарта ECMAScript, но доступны в данной среде.

Примеры:

Нативные объекты: Object, Array, Function, String, Number, Boolean, Date, RegExp, Map, Set, Promise, Math, JSON.

Хост-объекты (в браузере): window, document, console, XMLHttpRequest, setTimeout, DOM-элементы (например, HTMLElement).

Основные отличия:

Дополнительно:

Хост-объекты могут иметь свойства и методы, которые отличаются или даже отсутствуют в разных средах.

Поведение хост-объектов может быть не полностью стандартизировано, тогда как поведение нативных объектов четко описано в спецификации ECMAScript.

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

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

Основные преимущества семантической разметки:

Доступность: Улучшает взаимодействие с сайтом для людей с ограниченными возможностями, пользующихся скрин-ридерами.

SEO: Помогает поисковым системам лучше понять структуру и содержание страницы, что положительно влияет на ранжирование.

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

Совместимость: Обеспечивает корректное отображение контента на различных устройствах и в разных браузерах.

Примеры семантических тегов в HTML5:

<header>: Представляет вступление или навигацию для всего документа или определенной секции.

<nav>: Представляет группу ссылок для навигации.

<main>: Представляет основное содержание документа.

<article>: Представляет законченный, самодостаточный фрагмент контента, который может быть независимо распространен (например, запись в блоге, новость).

<section>: Представляет самостоятельную секцию контента в документе.

<aside>: Представляет контент, который косвенно связан с основным контентом страницы (например, боковая панель).

<footer>: Представляет нижний колонтитул для всего документа или определенной секции.

<figure> и <figcaption>: Представляют медиа-элемент (изображение, диаграмма и т.д.) и его описание.

<time>: Представляет дату и время.

Сравнение семантического и несемантического подхода:

Пример несемантической разметки:

Пример семантической разметки:

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

Force layout - это процесс, при котором браузер вынужден принудительно пересчитать стили и расположение элементов на странице до завершения текущего цикла рендеринга. Это происходит, когда JavaScript обращается к свойствам элемента, зависящим от его текущего отображения (например, offsetWidth, offsetHeight, getComputedStyle()) сразу после изменения стиля или структуры DOM, которое может повлиять на эти свойства.

Недостатки force layout заключаются в значительном снижении производительности. Каждый force layout требует от браузера выполнения дорогостоящих операций по расчету стилей, дерева рендеринга и расположения элементов. Если это происходит многократно, например, в цикле или при обработке событий, это может вызвать "тормоза" и плохой пользовательский опыт.

Сравнение с обычным рендерингом:

Пример кода, вызывающего force layout:

Избежать force layout можно, группируя изменения стиля, используя requestAnimationFrame для чтения свойств или избегая чтения свойств макета сразу после их изменения.

А если вопрос прозвучит не так, как вы готовились?

Так бывает чаще всего. ИзиСобес слышит вопрос интервьюера и подсказывает ответ прямо во время разговора — его не видно ни в Zoom, ни при демонстрации экрана.

Посмотреть ИзиСобес