Scope
Scope (область видимости) — контекст, определяющий доступность переменных и функций в конкретной точке кода.
Зачем нужно
Scope предотвращает конфликты имён, защищает данные от случайного изменения и определяет время жизни переменных. Понимание scope — ключ к пониманию замыканий и модульности.
Где используется
- Определение доступности переменных
- Замыкания (closures)
- Модульная система
- Управление памятью (сборка мусора)
Предпосылки
Виды scope
Global scope (глобальная)
// Переменные, объявленные вне функций/блоков
const APP_NAME = 'MyApp'; // доступна везде
// var создаёт свойство window
var globalVar = 'hello';
console.log(window.globalVar); // 'hello' (в браузере)
// let/const НЕ создают свойство window
let globalLet = 'world';
console.log(window.globalLet); // undefined
Function scope (функциональная)
function greet() {
var message = 'Привет'; // доступна только внутри greet
let name = 'Алиса'; // тоже только внутри
console.log(message, name);
}
greet;
// console.log(message); // ReferenceError
// console.log(name); // ReferenceError
Block scope (блочная)
if (true) {
let blockLet = 'видна только в блоке';
const blockConst = 'тоже только в блоке';
var blockVar = 'видна за пределами блока!';
}
// console.log(blockLet); // ReferenceError
// console.log(blockConst); // ReferenceError
console.log(blockVar); // 'видна за пределами блока!' — var игнорирует блоки
// Циклы создают блок
for (let i = 0; i < 3; i++) {
// i доступна только здесь
}
// console.log(i); // ReferenceError
Lexical scope (лексическая область видимости)
// Scope определяется тем, ГДЕ функция ОБЪЯВЛЕНА, а не где вызвана
const outer = 'внешняя';
function outerFn() {
const middle = 'средняя';
function innerFn() {
const inner = 'внутренняя';
console.log(inner); // ОК — своя переменная
console.log(middle); // ОК — из родительского scope
console.log(outer); // ОК — из глобального scope
}
innerFn;
// console.log(inner); // ReferenceError — нет доступа вниз
}
outerFn;
Scope chain (цепочка областей видимости)
const a = 'global';
function first() {
const b = 'first';
function second() {
const c = 'second';
function third() {
const d = 'third';
// Поиск переменной идёт вверх по цепочке:
// third → second → first → global
console.log(d); // 'third' (нашли в своём scope)
console.log(c); // 'second' (поднялись на 1 уровень)
console.log(b); // 'first' (поднялись на 2 уровня)
console.log(a); // 'global' (поднялись на 3 уровня)
}
third;
}
second;
}
first;
Замыкания (Closures)
// Функция «помнит» своё окружение
function createCounter() {
let count = 0; // приватная переменная
return {
increment { return ++count; },
decrement { return --count; },
getCount { return count; }
};
}
const counter = createCounter;
counter.increment; // 1
counter.increment; // 2
counter.decrement; // 1
counter.getCount; // 1
// count недоступен напрямую!
// Практический пример: фабрика функций
function multiply(factor) {
return (number) => number * factor;
}
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15
Частые ошибки
1. var в цикле с замыканием
for (var i = 0; i < 3; i++) {
setTimeout( => console.log(i), 100);
}
// 3, 3, 3 — все ссылаются на одну переменную var
// Решение 1: let
for (let i = 0; i < 3; i++) {
setTimeout( => console.log(i), 100);
}
// 0, 1, 2
// Решение 2: IIFE (до ES6)
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout( => console.log(j), 100);
})(i);
}
// 0, 1, 2
2. Случайные глобальные переменные
function oops() {
mistake = 'Я глобальная!'; // без let/const/var — глобальная!
}
oops;
console.log(window.mistake); // 'Я глобальная!'
// strict mode предотвращает это
'use strict';
function safe() {
// mistake = 'error'; // ReferenceError!
}
3. Shadowing (затенение)
const name = 'Глобальное имя';
function greet() {
const name = 'Локальное имя'; // Затеняет глобальное
console.log(name); // 'Локальное имя'
}
greet;
console.log(name); // 'Глобальное имя'
Практика
- Создай функцию-счётчик с замыканием (increment, decrement, reset)
- Напиши фабрику функций, которая создаёт приветствие на разных языках
- Покажи разницу var/let в цикле с setTimeout
- Создай модуль с приватными переменными через IIFE
Связанные темы
Ресурсы
🎓 Источник: 👶 JavaScript для начинающих 6. Функции и область видимости
- 📅 2021-10-02 · YouTube
- Тезисы: scope = окружение для разрешения идентификаторов. Лексический скоуп = по тексту программы. Каждая функция создаёт новый scope. Глобальный, function, block
⚡ Источник: JavaScript and the Lexical Environment · AsForJS
- 📅 2023-05-12 · YouTube
- Тезисы:
- В спеке нет "scope chain", есть
Environment Recordсо ссылкой[[OuterEnv]] - При обращении к идентификатору движок резолвит цепочку Environment Records снизу вверх до глобального
- Lexical Environment ≠ Variable Environment (последний только для
var)
- В спеке нет "scope chain", есть
⚡ Источник: ⎡03⎦ Performance of JavaScript variables identifiers · AsForJS
- 📅 2023-06-20 · YouTube
- Тезисы:
- Доступ к переменной из внешнего scope медленнее, чем к локальной (нужно идти по
[[OuterEnv]]) - V8 оптимизирует через context slot indexes, но через несколько уровней вложенности замедление заметно
withиevalполностью убивают оптимизации scope
- Доступ к переменной из внешнего scope медленнее, чем к локальной (нужно идти по