Learn JavaScript

Область видимости (scope) в JavaScript

Область видимости (scope) определяет, где в коде доступны переменные и функции. Понимание scope помогает избегать ошибок вроде “переменная не определена” и случайного перезаписывания значений.

Виды области видимости

В JavaScript чаще всего встречаются:

  • глобальная область видимости - доступна “сверху вниз” по всему файлу (и иногда шире)
  • функциональная область видимости - переменные видны внутри функции
  • блочная область видимости - переменные видны внутри блока { ... } (if, for, while и т.п.)

Глобальная область видимости

Переменные, объявленные на верхнем уровне файла, находятся в глобальной области видимости этого файла/модуля.

const appName = "Demo";

function show() {
  console.log(appName);
}

show(); // "Demo"

Глобальные переменные удобны для констант, но их обычно стараются ограничивать: слишком много глобальных значений усложняют поддержку и повышают риск конфликтов.

Функциональная область видимости

Все, что объявлено внутри функции, доступно только внутри этой функции.

function demo() {
  const value = 123;
  console.log(value);
}

demo();
console.log(value); // ReferenceError

Параметры функции - это тоже переменные внутри функции:

function sum(a, b) {
  return a + b;
}

console.log(sum(2, 3)); // 5

Блочная область видимости: let и const

let и const имеют блочную область видимости, то есть видны только внутри ближайших { ... }.

if (true) {
  const status = "ok";
  let count = 1;
  console.log(status, count);
}

console.log(status); // ReferenceError
console.log(count);  // ReferenceError

То же работает в for:

for (let i = 0; i < 2; i += 1) {
  console.log(i);
}
console.log(i); // ReferenceError

var и функциональная область видимости

var не имеет блочной области видимости - только функциональную. Это часто приводит к неожиданностям, поэтому var обычно избегают.

if (true) {
  var x = 10;
}

console.log(x); // 10

Еще один типичный пример - for с var:

for (var i = 0; i < 3; i += 1) {}

console.log(i); // 3 (переменная "утекла" наружу)

С let такого не происходит.

Lexical scope: переменные берутся из внешних областей

JavaScript использует лексическую (статическую) область видимости: функция “видит” переменные из тех областей, где она была объявлена.

const base = 10;

function addToBase(n) {
  return base + n;
}

console.log(addToBase(5)); // 15

addToBase использует base из внешней области.

Вложенные функции

Внутренняя функция видит переменные внешней:

function outer() {
  const prefix = "id-";

  function inner(n) {
    return prefix + n;
  }

  return inner(7);
}

console.log(outer()); // "id-7"

Это подводит к теме замыканий (closures), которая будет дальше.

Shadowing: переменная перекрывает переменную из внешней области

Если во внутреннем scope объявить переменную с тем же именем, она “перекроет” внешнюю. Это называется shadowing.

const value = "outer";

function demo() {
  const value = "inner";
  console.log(value); // "inner"
}

demo();
console.log(value); // "outer"

Shadowing иногда полезен, но чаще делает код менее очевидным. Обычно лучше избегать одинаковых имен в разных уровнях, если это не оправдано.

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

1) Использование переменной вне области видимости

function demo() {
  const x = 1;
}

demo();
console.log(x); // ReferenceError

2) Случайная “утечка” через var

if (true) {
  var temp = "data";
}

console.log(temp); // "data"

3) Путаница с одинаковыми именами

let count = 0;

function inc() {
  let count = 10;
  count += 1;
  return count;
}

console.log(inc());   // 11
console.log(count);   // 0

Здесь count внутри функции не меняет внешний count, потому что это другая переменная.

Что стоит запомнить

  • Scope определяет, где доступна переменная.
  • let и const - блочный scope, var - функциональный scope.
  • Функции видят переменные из внешних областей (lexical scope).
  • Одинаковые имена во вложенных областях создают shadowing и могут запутывать.