Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Замикання». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Замикання (closure) це функція, яка «пам'ятає» змінні з області, де її було створено, навіть якщо ця область уже завершила виконання.** Формально замикання це функція плюс її лексичне оточення (scope): поки на внутрішню функцію є посилання, захоплені змінні живуть далі, тому на них будують лічильники, приватні дані й фабрики функцій. ```javascript function outer() { let count = 0; return function inner() { count++; console.log(count); }; } const counter = outer(); counter(); // 1 counter(); // 2 ``` **Ключове:** функція запам'ятовує не значення змінної, а посилання на неї, тому стан зберігається між викликами.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Замикання (closure) це функція, яка «пам'ятає» змінні з області, де її було створено, навіть якщо ця область уже завершила виконання.** Інакше кажучи, замикання це функція разом із лексичним оточенням, у якому вона народилася. ## Теорія ### TL;DR - Замикання виникає, коли функція оголошена всередині іншої функції й використовує її змінні. - Зовнішня функція може завершитися, але захоплені змінні не знищуються, поки на них є посилання. - Замикання зберігає **посилання** на змінну, а не її копію, тому стан живе між викликами. - Кожен виклик зовнішньої функції створює **нове незалежне оточення** зі своїми змінними. - Це основний спосіб робити приватні дані, лічильники, фабрики функцій і мемоізацію. - Класична пастка: `var` у циклі разом із `setTimeout` дає одну спільну змінну на всі ітерації. ### Швидкий приклад ```javascript function outer() { let count = 0; // змінна зовнішньої функції function inner() { count++; console.log(count); } return inner; } const counter = outer(); // викликаємо зовнішню функцію counter(); // 1 counter(); // 2 counter(); // 3 ``` Що відбувається: 1. `outer()` виконується і створює змінну `count` та внутрішню функцію `inner`. 2. Повертає `inner`, яка **пам'ятає**, де її було створено. 3. Навіть після завершення `outer()` змінна `count` **не знищується**, бо на неї є посилання з `inner`. Коротко ланцюжок такий: ```text outer() -> створило count -> inner() -> запам'ятало count ``` ### Замикання це функція плюс її оточення Кожне замикання зберігає **лексичне оточення (scope)**, тобто набір змінних, до яких воно має доступ. ```javascript function makeAdder(x) { return function (y) { return x + y; // доступ до "x" із зовнішнього скоупу }; } const add5 = makeAdder(5); console.log(add5(10)); // 15 console.log(add5(7)); // 12 ``` Пояснення: - Функція `makeAdder` створює і повертає нову функцію. - Повернена функція **замикає** значення `x` (у нашому випадку `5`). - Навіть після завершення `makeAdder` змінна `x` залишається «живою». Важливо, що `makeAdder(5)` і `makeAdder(10)` дадуть дві різні функції з двома різними оточеннями, які нічого не знають одна про одну. ### Приватні змінні та стан Замикання це штатний спосіб зробити дані недоступними ззовні. ```javascript function createCounter() { let count = 0; return { increment() { count++; }, decrement() { count--; }, get() { return count; } }; } const counter = createCounter(); counter.increment(); counter.increment(); console.log(counter.get()); // 2 ``` Тут `count` **недоступний ззовні напряму**, але доступний із замкнених функцій (`increment`, `decrement`, `get`). Це і є спосіб створювати **приватні змінні** у JavaScript. ### Замикання зберігає посилання, а не значення Функція пам'ятає **не значення**, а **посилання на змінну** в зовнішньому оточенні. ```javascript function make() { let value = 1; return () => console.log(value++); } const f = make(); f(); // 1 f(); // 2, змінна "value" жива між викликами ``` Саме тому лічильник працює: обидва виклики читають і змінюють ту саму комірку пам'яті. ### Класична помилка з циклом ```javascript for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); } // через 1 секунду: 3, 3, 3 ``` Чому так: - Усі функції замкнули **одну й ту саму змінну** `i`, оголошену через `var` у функціональній області видимості. - Коли спрацьовує таймер, цикл уже завершився і `i` дорівнює `3`. Виправлення через `let`, який створює нову змінну на кожній ітерації: ```javascript for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); } // 0, 1, 2 ``` ### Підсумок і аналогія | Що це | Приклад | | --- | --- | | Функція, яка «пам'ятає» змінні з місця, де її створено | `return function () { console.log(a); }` | | Змінні живуть, навіть якщо зовнішня функція вже завершилася | Так | | Створює приватні дані та стан | Так | | Часто застосовується в колбеках, обробниках подій, модулях | Так | > Замикання це як рюкзак, у якому функція несе з собою всі потрібні змінні з місця, де вона народилася. ### Типові помилки - **Думати, що замикання копіює значення.** Воно тримає посилання на змінну, тож пізніша зміна цієї змінної буде видима всередині. - **Використовувати `var` у циклі з асинхронним колбеком.** Отримаєте останнє значення лічильника на всіх ітераціях. Беріть `let` або обгортайте тіло у функцію. - **Вважати, що кожен виклик зовнішньої функції ділить один стан.** Навпаки: кожен виклик створює окреме оточення, тому два лічильники незалежні. - **Створювати замикання в циклах на тисячі елементів без потреби.** Кожне тримає своє оточення в пам'яті, і якщо посилання не відпустити, дані не зберуться збирачем сміття, а це витік пам'яті. - **Плутати замикання з `this`.** Замикання захоплює змінні, а не `this`: у звичайній функції `this` визначається способом виклику, і лише стрілкова функція бере `this` із зовнішнього оточення.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.