Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Лексична область видимості». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Лексична область видимості, це область, яку визначає розташування коду в момент його написання, а не те, звідки функцію викликали.** Простіше кажучи: те, яка функція до яких змінних має доступ, вирішується на етапі оголошення й парсингу, а не на етапі виклику. Тому внутрішня функція бачить змінні всіх функцій, у які вона фізично вкладена, і пошук імені йде вгору по ланцюжку областей видимості (scope chain): `inner` -> `outer` -> `global`. У мовах з динамічною областю видимості все навпаки, там вирішує той, хто викликав функцію. Саме на лексичній області тримаються замикання (closures). ```javascript let x = "global"; function foo() { console.log(x); // "global", бо foo написано в глобальній області } function bar() { let x = "local"; foo(); } bar(); ``` **Ключове:** шукай там, де функцію написано, а не там, звідки її викликали.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Лексична область видимості (lexical scope), це область, яку визначає розташування коду в момент його написання, а не момент виконання.** Те, яка функція до яких змінних має доступ, вирішується на етапі оголошення, коли рушій читає початковий код, а не тоді, коли ви викликаєте функцію. ## Теорія ### TL;DR - Лексична область видимості визначається розташуванням коду в тексті програми. - Рішення про доступ до змінних ухвалюється на етапі оголошення та парсингу, а не під час виклику. - Внутрішня функція бачить змінні всіх функцій, у які вона вкладена, і глобальні змінні. - Пошук імені йде вгору по ланцюжку областей видимості (scope chain): `inner` -> `outer` -> `global`. - У мовах з динамічною областю видимості (Bash, старий Lisp) усе навпаки: вирішує той, хто викликав. - Замикання (closure), це практичний наслідок лексичної області видимості. ### Швидкий приклад ```javascript let a = 10; function outer() { let b = 20; function inner() { let c = 30; console.log(a, b, c); // 10 20 30 } inner(); } outer(); ``` Тут `inner` оголошено **всередині** `outer`, тому вона має доступ і до змінних `outer`, і до глобальних. JavaScript запам'ятав цю структуру **при оголошенні**, а не при виклику. Ланцюжок областей видимості: ```javascript inner -> outer -> global ``` ### Чому саме «лексична» Слово «лексична» (від англ. lexical) означає «та, що стосується тексту програми». Рушій «запам'ятовує» розташування функцій і блоків коду **під час парсингу**, коли читає початковий файл, а не тоді, коли ви викликаєте функції. Практичний наслідок: область видимості будь-якої функції можна визначити, просто подивившись на текст файлу, не запускаючи програму. Саме тому лінтери та IDE вміють підсвічувати недосяжні або затінені змінні ще до запуску. ### Лексична проти динамічної області видимості Деякі мови (наприклад Bash або старий Lisp) використовують **динамічну область видимості**: її визначає **той, хто викликав функцію**. У JavaScript область **лексична**: її визначає **те, де функцію написано**. ```javascript let x = "global"; function foo() { console.log(x); } function bar() { let x = "local"; foo(); // хто викликав foo? bar, але... } bar(); ``` Результат: ```javascript global ``` Тому що `foo` **оголошено** в глобальній області, і JavaScript шукає `x` саме там, а не там, звідки її викликали (`bar`). > Це і є суть лексичної області: «шукай там, де функцію написано, а не там, звідки її викликано». ### Замикання, практичний вияв лексичної області ```javascript function makeCounter() { let count = 0; return function() { count++; console.log(count); }; } const counter = makeCounter(); counter(); // 1 counter(); // 2 ``` Тут внутрішня функція **запам'ятала лексичне оточення** `makeCounter` і навіть після завершення зовнішньої функції все ще «бачить» `count`. Це можливо лише тому, що область видимості **лексична**, а не динамічна: посилання на зовнішнє оточення прошите у функцію при її створенні, тож воно живе стільки, скільки живе сама функція. ### Візуально ```javascript [ Global Scope ] a = 10 | [ outer() Scope ] b = 20 | [ inner() Scope ] c = 30 ``` Функція `inner` завжди «знає», де шукати `a` і `b`, бо це визначено **на етапі оголошення**. ### Підсумок | Поняття | Опис | | --- | --- | | **Лексична область видимості** | Область, у якій функція «бачить» змінні; визначається розташуванням у початковому коді | | **Коли визначається** | Під час написання коду, а не під час виконання | | **Навіщо потрібна** | Щоб рушій знав, які змінні доступні кожній функції | | **Пов'язане з** | Lexical Environment, Scope Chain, Closures | ### Типові помилки - Вважати, що область видимості залежить від місця виклику. Ні, лише від місця оголошення в тексті програми. - Очікувати, що `foo()` з прикладу вище побачить локальний `x` із `bar`. Це поведінка динамічної області, якої в JavaScript немає. - Плутати лексичну область із `this`. У звичайних функціях `this` визначається якраз динамічно, під час виклику; лексично його беруть лише стрілкові функції. - Думати, що після завершення зовнішньої функції її змінні гарантовано зникають. Якщо на них тримає посилання замикання, вони живуть далі. - Плутати лексичну область із hoisting. Hoisting відповідає на питання «коли змінна стає доступною», лексична область, на питання «де її взагалі шукати».Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.