JS Intermediate

Часть пути: DOM Basics

Область видимости и замыкания

Понять, где переменные доступны (global, function, block scope), и как вложенные функции «запоминают» внешние переменные (closures).

Средний (Intermediate)

Кратко

Область видимости (scope) определяет, где переменная доступна. Global scope — доступна везде. Function scope — только внутри функции. Block scope (let/const) — только внутри { }. Lexical scope — вложенная функция видит переменные внешней функции. Closure — когда функция «запоминает» внешние переменные даже после завершения внешней функции.


Подробно

Что такое область видимости?

Область видимости (scope) — это контекст, в котором переменная объявлена и доступна. JavaScript ищет переменные «сверху вниз» по цепочке областей видимости.

Три типа scope:

  • Global scope:

const name = 'Анна'; // доступна везде

  • Function scope:

function greet() { const message = 'Привет'; // только внутри greet } // message недоступна здесь

  • Block scope:

if (true) { let x = 10; // только внутри if const y = 20; // только внутри if } // x и y недоступны здесь

var vs let/const:

  • var создаёт function scope (вне блоков!)
  • let/const создают block scope

Lexical scope: Вложенная функция видит переменные ВСЕХ внешних функций:

function outer() { const a = 1; function inner() { const b = 2; console.log(a + b); // 3 — видит 'a' из outer } inner(); }

Замыкание (Closure): Функция + ссылка на внешние переменные.

function createCounter() { let count = 0; return function() { count++; return count; }; }

const counter = createCounter(); counter(); // 1 counter(); // 2 counter(); // 3

Счётчик count «замкнут» внутри функции. Даже после завершения createCounter, возвращённая функция помнит count.

Цепочка областей видимости (Scope Chain): Когда JavaScript ищет переменную, он поднимается по вложенности: inner → outer → global. Если не нашёл — ReferenceError.


Ментальная модель

Область видимости — как комната в доме. Вы видите то, что в вашей комнате (local scope) и то, что в коридоре (outer scope). Но не видите то, что в другой комнате (другая function scope). Closure — как комната с окном: даже когда вы уходите из коридора, вы всё ещё видите через окно то, что там было.


Примеры

**Самый простой:**

javascript
const global = 'я глобальная';

function test() {
  const local = 'я локальная';
  console.log(global); // видит глобальную
  console.log(local);  // видит локальную
}

test();
console.log(global); // OK
// console.log(local); // ReferenceError!

Global scope доступна везде. Function scope — только внутри функции.

**Простой:**

javascript
function createMultiplier(factor) {
  return function(number) {
    return number * factor; // factor «замкнута»
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

Каждая функция createMultiplier создаёт своё замыкание со своим factor.

**Реальный пример:**

javascript
function createBankAccount(initialBalance) {
  let balance = initialBalance;

  return {
    deposit(amount) {
      balance += amount;
      return balance;
    },
    withdraw(amount) {
      if (amount > balance) return 'Недостаточно средств';
      balance -= amount;
      return balance;
    },
    getBalance() {
      return balance;
    }
  };
}

const account = createBankAccount(1000);
console.log(account.deposit(500));    // 1500
console.log(account.withdraw(200));   // 1300
console.log(account.getBalance());    // 1300
// account.balance — undefined (приватная переменная!)

Balance защищён замыканием — его нельзя изменить напрямую.


Частые ошибки

**Неправильно:** Путать var и let/const в циклах

**Почему:** var создаёт одну переменную на всю функцию. Все итерации цикла видят одну и ту же переменную.

**Правильно:** Используйте let в циклах — каждая итерация получает свою переменную.

**Неправильно:** Думать, что closure — это копия переменной

**Почему:** Closure хранит ССЫЛКУ на переменную, а не копию. Изменения видны.

**Правильно:** Closure — функция + ссылка на внешние переменные.

**Неправильно:** Забывать, что цикл с var создаёт одну переменную

**Почему:** for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i)); } выведет 3, 3, 3.

**Правильно:** Используйте let: for (let i = 0...) — каждая итерация получает свою i.


Важно запомнить

  • Global scope — доступна везде
  • Function scope — только внутри функции
  • Block scope (let/const) — только внутри { }
  • Lexical scope — вложенная функция видит внешние переменные
  • Closure — функция + ссылка на внешние переменные
  • var не имеет block scope!

Связь

Назад: Вы знаете функции и переменные (J0-J26) — теперь вы понимаете, где они «живут».

Вперёд: Следующий урок (JI2) — глобальный объект window и его свойства.


Читайте дальше

Материалы курса JS Intermediate — PROlab Academy.