Финальный срез
Повторение теории. Неделя 8
1. Срез
1.1. Common principles
Наименование компонент, классов и интерфейсов — в React компоненты обязаны начинаться с заглавной буквы, чтобы JSX отличал их от обычных HTML-тегов.
Переиспользование кода достигается в основном за счет:
- Создания переиспользуемых компонентов путем передачи в них пропсов с разными данными. Также есть пропс
children, с помощью которого можно передавать одни компоненты внутрь других - Использования кастомных хуков для переиспользования логики
Принципы DRY, KISS и YAGNI:
- DRY (Don’t Repeat Yourself) — не дублируй свой код
- KISS (Keep It Simple, Stupid) — сохраняй код простым и тупым
- YAGNI (You Ain’t Gonna Need It) — тебе это не понадобится
Чистый код по Роберту Мартину — это концепция написания кода, который легко читать, понимать и поддерживать.
Code Review — это процесс проверки и анализа исходного кода для выявления ошибок, улучшения качества и обучения команды.
Принципы SOLID:
- Принцип единственной ответственности (Single Responsibility Principle) — модуль должен иметь одну причину для изменения
- Принцип открытости/закрытости (Open/Closed Principle) — программные сущности должны быть открыты для расширения, но закрыты для изменения. Это значит, что можно добавлять новую функциональность, не изменяя существующий код
- Принцип подстановки Барбары Лисков (Liskov Substitution Principle) — объекты класса наследника должны быть взаимозаменяемы с объектами родительского класса без изменения ожидаемого поведения программы. Проще говоря, если есть базовый класс, то любой его наследник должен соответствовать ожиданиям, заданным им, и не нарушать его контракт
- Принцип разделения интерфейсов (Interface Segregation Principle) — программные сущности не должны зависеть от методов, которые они не используют. Лучше разделить интерфейсы на более мелкие, специфичные
- Принцип инверсии зависимостей (Dependency Inversion Principle) — модули высокого уровня не должны зависеть от модулей низкого уровня. И те, и другие должны зависеть от абстракций
1.2. Programming paradigms (imperative and declarative)
Парадигма программирования — совокупность идей и понятий, определяющих стиль написания компьютерных программ, структурирование их работы и организацию вычислений. Основные виды включают императивную, декларативную, объектно-ориентированную и функциональную парадигмы.
Основные различия между императивным и декларативным подходами:
- Императивный подход — сосредоточен на точном описании алгоритма (как сделать)
- Есть четкая последовательность команд
- Есть состояние программы, циклы, условия и операция присваивания
- Оптимизация кода для повышения производительности — это задача программиста
- Декларативный подход — сосредоточен на результате (что получить)
- Программисту не нужно думать о низкоуровневых деталях (например, об управлении памятью или оптимизации)
- Состояние программы управляется автоматически
1.3. Functional programming (functors, key features)
Функциональное программирование — это парадигма программирования, в которой программы строятся путем композиции чистых функций, избегая изменения состояния и мутации данных.
Основные концепции:
- Чистые функции
- Детерминированность — одинаковые входные данные дают одинаковый результат
- Не имеют побочных эффектов — они ничего не меняют вне себя
- Иммутабельность
- Данные не изменяются, а создаются новые на основе старых
- Функции — объекты первого класса
- С функциями можно работать как с любыми другими значениями: передавать их аргументами, возвращать из других функций, присваивать переменным
- Функции высшего порядка
- Это функции, которые либо принимают другие функции в качестве аргументов, либо возвращают функции как результат
Функтор — это объект, реализующий метод map и соответствующий законам тождества и композиции.
Монада — функтор с дополнительными методами (bind, chain, flatMap), соответствующий законам левой идентичности, правой идентичности и ассоциативности.
1.4. JS. Common (function, hoisting, work with objects, etc)
В JavaScript используются стандартные управляющие конструкции:
if/elseдля ветвления логикиswitchдля множественных условий- тернарный оператор
? - циклы
for,whileиdo...while for...ofдля итерируемых объектов (массивы, строки)for...inдля перебора свойств объектов
Управление выполнением циклов:
break— прерывание циклаcontinue— прерывание текущей итерации и переход к следующей
Функции в JS бывают трёх видов:
- function declaration
- function expression
- стрелочная функция
Кроме того, есть IIFE (Immediately Invoked Function Expression) — это функция, которая объявляется и сразу же выполняется. Основная цель — изолировать область видимости. Базовый синтаксис выглядит так:
1
2
3
(function () {
// код внутри
})();
Функции — это объекты первого класса: их можно передавать аргументами, возвращать из других функций и присваивать переменным.
Область видимости переменных внутри функции:
- Переменные, объявленные внутри функции, видны только внутри этой функции
- Функция обладает полным доступом к внешним переменным и может изменять их значение
- Внешняя переменная используется, только если внутри функции нет такой локальной. Если одноимённая переменная объявляется внутри функции, тогда она перекрывает внешнюю
Типы данных:
- string
- number
- bigInt
- boolean
- null
- undefined
- symbol
- object
Операторы:
- Арифметические:
+-*/%** - Сравнения:
==(с приведением типов)===(строгое)!=!==><>=<= - Логические:
&&||!(возвращают значения, не толькоboolean) - Присваивания:
=+=-=*=/= - Тернарный:
условие ? значение_если_true : значение_если_false
Объект — это коллекция данных в формате «ключ: значение». Ключ — это всегда строка (или символ), а значение может быть чем угодно.
1
2
3
4
5
6
7
8
9
let someObj = { one: 1, two: 2 };
someObj.one;
someObj["one"];
someObj.newProp = "something";
delete someObj.newProp;
"one" in someObj;
Массив — это упорядоченный список элементов. В JS массивы могут содержать данные разных типов одновременно.
1
2
3
4
5
6
7
8
9
10
let someArr = [1, 2, 3];
someArr[0];
someArr.length;
someArr.filter / map / sort / reduce / forEach;
someArr.push(4); // Добавляет в конец
someArr.pop(); // Удаляет последний элемент
someArr.shift(); // Удаляет первый элемент
someArr.unshift(0); // Добавляет в начало
Объекты и массивы передаются по ссылке, а не по значению. Для создания поверхностной (shallow) копии можно использовать оператор spread: [...someArr] или {...someObj}.
Различия между объявлениями переменных:
- Область видимости (Scope):
var: функциональная область видимостиlet/const: блочная область видимости
- Всплытие (Hoisting):
var: всплывает с инициализациейundefined. Нет TDZlet/const: всплывают, но попадают в TDZ- TDZ (Temporal Dead Zone, временная мертвая зона) — это состояние переменных
let/constот начала блока до их объявления, при котором обращение к ним вызоветReferenceError
- Повторное объявление:
var: можно переобъявлятьlet: нельзя переобъявлять в той же областиconst: нельзя переобъявлять и изменять значение
- Инициализация:
var/let: можно объявить без значенияconst: требует инициализации при объявлении
Поднятие (hoisting) и области видимости переменных:
- Hoisting — это поведение JavaScript, при котором объявления переменных и функций перемещаются вверх своей области видимости на этапе компиляции
varимеет функциональную область видимости и hoisting с инициализациейundefinedletиconstимеют блочную область видимости и hoisting с TDZ (временной мертвой зоной)- Область видимости определяет доступность переменных: глобальная, функциональная, блочная
Шаблонные строки, созданные через обратные кавычки `someString`, поддерживают многострочность без символа переноса строки \n, интерполяцию переменных через ${expression}, а также вложенные выражения.
Строгое и нестрогое сравнение:
- Нестрогое сравнение
==приводит типы данных перед проверкой: например,5 == '5'вернетtrue - Строгое
===проверяет равенство без приведения, включая тип:5 === '5'вернетfalse - Особые случаи:
null == undefined=>true,NaN !== NaN=>true
Сборка мусора — это автоматический процесс освобождения памяти, занятой объектами, которые больше не используются программой и на которые нет ссылок.
Методы работы с объектами:
Object.keys(obj)— возвращает массив ключейObject.values(obj)— возвращает массив значенийObject.entries(obj)— возвращает массив пар [ключ, значение]Object.fromEntries([[key, value]])— создает объект из массива пар
Методы работы с массивами:
flat(depth)— “выравнивает” вложенные массивы на указанную глубинуflatMap()— объединяет функцииmap()иflat(), применяя функцию ко всем элементам массива и затем выравнивая результат на глубинуincludes(item)— проверяет наличие элементаArray.from(iterable)— создаёт массив из итерируемого объекта
1.5. JS. Data types
Типы данных:
- string
- number
- bigInt
- boolean
- null
- undefined
- symbol
- object
В JavaScript примитивный тип данных — это данные, которые не являются объектами и не имеют методов (хотя у них есть объекты-обёртки). Они представляют собой простые, неделимые значения. Все типы данных кроме object в JS являются примитивами.
- Иммутабельны
- Сравниваются по значению
- Не имеют методов или свойств (хотя JavaScript временно оборачивает их в объекты для доступа к методам)
Объект — это ссылочный тип данных, который хранит коллекцию ключ-значение.
- Мутабельны
- Сравниваются по ссылке
- Обладают методами
Для создания поверхностных (shallow) копий объектов можно использовать spread оператор ... или Object.assign().
Для глубоких копий SON.parse(JSON.stringify(obj)).
Упаковка — это когда примитивное значение автоматически оборачивается в соответствующий объект, чтобы можно было использовать методы этого объекта. JS временно создаёт объект-обёртку (String, Number, Boolean, BigInt, Symbol), чтобы вызвать метод, после чего удаляет его.
Распаковка — это обратный процесс: получение примитивного значения из объекта-обертки.
У null и undefined нет соответствующих им объектов-оберток, попытка вызвать методы приведет к ошибке.
Существует 3 наиболее широко используемых преобразования типов: строковое, численное и логическое.
Строковое– может быть вызвано с помощьюString(value). Для примитивных значений работает очевидным образомЧисленное– может быть вызвано с помощьюNumber(value). Подчиняется правилам:Значение Становится… undefinedNaNnull0true / false1 / 0stringПробельные символы по краям обрезаются.
Далее, если остаётся пустая строка, то получаем0,
иначе из непустой строки «считывается» число.
При ошибке результатNaNЛогическое– может быть вызвано с помощьюBoolean(value). Подчиняется правилам:Значение Становится… 0,null,undefined,NaN,""falseлюбое другое значение true
Оператор typeof возвращает строку с названием типа данных: number, string, boolean, undefined, object, function, symbol, bigint. Его ключевая особенность — ошибочное определение null как object.
1.6. JS. Closure
Замыкание — это комбинация функции и лексического окружения, в котором она была объявлена. Оно позволяет функции запоминать и получать доступ к переменным из внешней области видимости даже после того, как внешняя функция завершила выполнение.
Лексическое окружение — это внутренняя структура данных, которая хранит связь между идентификаторами (именами переменных, функций) и их значениями в определённом участке кода.
Основные компоненты:
- Запись окружения (environment record) — объект, хранящий фактические переменные и функции
- Ссылка на внешнее лексическое окружение (outer reference) — ссылка на окружение, в котором был создан текущий код
Каждый вызов функции создает новое лексическое окружение, образуя цепочку (scope chain). Именно эта цепочка позволяет замыканиям получать доступ к переменным из внешних функций даже после их завершения. Движок оптимизирует доступ к переменным через эту цепочку, но избыточная вложенность может влиять на производительность.
1.7. JS. Context
Контекст выполнения (this) — это специальная переменная, которая ссылается на объект, в контексте которого выполняется функция.
Его значение динамично и зависит от способа вызова:
- В глобальной области
thisссылается на глобальный объектwindow - В обычной функции
thisссылается на глобальный объект (нестрогий режим) илиundefined(строгий режим) - В методе объекта
this— сам объект (при вызове через точку:obj.method()) - В стрелочных функциях
thisберётся из внешнего лексического окружения
Глобальный контекст выполнения в JavaScript определяется при запуске программы и является базовым контекстом. В глобальном контексте находится всё, что объявлено не внутри функций, блоков или модулей:
- Глобальные переменные
- Глобальные функции
- Встроенные объекты (
Math,JSON,Dateи другие) - Глобальные API (
document,console,navigator)
Контекст (this) определяется в момент вызова функции, а не её объявления.
Контекст теряется, когда:
Функция передаётся как колбэк
1 2 3 4 5 6 7
const obj = { name: 'Alice', greet() { console.log(this.name); } }; setTimeout(obj.greet, 100); // Ошибка: this === window
Метод объекта присваивается переменной
1 2 3 4 5 6 7 8 9
const obj = { name: "Kate", greet() { console.log(this.name); } }; const greet = obj.greet; greet(); // Ошибка: this === undefined (в strict mode)
JavaScript предоставляет три метода для управления контекстом:
bind(context)— создаёт новую функцию с привязаннымthiscall(context, ...args)— вызывает функцию с заданнымthisи аргументамиapply(context, [args])— аналогиченcall, но аргументы передаются массивом
Решение задач на потерю и восстановление контекста:
Жёсткая привязка через
bind:1
setTimeout(obj.greet.bind(obj), 100); // "Kate"
Стрелочные функции:
1 2
const greet = () => obj.greet(); greet(); // "Kate"
Паттерн “мягкая привязка”:
1 2 3 4 5 6
function foo() { console.log(this.name); } const context = { name: "Soft" }; foo.call(context); // "Soft"
Контекст React (createContext) позволяет передавать данные через дерево компонентов без передачи пропсов:
Создание контекста:
1
const ThemeContext = createContext("light");
Обёртка провайдером:
1 2 3
<ThemeContext.Provider value="dark"> <App /> </ThemeContext.Provider>
Получение значения через
useContext:1 2 3 4
function Button() { const theme = useContext(ThemeContext); return <button className={theme}>Click</button>; }
1.8. JS. Asynchronous programming
Блокирующий код — это синхронные операции, которые останавливают выполнение программы до своего завершения (например, сложные вычисления или синхронные HTTP-запросы). В JavaScript, который работает в одном потоке, это приводит к “замораживанию” интерфейса, так как цикл событий (event loop) не может обрабатывать другие задачи до завершения блокирующей операции.
Event loop — это механизм, который управляет выполнением асинхронного кода в JavaScript. Он постоянно проверяет две очереди: стек вызовов (для синхронного кода) и очередь задач (для асинхронных колбэков). Когда стек пуст, event loop берёт первую задачу из очереди (например, колбэк setTimeout) и помещает её в стек.
Использование setTimeout и setInterval:
setTimeout(fn, delay)выполняет функциюfnодин раз после указанной задержкиdelayclearTimeout(id)— отменяетsetTimeoutпо его идентификатору
setInterval(fn, delay)вызываетfnповторно с интерваломdelayclearInterval(id)— останавливаетsetIntervalпо его идентификатору
setTimeout(fn, 0) не выполняет функцию мгновенно, а помещает её в конец очереди макрозадач.
Макро- и микрозадачи:
- Микрозадачи (
Promise,queueMicrotask,MutationObserver) выполняются сразу после текущего синхронного кода, перед следующей макрозадачей - Макрозадачи (
setTimeout,setInterval, события DOM) выполняются на следующей итерации event loop
Порядок выполнения:
- Синхронный код
- Все микрозадачи
requestAnimationFrame(callback)- Рендеринг
- Одна макрозадача
- Повтор
requestAnimationFrame(callback)выполняетcallbackперед следующей отрисовкой кадра. Оптимален для анимаций, так как синхронизирован с частотой обновления экранаrenderимеет приоритет над макрозадачами, но ждёт завершения всех микрозадач
Web Workers позволяют выполнять код в отдельном потоке, не блокируя основной. Используются для:
- Тяжёлых вычислений
- Обработки больших данных
Наблюдатели (например, MutationObserver, IntersectionObserver) реагируют на изменения в DOM или видимости элементов:
MutationObserverотслеживает изменения в дереве DOMIntersectionObserverопределяет, когда элемент появляется вviewport
queueMicrotask(fn) добавляет функцию в очередь микрозадач (как .then у промиса). Используется для выполнения кода сразу после текущего синхронного задания, но перед макрозадачами.
1.9. JS. Garbage collector
Сборка мусора — это автоматический процесс освобождения памяти, занятой объектами, которые больше не используются программой.
Сборщик мусора в JS работает по алгоритму маркировки и очистки (Mark-and-Sweep):
- Маркировка: движок обходит все достижимые объекты, начиная с корневых (глобальные переменные, стек вызовов), и помечает их как “живые”
- Очистка: память, занятая неотмеченными (“мертвыми”) объектами, освобождается
Современные движки дополняют этот подход поколенческой сборкой, разделяя объекты на “молодые” (часто меняющиеся) и “старые” (долгоживущие).
Объект считается “мусором”, если он недостижим из корневых точек:
- Нет ссылок из глобальных переменных
- Нет ссылок из стека вызовов функций
- Нет ссылок из других достижимых объектов
Стратегии оптимизации работы с памятью:
- Переиспользование объектов: пулы объектов вместо создания/удаления
- Избегание утечек: удаление обработчиков событий, таймеров
- Осторожность с замыканиями: они сохраняют ссылки на внешние переменные
- Использование типизированных массивов для больших данных
Поколенческий сборщик мусора разделяет объекты по “поколениям” (молодые, старые) и часто сканирует только молодые объекты.
1.10. JS. Promise, async/await
- Колбэки — это функции, передаваемые в качестве аргументов, которые вызываются после завершения операции
- Промис — это объект, представляющий результат асинхронной операции
Промис может находиться в одном из трёх состояний:
pending— начальное состояние, операция не завершенаfulfilled— операция завершена успешно (вызываетсяthen)rejected— операция завершена с ошибкой (вызываетсяcatch)
После перехода в fulfilled или rejected промис становится неизменяемым.
Метод finally выполняется после then или catch. Он выполняется вне зависимости от того, завершился промис успехом или ошибкой, и используют для очистки ресурсов.
Ошибки в промисах можно обрабатывать с помощью метода catch:
1
2
3
4
5
6
fetch("https://api.example.com")
.then((response) => {
if (!response.ok) throw new Error("HTTP error!");
return response.json();
})
.catch((err) => console.error(err));
Второй аргумент .then() может обрабатывать ошибки, но это менее удобно, чем .catch(), потому что такой подход не перехватывает ошибки внутри первого обработчика:
1
2
3
4
fetch("<https://api.example.com>").then(
(response) => response.json(), // если здесь будет ошибка
(err) => console.error("Ошибка:", err), // она не обработается здесь
);
Обработка ошибок в async/await работает синхронно и использует привычные try..catch блоки, что делает код более линейным и читаемым.
Статические методы:
Promise.resolve(value)— возвращает успешный промис сvaluePromise.reject(error)— возвращает отклонённый промис сerrorPromise.all([...promises])— ожидает все промисы; если один отклонён — вся группа отклоненаPromise.race([...promises])— возвращает первый завершённый промис (успешный или ошибку)Promise.allSettled([...promises])— ждёт все промисы, возвращает их статусы
1.11. JS. Error handling
Ошибки в JavaScript — это механизм прерывания нормального выполнения программы при возникновении исключительных ситуаций.
Классификация ошибок по моменту возникновения:
- Синтаксические ошибки — это ошибки, которые обнаруживаются на этапе парсинга кода, до запуска программы
- Ошибки времени выполнения — это ошибки, которые возникают во время выполнения программы
Типы ошибок:
Error— базовый класс, от которого наследуются все ошибки в jsReferenceError— переменная не существуетTypeError— неправильный тип или недопустимая операцияSyntaxError— синтаксическая ошибка в кодеRangeError— значение вне допустимого диапазона
Принципы работы конструкции try-catch:
В блоке try размещается потенциально опасный код, который может вызвать ошибку. Если внутри try возникает исключение, выполнение немедленно прерывается и управление передаётся в блок catch, куда попадает объект ошибки для обработки.
Блок finally выполняется в любом случае, не важно, было ли выброшено исключение или нет, что полезно для очистки ресурсов.
В JavaScript нельзя использовать несколько catch для разных типов ошибок (как в Java). Вместо этого нужно проверять тип ошибки внутри одного блока, используя if/else:
1
2
3
4
5
6
7
8
try {
// Код с возможными ошибками
} catch (err) {
if (err instanceof TypeError) {
console.log('TypeError');
} else if (err instanceof ReferenceError) {
} else {...}
}
В асинхронных функциях, как и в обычных, для обработки ошибок используется конструкция try...catch.
Перехват необработанных исключений:
1
2
3
window.addEventListener("error", (event) => {
console.log(event.error); // Перехватывает все ошибки
});
2. Срез
2.1. GP. Work with backend (HTTP requests, headers, etc)
HTTP (HyperText Transfer Protocol) — это прикладной протокол передачи данных, который лежит в основе работы Интернета. Он определяет, как именно клиент (обычно браузер) и сервер обмениваются сообщениями.
HTTPS (HyperText Transfer Protocol Secure) — это защищённая версия HTTP, в которой передача данных между клиентом и сервером шифруется с помощью протокола TLS.
HTTP-статусы — это трёхзначные коды, указывающие на результат запроса. Они делятся на пять групп:
- 1xx (Информационные) — запрос принят, обработка продолжается
- 2xx (Успех) — запрос успешно выполнен (например, 200 OK)
- 3xx (Перенаправление) — требуется дополнительное действие (301 Moved Permanently)
- 4xx (Ошибка клиента) — запрос содержит ошибку (404 Not Found)
- 5xx (Ошибка сервера) — сервер не смог обработать запрос (500 Internal Server Error)
JSON (JavaScript Object Notation) — текстовый формат обмена данными, основанный на синтаксисе JavaScript. В HTTP-запросах JSON используется как тело запроса или ответа.
CRUD (Create, Read, Update, Delete) соответствует HTTP-методам:
- POST — создание ресурса
- GET — получение ресурса
- PUT/PATCH — обновление (полное/частичное)
- DELETE — удаление
Эти методы используются в REST API для взаимодействия с сервером.
REST (Representational State Transfer) — это архитектурный стиль построения веб-сервисов, который описывает, как клиент и сервер должны взаимодействовать через HTTP.
Главная идея: Всё — это ресурсы, доступные по URL, над которыми выполняются стандартные операции.
Основные принципы:
- Любая сущность является ресурсом
- Единый интерфейс (HTTP-методы и статус-коды)
- Отсутствие состояния
- Клиент-сервер
- Кэширование
- Многоуровневая система
API называют RESTful, если он соблюдает принципы REST.
CORS (Cross-Origin Resource Sharing) — это механизм безопасности веб-браузеров, который контролирует доступ веб-страниц к ресурсам на другом домене.
Origin— HTTP-заголовок запроса, который браузер автоматически добавляет при кросс-доменных запросах. Показывает серверу с какого домена пришел запрос. Используется сервером, чтобы решить, разрешать запрос или нетAccess-Control-Allow-Origin— HTTP-заголовок ответа, который сервер посылает клиенту, чтобы разрешить или запретить доступ. Браузер проверяет его и разрешает JavaScript получить ответ, если домен совпадает
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(); // Отмена запроса
Это полезно для прерывания долгих запросов при переходе между страницами.
Установка заголовков в HTTP запросах:
1
2
3
4
5
6
fetch(url, {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
}
});
Для непрерывного соединения используются:
- WebSocket — двусторонняя связь в реальном времени
- Server-Sent Events (SSE) — потоковые сообщения от сервера
- Long Polling — сервер держит запрос открытым до новых данных
Эти методы полезны для чатов, уведомлений и live-обновлений.
2.2. GP. Request parameters and JSON
Клонирование объектов через JSON — это простой способ создания глубокой копии объекта в JavaScript:
1
2
let someObj = {1: 1, 2: 2}
let deepCopy = JSON.parse(JSON.stringify(someObj))
Добавление тела запроса в HTTP-запросы:
1
2
3
4
5
fetch(url, {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
headers: { 'Content-Type': 'application/json' }
});
2.3. JS. Classes
В JavaScript классы представляют собой удобный синтаксис для работы с прототипным наследованием. Они объявляются с помощью ключевого слова class и могут содержать конструктор, методы и свойства.
1
2
3
4
5
6
7
8
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
Когда мы создаём экземпляр класса с помощью new, автоматически вызывается конструктор, который инициализирует объект. В конструкторе через this мы можем задавать начальные значения свойств.
1
let someUser = new User("someName")