Область видимости, hoisting и this
Где живут переменные и почему метод «теряет» this.
Обновлено
Что такое область видимости
Область видимости (scope) = часть кода, где переменная существует и может использоваться. За её пределами переменной нет.
function order() {
const total = 100 // существует только внутри order()
}
console.log(total) // ✗ ReferenceErrorЗачем это нужно: без областей видимости все переменные были бы глобальными и мешали бы друг другу (две функции, у каждой свой i).
Виды областей видимости
| Вид | Создаётся | Содержит |
|---|---|---|
| глобальная | файлом / страницей | всё, что объявлено вне функций (в ES-модулях — глобально только для модуля) |
| функции | каждой функцией | её параметры и переменные (включая var) |
| блока | любыми { }: if, for, while, просто {} |
let, const, class |
const app = 'learn' // глобальная (для модуля)
function render() {
const title = 'Функции' // область функции
if (title) {
const upper = title.toUpperCase() // область блока
}
upper // ✗ уже не существует
}Лексическая область и цепочка областей
Когда вы используете переменную, JS ищет её в текущей области, затем в родительской, затем в её родителе… до глобальной. Эта цепочка называется scope chain.
Важно: родитель определяется тем, где функция написана в коде (лексически), а не тем, откуда она вызвана.
const x = 'global'
function outer() {
const x = 'outer'
return function inner() { return x } // inner написана внутри outer → видит x из outer
}
outer()() // 'outer'Отсюда берётся замыкание — см. Функции.
Что такое hoisting
Hoisting («поднятие») = перед выполнением кода JS проходит по области видимости и регистрирует все объявления. Эффект: некоторые вещи можно использовать до строки, где они написаны.
Каждый вид объявления поднимается по-своему:
| Объявление | Поднимается? | Что получите, если использовать до его строки |
|---|---|---|
function f() {} |
да, вместе с телом | функция работает нормально |
var x = 1 |
да, но без значения | undefined |
let / const |
да, но заблокировано | ReferenceError (TDZ) |
class A {} |
да, но заблокировано | ReferenceError |
const f = () => {} |
как любой const |
ReferenceError |
sayHi() // ✓ 'Привет' — объявление функции поднято целиком
function sayHi() { return 'Привет' }
console.log(a) // undefined — var поднят, значение появится только на его строке
var a = 1
console.log(b) // ✗ ReferenceError
const b = 2TDZ — временная мёртвая зона
Между началом области и строкой, где объявлен let/const, переменная находится в «мёртвой зоне» (TDZ): JS знает, что она есть, но не даёт её использовать. Это хорошо — ловятся баги, которые var прятал за undefined.
Что это значит на практике: можно организовать файл с главной функцией сверху и вспомогательными (объявления function) снизу — это работает благодаря hoisting.
Что такое this
this — особая переменная внутри функций, которая указывает на объект, в контексте которого выполняется функция. В отличие от остальных переменных, значение this зависит не от того, где функция написана, а от того, как она вызвана.
Правила в порядке приоритета
| Как вызвать | this — это |
Пример |
|---|---|---|
new Fn() |
новый созданный объект | new User('Ana') |
fn.call(obj), fn.apply(obj), fn.bind(obj) |
obj, задан явно |
greet.call(user) |
obj.method() |
объект перед точкой | user.greet() |
просто fn() |
undefined (в модулях / strict mode) |
const g = user.greet; g() |
| стрелочная функция | this того места, где она написана, — своего this нет |
() => this.name |
const user = {
name: 'Ana',
greet() { return `Привет, ${this.name}` },
}
user.greet() // 'Привет, Ana' — this = user
const greet = user.greet
greet() // ✗ this равен undefined — метод «оторвали» от объекта
const bound = user.greet.bind(user)
bound() // ✓ 'Привет, Ana'Почему стрелочные функции «чинят» колбэки
const timer = {
seconds: 0,
start() {
setInterval(function () { this.seconds++ }, 1000) // ✗ this — не timer
setInterval(() => { this.seconds++ }, 1000) // ✓ стрелочная берёт this из start()
},
}Когда this важен на практике
- В современном React (функции + хуки) — почти никогда.
- В классах (свои ошибки, SDK), в классических обработчиках событий и в старом коде.
- Правило: методы → обычные функции; колбэки внутри них → стрелочные; передаёте метод как колбэк →
bindили() => obj.method().
Коротко
- Область видимости = где живёт переменная: глобальная, функции, блока. Поиск поднимается по цепочке областей — по месту, где написан код.
- Hoisting = объявления регистрируются до выполнения; функции работают до определения,
let/constдают ошибку (TDZ). thisзависит от того, как вызвана функция; у стрелочных функций нет своегоthis.