Когда я готовился к собеседованию, я наткнулся на вопрос собеседования, в котором меня просили дать определение подъема. Многие сайты определяют подъем как «стандартное поведение JavaScript, заключающееся в перемещении объявлений наверх». Я мог бы просто процитировать этот ответ для интервью, но затем я начал спрашивать себя: что на самом деле означает «перемещение деклараций наверх»? Что насчет JavaScript, вызывающего подъем и когда это происходит на самом деле? Как изучение этой концепции поможет мне стать лучшим инженером?

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

На этапе компиляции JavaScript создает кучу памяти для всех объявлений переменных и функций, которые находятся в контексте его выполнения. JavaScript фактически не сохраняет значение или тип в своей памяти. Место просто выделено для объявления. Вот что такое подъем, когда переменные и объявления функций «поднимаются» или «перемещаются наверх» контекста выполнения. По сути, это происходит, когда в текущем контексте выполнения создается пространство для объявлений переменных и функций. (Имейте в виду, что для переменных let механизм JavaScript назначает пространство для хранения, но не инициализирует переменные значением undefined. Для переменных var JavaScript инициализирует переменные значением undefined.)

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

Подумайте, как движок JavaScript будет запускать этот код:

var one = 1;
function addTwo(num) {
	return num + 2;
}
var sum = addTwo(one);
console.log(sum);

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

one: undefined
addTwo: function(){}...
sum: undefined

Затем движок JavaScript запускал фазу выполнения и запускал код построчно. В конечном итоге вызов функции addTwo, создание функционального контекста выполнения и выполнение собственной фазы компиляции и выполнения до тех пор, пока addTwo (one) не станет значением выражения вызова, которое будет равно 3.

Рассмотрим другой пример:

function addTwo() {
	num = 4;
	return num + 2;
	var num;
}
var sum = addTwo();
console.log(sum);

На первый взгляд вы можете предположить, что это приведет к ошибке console.log. Но из-за подъема он возвращается как 6. Объявление num поднимается в верхнюю часть контекста выполнения addTwo на этапе компиляции и инициализируется как undefined. И во время фазы выполнения num инициализируется до 4.

Давайте еще раз рассмотрим этот пример, но вместо этого объявим с помощью let:

function addTwo() {
	num = 4;
	return num + 2;
	let num;
}
let sum = addTwo();
console.log(sum);

Хотя подъем выполняется для переменных let на этапе компиляции, когда механизм JavaScript назначает память для этих переменных. Механизм JavaScript не инициализирует эти переменные как undefined. Эти переменные будут находиться во временной мертвой зоне, пока не будут определены. Таким образом, в приведенном выше примере вы получите сообщение об ошибке cannot access 'num' before initialization вместо того, чтобы вернуть 6.

Лучше понимая, что происходит внутри JavaScript, вы сможете лучше устранять ошибки и понимать, как выполняется ваш код. Вы также лучше поймете разницу между ошибками времени компиляции и ошибками времени выполнения (выполнения). Мое объяснение определенно было упрощенной версией того, что происходит с JavaScript. Но по мере того, как я продолжаю расти как разработчик, я буду продолжать копаться, пытаясь демистифицировать темы. Если у вас есть предложения по редактированию моего сообщения, свяжитесь с нами, потому что я всегда стараюсь расти и учиться!

Справочные материалы:

Статьи:

Видео: