Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке контекст виконання (execution context)?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Execution context (контекст виконання)** - внутрішнє середовище, яке JavaScript-рушій створює для кожного фрагмента коду. В ньому містяться змінні, ланцюжок областей видимості та прив'язка `this`. ```javascript var x = 1; function show() { var y = 2; console.log(x); // 1 - знайдено через ланцюжок до глобального контексту console.log(y); // 2 - локальна змінна цього контексту } show(); ``` **Ключове:** кожен виклик функції отримує власний контекст. Контексти накопичуються на стеку викликів і знімаються, коли функція повертає результат.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Execution context (контекст виконання)** - внутрішнє середовище, яке JavaScript-рушій створює щоразу при запуску коду. В ньому зберігаються змінні, ланцюжок областей видимості та прив'язка `this`. ## Теорія ### TL;DR - Аналогія: кожен виклик функції отримує власне робоче місце зі своїм набором змінних, довідником (ланцюжок областей видимості) і тегом власника (`this`). - Три типи: глобальний (один на всю програму), функціональний (окремий на кожен виклик), eval (краще не чіпати). - Дві фази: фаза створення (тут відбувається hoisting) і фаза виконання (код виконується рядок за рядком). - `this` змінюється залежно від контексту. Стрілкові функції власного контексту не створюють, вони беруть `this` від батька. - Отримав несподіваний `undefined` або неправильний `this` - трасуй стек контекстів. ### Швидкий приклад ```javascript console.log(a); // undefined - підняте у фазі створення глобального контексту var a = 2; console.log(a); // 2 function greet() { // Новий функціональний контекст кладеться на стек викликів console.log(b); // undefined - підняте всередині цього контексту var b = 'hello'; console.log(b); // 'hello' } greet(); ``` Глобальний контекст піднімає `a` до `undefined` під час фази створення. Коли викликається `greet()`, на стек кладеться новий функціональний контекст зі своїм `b`. Він бачить `a` через ланцюжок областей видимості, але `b` існує тільки всередині нього. ### Дві фази кожного контексту Рушій виконує кожен контекст у два кроки. **Фаза створення**: змінні з `var` підіймаються і отримують `undefined`. Оголошення функцій підіймаються повністю. Будується ланцюжок областей видимості. Визначається `this`. **Фаза виконання**: код іде рядок за рядком, змінним присвоюються значення, функції викликаються. Саме тому звернення до `x` перед `var x = 1` дає `undefined`, а не ReferenceError. Змінна вже зареєстрована в контексті під час фази створення, просто без значення. ### Чим execution context відрізняється від scope [Область видимості (scope)](/questions/what-is-scope) визначає, які змінні бачить конкретна ділянка коду. Execution context - це повний пакет: об'єкт змінних (всі `var` і оголошення функцій), ланцюжок областей видимості до зовнішніх контекстів, значення `this` і позиція в стеку викликів. Scope - лише один компонент контексту. ### `this` у різних контекстах При виклику методу `this` - це об'єкт перед крапкою. При звичайному виклику функції `this` - глобальний об'єкт (або `undefined` в strict mode). Стрілкові функції власного контексту не створюють. Вони захоплюють `this` з оточуючого контексту в момент оголошення. ```javascript const obj = { name: 'World', hello: function() { console.log(this.name); // 'World' - контекст методу, this = obj setTimeout(function() { console.log(this.name); // undefined - новий глобальний контекст }, 0); setTimeout(() => { console.log(this.name); // 'World' - стрілка успадковує this з hello }, 0); } }; obj.hello(); ``` Звичайна функція в `setTimeout` отримує власний глобальний контекст. Стрілкова - взагалі не створює нового. ### Типові помилки **1. Очікувати що `var` обмежений блоком** ```javascript if (true) { var score = 100; } console.log(score); // 100 - var належить функціональному або глобальному контексту ``` `var` прив'язаний до свого execution context, а не до блоку `{}`. Для блокової видимості використовуй `let` або `const`. **2. Втрата `this` у callback** ```javascript class Timer { constructor() { this.count = 0; } start() { setInterval(function() { this.count++; // TypeError: this is undefined в strict mode }, 1000); } } ``` Це, мабуть, найчастіше питання про `this` на junior-співбесідах з JavaScript. Callback виконується в новому контексті, де `this` не є екземпляром Timer. Рішення: стрілкова функція в callback або `.bind(this)` у конструкторі. **3. Очікувати що eval ізольований** ```javascript var secret = 'admin'; eval('console.log(secret)'); // 'admin' - успадковує зовнішній scope ``` `eval` створює власний контекст, але успадковує зовнішню область видимості. Плюс вимикає частину оптимізацій V8. Для ізольованого динамічного коду використовуй `new Function()`. **4. Забути що strict mode змінює `this`** Без strict mode `this` у звичайному виклику функції - глобальний об'єкт. У strict mode - `undefined`. Додавання `"use strict"` до існуючого файлу може непомітно зламати код, який розраховував на `this === window`. ### Де це зустрічається на практиці - **React function components**: кожен рендер створює новий функціональний контекст. [Хуки на кшталт `useState`](/questions/what-is-usestate) захоплюють стан для цього контексту через замикання (closure). - **Express route handlers**: кожен запит виконується у власному контексті. `req` і `res` локальні для цього виклику. - **Callbacks у `setTimeout`**: виконуються в новому глобальному контексті. Стрілкові функції - стандартне рішення для збереження `this`. - **Node.js модулі**: кожен файл запускається у wrapper-функції зі своїм контекстом, тому `var` на верхньому рівні не потрапляє в глобальний об'єкт. ### Follow-up питання **Q:** Яка різниця між фазою створення і фазою виконання? **A:** У фазі створення `var`-змінні підіймаються до `undefined`, оголошення функцій підіймаються повністю, визначається `this`. У фазі виконання код іде рядок за рядком і змінним присвоюються значення. **Q:** Як ланцюжок областей видимості працює між контекстами? **A:** Кожен контекст зберігає посилання на VariableEnvironment зовнішнього контексту. Якщо змінна не знайдена локально, рушій іде вверх по ланцюжку до глобального контексту. **Q:** Яка різниця між call stack і стеком execution context? **A:** Це один механізм з двох точок зору. Call stack відстежує які функції чекають повернення. Стек execution context описує повний стан кожного фрейму: змінні, ланцюжок областей видимості і `this`. **Q:** Як [стрілкові функції](/questions/arrow-functions-vs-regular-functions) впливають на execution context? **A:** Вони не створюють власного контексту. `this` і scope успадковуються від лексичного батька в момент оголошення функції. **Q:** Чому `this` є `undefined` у Node.js strict mode всередині функції? **A:** Strict mode не прив'язує глобальний об'єкт до `this` при звичайному виклику функції. Значення залишається `undefined` поки не буде встановлене явно через `.call()`, `.apply()` або `.bind()`. ## Приклади ### Hoisting у глобальному і функціональному контекстах ```javascript // Глобальний контекст - 'name' піднімається до undefined у фазі створення console.log(name); // undefined var name = 'Alice'; console.log(name); // 'Alice' function getGreeting() { // Функціональний контекст - власна фаза створення console.log(greeting); // undefined (підняте в цьому контексті) var greeting = 'Hello, ' + name; // 'name' знайдено через ланцюжок console.log(greeting); // 'Hello, Alice' } getGreeting(); ``` `name` доступна всередині `getGreeting` через ланцюжок областей видимості до глобального контексту. `greeting` існує тільки у власному контексті функції і не видима зовні. ### `this` у React-компоненті ```javascript function UserProfile({ userId }) { // Новий execution context при кожному рендері const [user, setUser] = React.useState(null); React.useEffect(() => { // Стрілкова функція - власного контексту немає, успадковує від UserProfile fetch(`/api/user/${userId}`) // userId з контексту цього рендеру .then(res => res.json()) .then(setUser); }, [userId]); return user ? <div>{user.name}</div> : <div>Loading...</div>; } ``` Кожен рендер `UserProfile` створює новий execution context. Стрілкова функція в `useEffect` замикає `userId` з конкретного рендеру. Якщо не вказати `userId` в масиві залежностей, ефект триматиме застаріле значення зі старого контексту - це класичний баг із stale closure.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.