Hoisting

Последнее обновление: 14.09.2023

Hoisting представляет процесс доступа к переменным до их определения. Возможно, данная концепция выглядит немного странно, но она связана с работой компилятора JavaScript. Компиляция кода происходит в два прохода. При первом проходе компилятор получает все объявления переменных, все идентификаторы. При этом никакой код не выполняется, методы не вызываются. При втором проходе собственно происходит выполнение. И даже если переменная определена после непосредственного использования, ошибки не возникнет, так как при первом проходе компилятору уже известны все переменные.

То есть как будто происходит поднятие кода с определением переменных и функций вверх до их непосредственного использования. Поднятие на английский переводится как hoisting, сообственно поэтому данный процесс так и называется.

Переменные var

var-переменные, которые попадают под hoisting, по умолчанию получают значение undefined. Например, возьмем следующий простейший код:

console.log(foo);

Его выполнение вызовет ошибку ReferenceError: foo is not defined

Добавим определение переменной:

console.log(foo);	// undefined
var foo = "Tom";

В этом случае консоль выведет значение "undefined". При первом проходе компилятор узнает про существование переменной foo. Она получает значение undefined. При втором проходе вызывается метод console.log(foo).

Возьмем другой пример:

var c = a * b;
var a = 7;
var b = 3;
console.log(c);	// NaN

Здесь та же ситуация. Переменные a и b используются до опеределения. По умолчанию им присваиваются значения undefined. А если умножить undefined на undefined, то получим Not a Number (NaN).

Все то же самое относится и к использованию функций. Мы можем сначала вызвать функцию, а потом уже ее определить:

display();

function display(){
	console.log("Hello Hoisting");
}

Здесь функция display благополучно отработает, несмотря на то, что она определена после вызова.

Но от этой ситуации надо отличать тот случай, когда функция определяется в виде переменной:

display();

var display = function (){
	console.log("Hello Hoisting");
}

В данном случае мы получим ошибку TypeError: display is not a function. При первом проходе компилятор также получит переменную display и присвоет ей значение undefined. При втором проходе, когда надо будет вызывать функцию, на которую будет ссылаться эта переменная, компилятор увидит, что вызывать то нечего: переменная display пока еще равна undefined. И будет выброшена ошибка.

let-переменные и константы

Процесс hoisting для let-переменных и констант будет отличаться: в отличие от var-переменных константам и let-переменным при хостинге не присваивается начальное значение. Итак, выше мы видели, что если мы используем var-переменные до объявления, то они получают по умолчанию значение undefined. Теперь посмотрим, что будет с let-переменной:

console.log(foo);   // Uncaught ReferenceError: Cannot access 'foo' before initialization
let foo = "Tom";
console.log(foo);   // не будет выполняться

В данном случае в первой строке мы получим ошибку

Uncaught ReferenceError: Cannot access 'foo' before initialization

От этой ситуации следует отличать момент, когда let-переменная объявлена, но не инициализирована:

let foo;            // по умолчанию foo = undefined
console.log(foo);   // undefined
foo = "Tom";
console.log(foo);   // Tom

Неинициализированная let-переменная по умолчанию будет иметь значение undefined, и в данном случае ошибки не будет.

То же самое касается и констант:

console.log(foo);   // Uncaught ReferenceError: Cannot access 'foo' before initialization
const foo = "Tom";
console.log(foo);   // не будет выполняться
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850