Guru Frontend at YeaHub | РУСЛАН КУЯНЕЦ
Всем привет!
Я Founder & Tech Lead at YeaHub — Open Source платформа для IT-специалистов. Основатель Frontend-сообщества Reactify, веду YouTube и Telegram каналы. За 4 года в IT прошел путь от Junior до Senior и создал с нуля проект в роли Tech Lead
Помимо разработки, я ментор по Frontend — помогаю новичкам прокачаться и устроиться в IT.
А ещё я Guru по Frontend в YeaHub: отвечаю за качество контента, а вместе с учениками разрабатываю web версию платформы. Проект Open Source, так что можно заглянуть под капот — код открыт в нашем репозитории.
А ещё подписывайтесь на другие каналы Экосистемы YeaHub
Проект YeaHub:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2🔥2😁2
#JavaScript
🚩 ES6 модули vs CommonJS: в чем принципиальная разница?
Многие думают, что import/export и require/module.exports — это просто разный синтаксис для одного и того же. На самом деле это кардинально разные подходы.
Главное различие — время загрузки. CommonJS загружает модули во время выполнения кода, то есть динамически. ES6 модули анализируются и загружаются еще до выполнения кода, статически.
Статический анализ ES6 модулей позволяет инструментам сборки (webpack, vite) понимать какой код действительно используется и выбрасывать неиспользуемые части (tree shaking). С CommonJS это невозможно — нужно включать весь модуль целиком.
ES6 модули не позволяют условную загрузку напрямую в коде, зато дают гарантии производительности. CommonJS гибче — можно загружать модули по условию, но теряются оптимизации.
Пример:
Современные приложения используют ES6 модули для основного кода (лучшая оптимизация) и динамические импорты для условной загрузки. CommonJS остается актуальным для Node.js и legacy проектов.
🔎 🔎 🔎 🔎 🔄
#⃣ Новости
#⃣ База вопросов
Многие думают, что import/export и require/module.exports — это просто разный синтаксис для одного и того же. На самом деле это кардинально разные подходы.
Главное различие — время загрузки. CommonJS загружает модули во время выполнения кода, то есть динамически. ES6 модули анализируются и загружаются еще до выполнения кода, статически.
Статический анализ ES6 модулей позволяет инструментам сборки (webpack, vite) понимать какой код действительно используется и выбрасывать неиспользуемые части (tree shaking). С CommonJS это невозможно — нужно включать весь модуль целиком.
ES6 модули не позволяют условную загрузку напрямую в коде, зато дают гарантии производительности. CommonJS гибче — можно загружать модули по условию, но теряются оптимизации.
Пример:
// CommonJS - загрузка во время выполнения
if (condition) {
const helper = require('./helper'); // Работает
}
// ES6 - статический импорт (анализируется до выполнения)
import helper from './helper.js'; // Загружается всегда
// ES6 - динамический импорт
if (condition) {
const helper = await import('./helper.js'); // Работает
}
Современные приложения используют ES6 модули для основного кода (лучшая оптимизация) и динамические импорты для условной загрузки. CommonJS остается актуальным для Node.js и legacy проектов.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2
#React
🚩 Почему React Context может убить производительность вашего приложения?
React Context — это удобно, но при неправильном использовании превращается в "пожирателя производительности".
Главная проблема Context в том, что при изменении его значения перерисовываются ВСЕ компоненты, которые его используют. Даже те, которым эти изменения совершенно не нужны.
❌ Типичная ошибка — создать один "супер-контекст" со всеми данными приложения: пользователь, тема, корзина, уведомления. При добавлении товара в корзину перерисовывается весь интерфейс, включая компоненты, которые показывают только тему оформления.
✅ Правильный подход — разделять Context по доменам. Отдельный контекст для пользователя, отдельный для темы, отдельный для корзины. Тогда изменения в корзине не затронут компоненты темы.
Еще одна проблема — передача нового объекта в value каждый рендер. React считает объекты разными, даже если их содержимое одинаковое, и запускает перерисовку всех потребителей.
Пример:
Используйте Context для редко меняющихся данных (тема, язык, пользователь). Для частых обновлений лучше выбрать специализированные решения вроде Zustand или React Query.
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
React Context — это удобно, но при неправильном использовании превращается в "пожирателя производительности".
Главная проблема Context в том, что при изменении его значения перерисовываются ВСЕ компоненты, которые его используют. Даже те, которым эти изменения совершенно не нужны.
Еще одна проблема — передача нового объекта в value каждый рендер. React считает объекты разными, даже если их содержимое одинаковое, и запускает перерисовку всех потребителей.
Пример:
// Плохо - новый объект каждый рендер
function Provider({children}) {
const [user, setUser] = useState();
return (
<Context.Provider value={{user, setUser}}> // Всегда новый объект!
{children}
</Context.Provider>
);
}
// Хорошо - стабильное значение
function Provider({children}) {
const [user, setUser] = useState();
const value = useMemo(() => ({user, setUser}), [user]);
return (
<Context.Provider value={value}>
{children}
</Context.Provider>
);
}
Используйте Context для редко меняющихся данных (тема, язык, пользователь). Для частых обновлений лучше выбрать специализированные решения вроде Zustand или React Query.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
#JavaScript
🚩 Почему массивы в JavaScript — это не настоящие массивы?
Многие разработчики думают, что массивы в JavaScript работают как в других языках программирования. На самом деле это обычные объекты с особым поведением, и это объясняет множество странностей языка.
JavaScript массивы — это объекты со специальным свойством length и методами для работы с индексами. Под капотом [1, 2, 3] превращается в объект {0: 1, 1: 2, 2: 3, length: 3}. Именно поэтому typeof [] возвращает "object", а не "array".
Эта особенность позволяет делать с массивами то, что невозможно в других языках, но также создает неожиданное поведение.
Примеры странного поведения:
📎 Ключевые особенности JavaScript массивов:
🟣 Индексы всегда строки — даже когда выглядят как числа
🟣 Length не всегда равен количеству элементов — может быть больше из-за "дырок"
🟣 Могут содержать произвольные свойства — которые не влияют на length
🟣 Автоматически расширяются — при присвоении по большому индексу
Понимание этих особенностей критично для избежания багов:
Практические последствия:
✅ Безопасные практики:
- Используйте методы массива (push, pop, splice) вместо прямого присвоения
- Для итерации используйте for...of или методы типа forEach
- Проверяйте элементы на undefined при работе с внешними данными
❌ Избегайте:
- Присвоения по произвольным индексам
- Добавления нечисловых свойств к массивам
- Полагания на length как индикатор количества элементов
JavaScript массивы гибкие, но эта гибкость требует понимания их истинной природы. Помните: это объекты, притворяющиеся массивами!
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Многие разработчики думают, что массивы в JavaScript работают как в других языках программирования. На самом деле это обычные объекты с особым поведением, и это объясняет множество странностей языка.
JavaScript массивы — это объекты со специальным свойством length и методами для работы с индексами. Под капотом [1, 2, 3] превращается в объект {0: 1, 1: 2, 2: 3, length: 3}. Именно поэтому typeof [] возвращает "object", а не "array".
Эта особенность позволяет делать с массивами то, что невозможно в других языках, но также создает неожиданное поведение.
Примеры странного поведения:
// Можно добавлять произвольные свойства
const arr = [1, 2, 3];
arr.customProperty = 'hello';
arr['weird'] = 'world';
// Можно создавать "дырки" в массиве
const sparse = [];
sparse[100] = 'last';
console.log(sparse.length); // 101
console.log(sparse[50]); // undefined
// Индексы автоматически преобразуются в строки
arr[1] === arr['1']; // true
arr[01] === arr[1]; // true (01 это восьмеричная запись)
Понимание этих особенностей критично для избежания багов:
// Плохо - можно случайно создать "дырку"
const items = ['a', 'b', 'c'];
items[items.length + 10] = 'd'; // Создали дырку!
for (let i = 0; i < items.length; i++) {
console.log(items[i]); // Выведет много undefined
}
// Хорошо - используем методы массива
const items = ['a', 'b', 'c'];
items.push('d'); // Безопасное добавление
// Плохо - полагаемся на length для подсчета элементов
const count = arr.length; // Может быть неточно из-за "дырок"
// Хорошо - считаем реальные элементы
const count = arr.filter(item => item !== undefined).length;
Практические последствия:
- for...in итерирует по всем свойствам, включая нечисловые индексы
- Array.from() может создать неожиданно большой массив из sparse array
- JSON.stringify() превращает "дырки" в null
- Используйте методы массива (push, pop, splice) вместо прямого присвоения
- Для итерации используйте for...of или методы типа forEach
- Проверяйте элементы на undefined при работе с внешними данными
- Присвоения по произвольным индексам
- Добавления нечисловых свойств к массивам
- Полагания на length как индикатор количества элементов
JavaScript массивы гибкие, но эта гибкость требует понимания их истинной природы. Помните: это объекты, притворяющиеся массивами!
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍8❤5🔥4
#TypeScript
🚩 Почему any в TypeScript хуже, чем отсутствие типов?
Многие разработчики считают any безобидной "заглушкой" для сложных типов. На самом деле any не просто отключает проверку типов — он активно их ломает, создавая ложное чувство безопасности и распространяя "заражение" по всему коду.
TypeScript с any хуже обычного JavaScript, потому что создает иллюзию типизации. В JavaScript вы знаете, что типов нет, и пишете соответствующие проверки. С any вы думаете, что защищены, но на самом деле беззащитны.
Any — это не "я разберусь с типом позже", это "я сломал систему типов для всех, кто использует этот код". Проблема в том, что any заразен — он распространяется на все взаимодействующие с ним типы.
Как any ломает типизацию:
📎 Проблемы any в типизации:
🟣 Заражение типов — все взаимодействующие типы становятся any
🟣 Отсутствие автодополнения — IDE не может предложить методы и свойства
🟣 Скрытие ошибок — TypeScript не покажет проблемы до runtime
🟣 Ложная безопасность — код выглядит типобезопасным, но не является таковым
Когда любой тип лучше any:
unknown — для данных с неизвестной структурой
object — для объектов без конкретной структуры
Record<string, unknown> — для словарей с неизвестными значениями
Дженерики — для переиспользуемых функций
Union типы — когда знаете возможные варианты
✅ Безопасные практики:
- Включите strict режим и noImplicitAny в tsconfig
- Используйте unknown вместо any для внешних данных
- Создавайте type guards для проверки структуры объектов
- Применяйте утилитарные типы (Partial, Pick, Omit)
❌ Избегайте:
- Использования any как "быстрого решения"
- Передачи any в качестве параметров функций
- Касты через any без веских причин
Any в TypeScript — это не инструмент разработки, это вредитель. Каждый any делает ваш код менее надежным и сложнее в поддержке!
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Многие разработчики считают any безобидной "заглушкой" для сложных типов. На самом деле any не просто отключает проверку типов — он активно их ломает, создавая ложное чувство безопасности и распространяя "заражение" по всему коду.
TypeScript с any хуже обычного JavaScript, потому что создает иллюзию типизации. В JavaScript вы знаете, что типов нет, и пишете соответствующие проверки. С any вы думаете, что защищены, но на самом деле беззащитны.
Any — это не "я разберусь с типом позже", это "я сломал систему типов для всех, кто использует этот код". Проблема в том, что any заразен — он распространяется на все взаимодействующие с ним типы.
Как any ломает типизацию:
// Плохо - any распространяется как вирус
function processData(data: any) {
return data.someProperty; // Тип результата тоже any!
}
const result = processData(userData); // result: any
const value = result.unknownMethod(); // value: any, ошибка в runtime!
// Хорошо - используйте unknown для неизвестных типов
function processData(data: unknown) {
// Принуждает к проверке типа
if (typeof data === 'object' && data !== null && 'someProperty' in data) {
return (data as {someProperty: string}).someProperty;
}
throw new Error('Invalid data format');
}
Когда любой тип лучше any:
unknown — для данных с неизвестной структурой
object — для объектов без конкретной структуры
Record<string, unknown> — для словарей с неизвестными значениями
Дженерики — для переиспользуемых функций
Union типы — когда знаете возможные варианты
- Включите strict режим и noImplicitAny в tsconfig
- Используйте unknown вместо any для внешних данных
- Создавайте type guards для проверки структуры объектов
- Применяйте утилитарные типы (Partial, Pick, Omit)
- Использования any как "быстрого решения"
- Передачи any в качестве параметров функций
- Касты через any без веских причин
Any в TypeScript — это не инструмент разработки, это вредитель. Каждый any делает ваш код менее надежным и сложнее в поддержке!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3
#React
🚩 Почему key={index} в React — это не просто warning, а реальная проблема?
React использует ключи для определения, какие элементы изменились, добавились или удалились. Когда вы используете индекс как ключ, React не может корректно отследить элементы при изменении порядка списка.
Это приводит к тому, что React переиспользует компоненты не по назначению, сохраняя состояние там, где его быть не должно.
Классический пример проблемы:
📎 Проблемы key={index}:
🟣 Потеря состояния компонентов — фокус, введенный текст, анимации
🟣 Неправильные обновления — React обновляет не те элементы
🟣 Проблемы с производительностью — лишние перерендеры и создания компонентов
🟣 Баги в формах — значения полей "перепрыгивают" между элементами
Когда index может быть приемлемым:
Статический список — никогда не изменяется порядок
Без состояния — компоненты не хранят внутреннее состояние
Простой вывод — только отображение данных без интерактивности
Пагинация — каждая страница загружается заново
✅ Правильные практики:
- Используйте уникальные и стабильные идентификаторы
- Ключ должен быть одинаковым для одного элемента между рендерами
- Избегайте генерации ключей внутри render функции
- Тестируйте изменение порядка элементов
❌ Избегайте:
- key={index} для динамических списков
- Случайных ключей (Math.random(), Date.now() в рендере)
- Одинаковых ключей для разных элементов
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
React использует ключи для определения, какие элементы изменились, добавились или удалились. Когда вы используете индекс как ключ, React не может корректно отследить элементы при изменении порядка списка.
Это приводит к тому, что React переиспользует компоненты не по назначению, сохраняя состояние там, где его быть не должно.
Классический пример проблемы:
// Плохо - key={index} ломает логику
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
<TodoItem
key={index} // Индекс как ключ!
todo={todo}
onComplete={() => completeTodo(todo.id)}
/>
))}
</ul>
);
}
// При удалении первого элемента:
// Было: [0: "Купить молоко", 1: "Погулять с собакой", 2: "Написать код"]
// Стало: [0: "Погулять с собакой", 1: "Написать код"]
// React думает, что изменился последний элемент, а не первый!
// Хорошо - уникальный и стабильный ключ
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id} // Стабильный уникальный ключ
todo={todo}
onComplete={() => completeTodo(todo.id)}
/>
))}
</ul>
);
}
Когда index может быть приемлемым:
Статический список — никогда не изменяется порядок
Без состояния — компоненты не хранят внутреннее состояние
Простой вывод — только отображение данных без интерактивности
Пагинация — каждая страница загружается заново
- Используйте уникальные и стабильные идентификаторы
- Ключ должен быть одинаковым для одного элемента между рендерами
- Избегайте генерации ключей внутри render функции
- Тестируйте изменение порядка элементов
- key={index} для динамических списков
- Случайных ключей (Math.random(), Date.now() в рендере)
- Одинаковых ключей для разных элементов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤2
#JavaScript
🚩 Почему замыкания в JavaScript создают утечки памяти и как этого избежать?
Замыкание — это функция, которая "запоминает" переменные из внешней области видимости даже после того, как эта область перестала существовать. Проблема в том, что JavaScript движок не может освободить память от этих переменных, пока существует ссылка на замыкание.
Это создает скрытые утечки памяти, которые накапливаются со временем и замедляют приложение.
📎 Основные источники утечек через замыкания:
🟣 Обработчики событий — накапливаются при каждом создании компонента
🟣 Таймеры и интервалы — продолжают держать ссылки после очистки
🟣 Кеширование функций — сохраняют контекст навсегда
🟣 React useCallback — может держать устаревшие зависимости
Частые ошибки в React компонентах:
✅ Безопасные практики:
- Выносите тяжелые вычисления за пределы замыканий
- Обнуляйте ссылки на большие объекты после использования
- Используйте WeakMap для кеширования с автоочисткой
- Регулярно профилируйте память в DevTools
❌ Избегайте:
- Захвата больших объектов в замыкания без необходимости
- Бесконечно живущих замыканий с большим контекстом
- Кеширования замыканий без стратегии очистки
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Замыкание — это функция, которая "запоминает" переменные из внешней области видимости даже после того, как эта область перестала существовать. Проблема в том, что JavaScript движок не может освободить память от этих переменных, пока существует ссылка на замыкание.
Это создает скрытые утечки памяти, которые накапливаются со временем и замедляют приложение.
Частые ошибки в React компонентах:
// ❌ ПЛОХО - утечка памяти
function BadComponent() {
const [name, setName] = useState('');
const [bigData] = useState(new Array(100000).fill('данные')); // 100к элементов
// Замыкание захватывает ВСЁ - включая bigData, хотя не использует его!
const handleClick = useCallback(() => {
console.log(`Привет, ${name}!`); // Использует только name
}, [name]); // bigData НЕ в зависимостях, но ВСЁ РАВНО захвачен замыканием!
return <button onClick={handleClick}>Нажми</button>;
}
// ✅ ХОРОШО - без утечки
function GoodComponent() {
const [name, setName] = useState('');
// bigData вынесен за пределы компонента или в отдельный хук
const handleClick = useCallback(() => {
console.log(`Привет, ${name}!`);
}, [name]); // Захватывает только легкую строку name
return <button onClick={handleClick}>Нажми</button>;
}
// Проблема: замыкание в useCallback "видит" ВСЕ переменные из области видимости
// и держит на них ссылки, даже если функция их не использует!
- Выносите тяжелые вычисления за пределы замыканий
- Обнуляйте ссылки на большие объекты после использования
- Используйте WeakMap для кеширования с автоочисткой
- Регулярно профилируйте память в DevTools
- Захвата больших объектов в замыкания без необходимости
- Бесконечно живущих замыканий с большим контекстом
- Кеширования замыканий без стратегии очистки
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤2🏆2
#JavaScript
🚩 Почему setTimeout(fn, 0) не выполняется мгновенно и что такое Event Loop на самом деле?
Многие думают, что JavaScript — однопоточный язык, где код выполняется строка за строкой. На самом деле это лишь половина правды. JavaScript действительно однопоточный — у него один Call Stack для выполнения кода. Но браузер предоставляет дополнительные возможности: Web APIs (setTimeout, fetch, DOM events), которые работают параллельно с основным потоком.
Event Loop — это механизм, который решает, когда и в каком порядке выполнять асинхронные задачи.
🖇 Как работает Event Loop:
- Call Stack — выполняет синхронный код по принципу LIFO
- Web APIs — обрабатывают асинхронные операции (таймеры, HTTP запросы)
- Task Queue — очередь для колбэков из Web APIs
- Microtask Queue — приоритетная очередь для промисов и queueMicrotask
Практический пример неожиданного поведения:
❓ Почему именно такой порядок?
1. Выполняется весь синхронный код (1, 4)
2. Call Stack пустой — Event Loop проверяет очереди
3. Микрозадачи имеют приоритет над макрозадачами (3)
4. Только потом выполняются макрозадачи (2)
🖇 Приоритеты Event Loop (от высшего к низшему):
1. Синхронный код в Call Stack
2. Microtask Queue (промисы, queueMicrotask)
3. Task Queue (setTimeout, setInterval, DOM события)
4. Render Queue (перерисовка браузера)
Частые ошибки в понимании:
🖇 Проблемы с неправильным пониманием Event Loop:
- Интерфейс "зависает" из-за долгих синхронных операций
- Неправильный порядок выполнения асинхронных задач
- Race conditions в сложных сценариях
- Проблемы с анимациями и пользовательским опытом
✅ Практические правила:
- Разбивайте тяжелые вычисления на части через setTimeout
- Используйте requestAnimationFrame для анимаций
- Помните о приоритете микрозадач при отладке
- Не блокируйте Event Loop долгими синхронными операциями
❌ Избегайте:
- Тяжелых синхронных циклов без перерывов
- Предположений о том, что setTimeout(fn, 0) выполнится мгновенно
- Игнорирования разницы между микро- и макрозадачами
Event Loop — это сердце JavaScript. Понимание его работы критично для создания отзывчивых приложений
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Многие думают, что JavaScript — однопоточный язык, где код выполняется строка за строкой. На самом деле это лишь половина правды. JavaScript действительно однопоточный — у него один Call Stack для выполнения кода. Но браузер предоставляет дополнительные возможности: Web APIs (setTimeout, fetch, DOM events), которые работают параллельно с основным потоком.
Event Loop — это механизм, который решает, когда и в каком порядке выполнять асинхронные задачи.
- Call Stack — выполняет синхронный код по принципу LIFO
- Web APIs — обрабатывают асинхронные операции (таймеры, HTTP запросы)
- Task Queue — очередь для колбэков из Web APIs
- Microtask Queue — приоритетная очередь для промисов и queueMicrotask
Практический пример неожиданного поведения:
console.log('1'); // Синхронно
setTimeout(() => console.log('2'), 0); // Макрозадача
Promise.resolve().then(() => console.log('3')); // Микрозадача
console.log('4'); // Синхронно
// Результат: 1, 4, 3, 2
// НЕ 1, 2, 3, 4 как многие ожидают!
1. Выполняется весь синхронный код (1, 4)
2. Call Stack пустой — Event Loop проверяет очереди
3. Микрозадачи имеют приоритет над макрозадачами (3)
4. Только потом выполняются макрозадачи (2)
1. Синхронный код в Call Stack
2. Microtask Queue (промисы, queueMicrotask)
3. Task Queue (setTimeout, setInterval, DOM события)
4. Render Queue (перерисовка браузера)
Частые ошибки в понимании:
// ❌ ПЛОХО - блокировка интерфейса
for (let i = 0; i < 1000000; i++) {
// Синхронный код блокирует Event Loop
heavyCalculation();
}
// ✅ ХОРОШО - не блокируем Event Loop
function processChunk(data, chunkSize = 1000) {
const chunk = data.splice(0, chunkSize);
chunk.forEach(item => heavyCalculation(item));
if (data.length > 0) {
setTimeout(() => processChunk(data, chunkSize), 0); // Даем Event Loop передохнуть
}
}
- Интерфейс "зависает" из-за долгих синхронных операций
- Неправильный порядок выполнения асинхронных задач
- Race conditions в сложных сценариях
- Проблемы с анимациями и пользовательским опытом
- Разбивайте тяжелые вычисления на части через setTimeout
- Используйте requestAnimationFrame для анимаций
- Помните о приоритете микрозадач при отладке
- Не блокируйте Event Loop долгими синхронными операциями
- Тяжелых синхронных циклов без перерывов
- Предположений о том, что setTimeout(fn, 0) выполнится мгновенно
- Игнорирования разницы между микро- и макрозадачами
Event Loop — это сердце JavaScript. Понимание его работы критично для создания отзывчивых приложений
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2❤1
#JavaScript
🚩 Почему var "всплывает", а let/const бросают ошибки? Разбираемся с hoisting и Temporal Dead Zone
Многие разработчики думают, что hoisting — это просто "поднятие переменных наверх". На самом деле это результат двухфазной обработки кода JavaScript движком.
Сначала JavaScript сканирует весь код и создает переменные в памяти (compilation phase), а потом уже выполняет код и присваивает значения (execution phase). Все объявления "всплывают", но инициализируются по-разному.
Var всплывает и сразу инициализируется как undefined. Let и const тоже всплывают, но попадают в "мертвую зону" (Temporal Dead Zone) — они существуют в памяти, но обращение к ним до инициализации вызывает ошибку.
Пример, который ломает мозг:
Именно поэтому современный JavaScript рекомендует let/const — они защищают от случайного использования переменных до их объявления, превращая потенциальные баги в явные ошибки.
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Многие разработчики думают, что hoisting — это просто "поднятие переменных наверх". На самом деле это результат двухфазной обработки кода JavaScript движком.
Сначала JavaScript сканирует весь код и создает переменные в памяти (compilation phase), а потом уже выполняет код и присваивает значения (execution phase). Все объявления "всплывают", но инициализируются по-разному.
Var всплывает и сразу инициализируется как undefined. Let и const тоже всплывают, но попадают в "мертвую зону" (Temporal Dead Zone) — они существуют в памяти, но обращение к ним до инициализации вызывает ошибку.
Пример, который ломает мозг:
// Что выведет этот код?
console.log(typeof myVar); // "undefined"
console.log(myVar); // undefined
if (false) {
var myVar = 'never executed';
}
// Var всплывает и инициализируется undefined,
// даже если код в блоке никогда не выполнится!
// А теперь с let:
console.log(typeof myLet); // ReferenceError!
if (false) {
let myLet = 'never executed';
}
Именно поэтому современный JavaScript рекомендует let/const — они защищают от случайного использования переменных до их объявления, превращая потенциальные баги в явные ошибки.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2
#React
🚩 Почему useEffect с пустым массивом зависимостей — это не componentDidMount?
Многие React разработчики думают, что useEffect(() => {}, []) — это прямой аналог componentDidMount из классовых компонентов. На самом деле это опасное заблуждение, которое приводит к тонким багам и проблемам с производительностью.
Главное отличие в том, что useEffect выполняется ПОСЛЕ рендера и обновления DOM, а componentDidMount — сразу после монтирования. Но есть гораздо более серьезная проблема: в React 18+ с Strict Mode компоненты могут размонтироваться и смонтироваться заново для проверки корректности cleanup функций.
Это значит, что ваш "одноразовый" useEffect может выполниться дважды!
Типичная ошибка — думать, что код выполнится только один раз:
Правильный подход — всегда писать cleanup функции:
📎 Ключевые отличия от componentDidMount:
🟣 Время выполнения — после обновления DOM, не сразу после монтирования
🟣 Strict Mode — может выполняться дважды в development режиме
🟣 Cleanup обязателен — React ожидает возможность "отменить" эффект
🟣 Concurrent Features — в будущих версиях React эффекты могут прерываться
Еще одна проблема — попытка имитировать componentWillUnmount через cleanup функцию. Cleanup вызывается не только при размонтировании, но и перед каждым повторным выполнением эффекта.
✅ Безопасные практики:
- Всегда пишите cleanup функции для асинхронных операций
- Тестируйте компоненты с включенным Strict Mode
- Используйте флаги отмены для предотвращения race conditions
- Помните: cleanup ≠ componentWillUnmount
❌ Избегайте:
- Предположений о единоразовом выполнении useEffect
- Побочных эффектов без возможности их отмены
- Прямого сравнения с методами жизненного цикла классов
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Многие React разработчики думают, что useEffect(() => {}, []) — это прямой аналог componentDidMount из классовых компонентов. На самом деле это опасное заблуждение, которое приводит к тонким багам и проблемам с производительностью.
Главное отличие в том, что useEffect выполняется ПОСЛЕ рендера и обновления DOM, а componentDidMount — сразу после монтирования. Но есть гораздо более серьезная проблема: в React 18+ с Strict Mode компоненты могут размонтироваться и смонтироваться заново для проверки корректности cleanup функций.
Это значит, что ваш "одноразовый" useEffect может выполниться дважды!
Типичная ошибка — думать, что код выполнится только один раз:
// Плохо - может выполниться дважды в development!
useEffect(() => {
fetchUserData(); // Запрос может дублироваться
initializeAnalytics(); // Аналитика инициализируется дважды
startWebSocket(); // Создается два подключения!
}, []); // Пустой массив НЕ гарантирует одноразовое выполнение
Правильный подход — всегда писать cleanup функции:
// Хорошо - учитываем возможное повторное выполнение
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
const data = await fetchUserData();
if (!cancelled) {
setUser(data);
}
};
fetchData();
return () => {
cancelled = true; // Cleanup предотвращает обновление состояния
};
}, []);
Еще одна проблема — попытка имитировать componentWillUnmount через cleanup функцию. Cleanup вызывается не только при размонтировании, но и перед каждым повторным выполнением эффекта.
// Неправильное понимание cleanup
useEffect(() => {
const timer = setInterval(tick, 1000);
return () => {
clearInterval(timer); // Вызывается НЕ только при размонтировании!
};
}, [someValue]); // При изменении someValue cleanup тоже сработает
- Всегда пишите cleanup функции для асинхронных операций
- Тестируйте компоненты с включенным Strict Mode
- Используйте флаги отмены для предотвращения race conditions
- Помните: cleanup ≠ componentWillUnmount
- Предположений о единоразовом выполнении useEffect
- Побочных эффектов без возможности их отмены
- Прямого сравнения с методами жизненного цикла классов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1
#CSS
🚩 Почему margin: auto не центрирует элемент, а иногда центрирует?
На самом деле это работает только в очень специфических условиях, а в остальных случаях создает больше проблем, чем решает.
Margin: auto работает только тогда, когда браузер может вычислить "лишнее" пространство. Для блочных элементов это работает горизонтально (если есть ширина), но НЕ работает вертикально. Для инлайн элементов не работает вообще.
Типичные ошибки с margin: auto:
Когда margin: auto действительно работает:
📎 Почему margin: auto такой "капризный":
🟣 Нужна явная ширина — без width браузер не знает, что центрировать
🟣 Работает только горизонтально — вертикальное центрирование требует других методов
🟣 Не работает с float — плавающие элементы выходят из нормального потока
🟣 Игнорируется инлайн элементами — нужно сделать display: block
Современные альтернативы намного надежнее:
- Flexbox для одномерного центрирования
- Grid для двумерного позиционирования
- Position + transform для абсолютного центрирования
✅ Современный подход:
- Используйте flexbox/grid вместо margin: auto для центрирования
- Margin: auto оставьте для горизонтального центрирования блоков
- Изучите современные методы layout вместо старых трюков
❌ Избегайте:
- Попыток вертикального центрирования через margin: auto
- Использования без явной ширины элемента
- Применения к инлайн элементам
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
На самом деле это работает только в очень специфических условиях, а в остальных случаях создает больше проблем, чем решает.
Margin: auto работает только тогда, когда браузер может вычислить "лишнее" пространство. Для блочных элементов это работает горизонтально (если есть ширина), но НЕ работает вертикально. Для инлайн элементов не работает вообще.
Типичные ошибки с margin: auto:
/* Плохо - не сработает */
.center-me {
margin: auto; /* Вертикально НЕ центрирует! */
width: 300px;
height: 200px;
}
/* Плохо - инлайн элементы игнорируют margin: auto */
span {
margin: auto; /* Бесполезно для инлайн */
}
Когда margin: auto действительно работает:
/* Хорошо - центрирование блока горизонтально */
.container {
width: 300px; /* Обязательно указать ширину! */
margin: 0 auto; /* Только горизонтально */
}
/* Современное решение - используйте flexbox */
.parent {
display: flex;
justify-content: center; /* Горизонтально */
align-items: center; /* Вертикально */
}
Современные альтернативы намного надежнее:
- Flexbox для одномерного центрирования
- Grid для двумерного позиционирования
- Position + transform для абсолютного центрирования
- Используйте flexbox/grid вместо margin: auto для центрирования
- Margin: auto оставьте для горизонтального центрирования блоков
- Изучите современные методы layout вместо старых трюков
- Попыток вертикального центрирования через margin: auto
- Использования без явной ширины элемента
- Применения к инлайн элементам
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍10❤1
#TypeScript
🚩 Почему interface и type в TypeScript НЕ взаимозаменяемы?
Ключевое различие: interface предназначен для описания структуры объектов и может быть расширен другими разработчиками. Type — это алиас для любого типа, он закрыт для расширения после объявления.
Типичная ошибка — использовать type для публичных API:
Правильный подход — interface для расширяемых структур:
📎 Когда использовать каждый:
🟣 Interface — объекты, классы, расширяемые API, библиотеки
🟣 Type — union типы, primitive алиасы, computed типы, закрытые структуры
Уникальные возможности type:
Уникальные возможности interface:
✅ Практические правила:
- Interface для объектов и расширяемых API
- Type для union типов и алиасов
- Interface для библиотек (позволяет пользователям расширять)
- Type для внутренней логики приложения
❌ Избегайте:
- Type для публичных API, которые могут расширяться
- Interface для простых алиасов примитивов
- Смешивания подходов в одном проекте без правил
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Ключевое различие: interface предназначен для описания структуры объектов и может быть расширен другими разработчиками. Type — это алиас для любого типа, он закрыт для расширения после объявления.
Типичная ошибка — использовать type для публичных API:
// Плохо - закрытый тип, нельзя расширить
export type UserConfig = {
theme: string;
language: string;
};
// Пользователи НЕ могут добавить свои поля!
// UserConfig & { customField: string } - приходится создавать новый тип
Правильный подход — interface для расширяемых структур:
// Хорошо - открытый для расширения
export interface UserConfig {
theme: string;
language: string;
}
// Пользователи могут легко расширять
declare module 'your-lib' {
interface UserConfig {
customField: string; // Declaration merging!
}
}
Уникальные возможности type:
// Только type может делать union типы
type Status = 'loading' | 'success' | 'error';
// Computed типы на основе других
type UserKeys = keyof User; // 'name' | 'email' | 'id'
// Условные типы
type NonNullable<T> = T extends null | undefined ? never : T;
Уникальные возможности interface:
// Declaration merging - объединение объявлений
interface Window {
myGlobalVar: string;
}
interface Window {
anotherVar: number; // Автоматически объединится!
}
// Расширение через extends (более читаемо чем &)
interface AdminUser extends User {
permissions: string[];
}
- Interface для объектов и расширяемых API
- Type для union типов и алиасов
- Interface для библиотек (позволяет пользователям расширять)
- Type для внутренней логики приложения
- Type для публичных API, которые могут расширяться
- Interface для простых алиасов примитивов
- Смешивания подходов в одном проекте без правил
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5
#React
🚩 Почему useState с объектами и массивами ломает React и как это исправить?
React использует Object.is() для сравнения состояния, а не глубокое сравнение. Когда вы мутируете объект или массив напрямую, ссылка остается той же, и React думает, что ничего не изменилось. Компонент не перерендеривается, UI не обновляется.
Это создает "призрачные баги" — состояние в DevTools показывает новые данные, но интерфейс отображает старые.
Типичные ошибки с мутациями:
Правильный подход — иммутабельные обновления:
📎 Проблемы мутаций в React:
🟣 Пропущенные рендеры — UI не синхронизирован с состоянием
🟣 Баги в useEffect — зависимости не отслеживают мутации
🟣 Проблемы с memo — оптимизация не работает
🟣 Race conditions — асинхронные обновления перезаписывают изменения
Особенно опасны мутации в useEffect:
✅ Инструменты для иммутабельности:
- Spread оператор для простых случаев
- Immer для сложных структур
- Ramda/Lodash fp для функционального подхода
- useImmer хук для удобства
❌ Избегайте:
- Прямых мутаций state объектов и массивов
- Методов типа push(), pop(), sort() на state массивах
- Изменения вложенных свойств без создания новых объектов
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
React использует Object.is() для сравнения состояния, а не глубокое сравнение. Когда вы мутируете объект или массив напрямую, ссылка остается той же, и React думает, что ничего не изменилось. Компонент не перерендеривается, UI не обновляется.
Это создает "призрачные баги" — состояние в DevTools показывает новые данные, но интерфейс отображает старые.
Типичные ошибки с мутациями:
// Плохо - мутация массива
const [items, setItems] = useState([1, 2, 3]);
const addItem = () => {
items.push(4); // Мутация!
setItems(items); // React не увидит изменений
};
// Плохо - мутация объекта
const [user, setUser] = useState({name: 'John', age: 25});
const updateAge = () => {
user.age = 26; // Мутация!
setUser(user); // Ссылка та же, React не обновится
};
Правильный подход — иммутабельные обновления:
// Хорошо - новый массив
const addItem = () => {
setItems([...items, 4]); // Новая ссылка
};
const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};
// Хорошо - новый объект
const updateAge = () => {
setUser({...user, age: 26}); // Новая ссылка
};
// Для сложных объектов используйте immer
const updateUser = () => {
setUser(produce(user, draft => {
draft.profile.settings.theme = 'dark';
}));
};
Особенно опасны мутации в useEffect:
// Плохо - бесконечный цикл или пропущенные обновления
useEffect(() => {
data.processed = true; // Мутация в зависимости!
setData(data);
}, [data]); // data ссылка не изменилась, эффект может не сработать
- Spread оператор для простых случаев
- Immer для сложных структур
- Ramda/Lodash fp для функционального подхода
- useImmer хук для удобства
- Прямых мутаций state объектов и массивов
- Методов типа push(), pop(), sort() на state массивах
- Изменения вложенных свойств без создания новых объектов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥3❤2
#JavaScript
🚩 Почему == в JavaScript сравнивает не то, что вы думаете?
JavaScript использует алгоритм Type Coercion с 11 шагами, который может давать совершенно неожиданные результаты. Проблема не просто в том, что [ ] == false (что логично), а в том, что [ ] == ![ ] возвращает true. То есть массив равен своему отрицанию!
Примеры, которые сломают мозг:
Безопасная альтернатива — строгое сравнение:
📎 Почему == такой непредсказуемый:
🟣 Асимметричность — x == y может отличаться от y == x
🟣 Контекстная зависимость — результат зависит от порядка операндов
🟣 Множественные преобразования — один операнд может преобразовываться несколько раз
🟣 Исключения из правил — null и undefined имеют особое поведение
Алгоритм Type Coercion настолько сложен, что даже опытные разработчики не могут предсказать результат без документации. Он включает преобразования к примитивам, числам, строкам, с особыми правилами для каждого типа.
✅ Современные практики:
- Всегда используйте === и !== для сравнений
- Единственное исключение: value != null для проверки на null/undefined
- Используйте TypeScript для предотвращения случайных сравнений разных типов
- Применяйте ESLint правило eqeqeq для автоматического контроля
❌ Никогда не используйте == когда:
- Сравниваете с boolean (используйте !!value)
- Работаете с числами и строками одновременно
- Не уверены в типах операндов
🔎 🔎 🔎 🔎 🔄
#⃣ Новости
#⃣ База вопросов
JavaScript использует алгоритм Type Coercion с 11 шагами, который может давать совершенно неожиданные результаты. Проблема не просто в том, что [ ] == false (что логично), а в том, что [ ] == ![ ] возвращает true. То есть массив равен своему отрицанию!
Примеры, которые сломают мозг:
// Логика JavaScript'а
[] == false // true (массив пустой, false пустой)
[] == ![] // true (массив равен собственному отрицанию!)
[] == 0 // true (массив преобразуется к 0)
'0' == false // true (строка '0' становится числом 0)
// Но внимание:
false == 'false' // false (!)
false == undefined // false
false == null // false
// И совсем сумасшествие:
null == 0 // false
null >= 0 // true (!)
null > 0 // false
// undefined ведет себя по-другому:
undefined == 0 // false
undefined >= 0 // false
undefined == null // true
Безопасная альтернатива — строгое сравнение:
// Хорошо - предсказуемое поведение
[] === false // false
'0' === 0 // false
null === 0 // false
undefined === 0 // false
// Для проверки на null/undefined используйте явные проверки:
if (value != null) { // Единственный полезный случай для ==
// value не null и не undefined
}
// Или еще лучше:
if (value !== null && value !== undefined) {
// Явно и понятно
}
Алгоритм Type Coercion настолько сложен, что даже опытные разработчики не могут предсказать результат без документации. Он включает преобразования к примитивам, числам, строкам, с особыми правилами для каждого типа.
- Всегда используйте === и !== для сравнений
- Единственное исключение: value != null для проверки на null/undefined
- Используйте TypeScript для предотвращения случайных сравнений разных типов
- Применяйте ESLint правило eqeqeq для автоматического контроля
- Сравниваете с boolean (используйте !!value)
- Работаете с числами и строками одновременно
- Не уверены в типах операндов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥2❤1
Forwarded from Reactify | Frontend Разработка
На собеседованиях часто просят определить порядок вывода в консоль — это типичные задачи на понимание работы Event Loop в JavaScript. Их нужно не просто уметь решать, но и грамотно объяснять, почему вывод именно такой.
Многие новички теряются на таких заданиях, потому что не понимают, как работает механизм очередей и стеков исполнения. На самом деле, всё становится гораздо проще, когда ты разберёшься в логике работы Event Loop. После этого ты сможешь решать практически любую подобную задачу.
Кстати, у меня есть видео, где я подробно разбираю 5 разных задач, показывая на схеме каждый шаг:
— что куда попадает
— в какую очередь отправляется
— в какой момент выполняется
Рекомендую посмотреть, особенно если хочешь уверенно чувствовать себя на техническом собеседовании.
console.log('Start');
Promise.resolve()
.then(() => {
console.log('Promise 1');
Promise.resolve()
.then(() => console.log('Nested Promise'));
})
.then(() => console.log('Promise 2'));
console.log('End');
console.log('Start');
setTimeout(() => console.log('Timeout 1'), 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
setTimeout(() => {
console.log('Timeout 2');
Promise.resolve().then(() => console.log('Promise inside Timeout'));
}, 0);
});
setTimeout(() => console.log('Timeout 3'), 0);
console.log('End');
Promise.resolve('Data')
.then(res => { throw new Error('Problem'); })
.catch(err => {
console.log('Caught:', err.message);
return 'Recovered';
})
.finally(() => console.log('Cleanup'))
.then(res => console.log('Final:', res));
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Разбор задач по Event Loop с собеседований
В этом видео мы подробно разберем выполнение кода в задачах по Event Loop шаг за шагом. Мы визуализируем процесс на схеме, чтобы показать, что происходит с кодом в момент его выполнения. Разберемся, как работает стек вызовов, что такое микротаски, макротаски…
👍6❤2🔥2
#JavaScript
🚩 Почему Promise.all() может убить ваше приложение одной неудачной операцией?
Представьте: вы загружаете профили 100 пользователей. 99 запросов успешны, но один пользователь удалил аккаунт. Promise.all() отбросит ВСЕ результаты и вернет только ошибку. Вы потеряете 99 валидных результатов из-за одной неудачи!
Типичная ошибка — использовать Promise.all() для независимых операций:
Правильный подход — Promise.allSettled() для независимых операций:
📎 Когда использовать каждый метод:
🟣 Promise.all() — когда ВСЕ операции критичны (оплата + списание + логирование)
🟣 Promise.allSettled() — когда операции независимы (загрузка данных, аналитика)
🟣 Promise.race() — когда нужен первый результат (timeout, fallback)
Еще одна проблема — Promise.all() не отменяет остальные промисы при ошибке. Они продолжают выполняться в фоне, тратя ресурсы и потенциально вызывая побочные эффекты.
✅ Безопасные практики:
- Используйте Promise.allSettled() для независимых операций
- Promise.all() только когда все операции действительно критичны
- Добавляйте timeout для защиты от зависших промисов
- Логируйте частичные неудачи для мониторинга
❌ Избегайте:
- Promise.all() для операций, которые могут независимо падать
- Игнорирования частичных результатов
- Отсутствия обработки ошибок в критичных операциях
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Представьте: вы загружаете профили 100 пользователей. 99 запросов успешны, но один пользователь удалил аккаунт. Promise.all() отбросит ВСЕ результаты и вернет только ошибку. Вы потеряете 99 валидных результатов из-за одной неудачи!
Типичная ошибка — использовать Promise.all() для независимых операций:
// Плохо - одна ошибка убивает всё
const users = await Promise.all([
fetchUser(1), // успех
fetchUser(2), // успех
fetchUser(3), // 404 ошибка
fetchUser(4), // успех
]);
// Результат: только ошибка, успешные результаты потеряны!
Правильный подход — Promise.allSettled() для независимых операций:
// Хорошо - получаем все результаты
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(2),
fetchUser(3),
fetchUser(4),
]);
const users = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
// Получили 3 из 4 пользователей вместо 0!
Еще одна проблема — Promise.all() не отменяет остальные промисы при ошибке. Они продолжают выполняться в фоне, тратя ресурсы и потенциально вызывая побочные эффекты.
- Используйте Promise.allSettled() для независимых операций
- Promise.all() только когда все операции действительно критичны
- Добавляйте timeout для защиты от зависших промисов
- Логируйте частичные неудачи для мониторинга
- Promise.all() для операций, которые могут независимо падать
- Игнорирования частичных результатов
- Отсутствия обработки ошибок в критичных операциях
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤1
Техническое собеседование. ЗП: от 200к. Май 2025. Проект: Сбер. Опыт: 4 года. Задачи: Отсортировать список нечетных чисел, валидность скобок.
Вопросы:
- Перечислите основные методы массивов
- Какие есть способы создания объектов и в чем их отличия?
- Что такое Promise?
- Расскажите про все методы Promise?
- Расскажите про this и контекст?
Все 14 вопросов можно посмотреть на нашей платформе
#собес
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥2❤1
#JavaScript
Изучаешь JavaScript? Тогда это видео для тебя. Сегодня полный курс по тому, как взаимодействовать через JavaScript - DOM API
🔎 🔎 🔎 🔎 🔄
#️⃣ Видео
#⃣Новости
#⃣База вопросов
Изучаешь JavaScript? Тогда это видео для тебя. Сегодня полный курс по тому, как взаимодействовать через JavaScript - DOM API
#⃣Новости
#⃣База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
JavaScript DOM с 0 до Профи. Полный курс + Практика
DOM — это представление HTML-документа в виде дерева объектов, с которым можно работать через JavaScript. В этом видео мы рассмотрим, что такое Document object model и как браузер создает DOM при загрузке страницы. Без DOM in javascript веб-страницы были…
👍6❤2🔥2🏆1
#JavaScript
🚩 Почему closures в JavaScript создают утечки памяти и как их избежать?
Замыкания (closures) — одна из самых мощных фич JavaScript, но они могут незаметно "съедать" память вашего приложения. Проблема в том, что замыкание сохраняет ВСЮ лексическую область видимости, даже если использует только одну переменную из нее.
Когда внешняя функция завершается, все ее переменные должны быть удалены сборщиком мусора. Но если есть замыкание — переменные остаются в памяти навсегда!
Типичная ошибка — случайные замыкания в циклах и обработчиках:
📎 Опасные места для утечек памяти:
🟣 Event listeners — особенно в циклах и с внешними переменными
🟣 Timers и intervals — setInterval с замыканием на большие объекты
🟣 React useCallback — неправильные зависимости кэшируют старые данные
🟣 Node.js серверы — замыкания в обработчиках запросов
Особенно опасны замыкания в React:
✅ Безопасные практики:
❌ Избегайте:
🔎 🔎 🔎 🔎 🔄
#⃣ Вопрос
#⃣ Новости
#⃣ База вопросов
Замыкания (closures) — одна из самых мощных фич JavaScript, но они могут незаметно "съедать" память вашего приложения. Проблема в том, что замыкание сохраняет ВСЮ лексическую область видимости, даже если использует только одну переменную из нее.
Когда внешняя функция завершается, все ее переменные должны быть удалены сборщиком мусора. Но если есть замыкание — переменные остаются в памяти навсегда!
Типичная ошибка — случайные замыкания в циклах и обработчиках:
// Плохо - каждый обработчик держит ссылку на огромный объект
function setupEventListeners(massiveDataArray) {
const hugeObject = { data: massiveDataArray }; // 100MB данных
for (let i = 0; i < 1000; i++) {
document.getElementById(`btn-${i}`).addEventListener('click', () => {
console.log(`Button ${i} clicked`);
// Замыкание держит ВЕСЬ hugeObject в памяти!
});
}
} // hugeObject никогда не будет удален из памяти
Правильный подход — минимизация замыканий:
javascript// Хорошо - передаем только нужные данные
function setupEventListeners(massiveDataArray) {
for (let i = 0; i < 1000; i++) {
const buttonId = i; // Копируем только нужное значение
document.getElementById(`btn-${i}`).addEventListener('click', () => {
console.log(`Button ${buttonId} clicked`);
// Замыкание держит только buttonId, не весь scope
});
}
}
// Еще лучше - вынести функцию наружу
function createClickHandler(buttonId) {
return () => console.log(`Button ${buttonId} clicked`);
}
Особенно опасны замыкания в React:
// Плохо - компонент держит все предыдущие состояния
const MyComponent = () => {
const [count, setCount] = useState(0);
const [heavyData, setHeavyData] = useState(null);
const handleClick = useCallback(() => {
// Замыкание на старое значение heavyData!
console.log(heavyData?.length);
setCount(count + 1);
}, []); // Пустые зависимости = замыкание на первый рендер
return <button onClick={handleClick}>Click</button>;
};
Используйте правильные зависимости в useCallback/useMemo
Очищайте event listeners при размонтировании
Выносите функции наружу, если они не нуждаются в замыкании
Используйте WeakMap/WeakSet для слабых ссылок
Замыканий на большие объекты без необходимости
Пустых массивов зависимостей в React хуках
Забывать удалять обработчики событий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1