Начнем с того, что вам нужно обязательно ознакомиться с предыдущим уроком.
Поднятие(hoisting) - Так называют некий механизм который относится только к объявлению функций и переменных. Если вы читали предыдущий урок, то вы уже знаете, что такое hoisting
, просто вам об этом никто не сказал).
Мы уже знаем, что такое фаза создания, так вот hoisting
это как раз о том, когда переменные помещаются в лексическое окружение. То есть объявления переменных и функций попадают в память в процессе компиляции.
И сразу скажу, то, что мы говорим про переменные, то касается и функций. Функции в JavaScript являются объектами первого класса. То есть это значит, что они могут быть сохранены в переменных, переданы в качестве аргументов и возвращены из других функций. Они также могут быть перезаписаны так же, как и обычные переменные.
function a () {
return 'a'
}
console.log(a); // тут мы получим тело функции
Или очень понятый пример:
function a () {
return 'a';
}
a = undefined;
console.log(a); // undefined
Мы перезаписали функцию a
и теперь там undefined
. Все как и с обычной переменной.
Мы знаем, что можем использовать обычную declaration
функцию до ее объявления.
После прошлого урока теперь вам не сложно догадаться, почему так происходит.
// мы вызвали функцию здесь
console.log(gintama("Кагура")); // Мой любимый персонаж в гинтаме это Кагура
// а здесь мы функцию создали
function gintama(name) {
console.log(`Мой любимый персонаж в гинтаме это ${name}`);
}
Вы все правильно подумали! Потому что во время фазы создания наша функция поместилась в глобальное лексическое окружение. И во время выполнения когда мы наткнулись на вызов функции, мы ее выполняем, так как она есть в нашем лексическом окружении.
Так вот то, что мы можем так сделать и называют hoisting
.
Все очень просто:
hi(); // ReferenceError: Cannot access 'hi' before initialization
let hi = function() {
console.log('Привет красавчик:з');
};
Знакомая ошибка не правда ли? И вы снова все правильно подумали, молодец! Ну конечно же все потому, что тело функции мы присваиваем переменной и происходит это уже во время выполнения. Поэтому в примере выше, наше функциональное выражение ведет себя точно так же как и обычная переменная let
.
Конечно же тоже касается и стрелочной функции.
hi(); // ReferenceError: Cannot access 'hi' before initialization
let hi = () => {
console.log('Привет красавчик:з');
};
Мы можем поменять их на var
.
hi(); // TypeError: hi is not a function
var hi = () => {
console.log('Привет красавчик:з');
};
Но тут лишь поменяется наша ошибка. Нам скажут, что наша переменная не является функцией и конечно hi
не функция. На момент вызова в лексическом окружении в переменной hi
находится undefined
.
Разберем пару примеров с разными переменными, лишним не будет.
// Здесь уже существует переменная var name.
console.log(name); // Здесь будет undefined, оно подставилось во время фазы создания.
var name = 'Dima' // присвоим нашу строку здесь
console.log(name); // Dima - здесь мы уже видим нашу строку
Пример 2
var x = 1; // Инициализируем x
console.log(x + " " + y); // '1 undefined'
var y = 2;
Тоже самое что и:
var x;
var y;
x = 1;
console.log(x + " " + y); // '1 undefined'
y = 2;
Пример 3
Распространенный пример с функцией:
var a = 'Привет';
function b() {
// вот здесь условие не сработает, но переменные var все равно будет в лексическом окружении функции
if(false) {
var a = 'Дима'; // это условие не сработает, поэтому в Лекс Окр переменная a останется с undefined
} else {
var b = 'Сашенька ювелир'; // это условие сработает и присваивание произойдет
}
console.log(a); // undefined
console.log(b); // Сашенька ювелир
}
b();
Код выше идентичен этому коду. Тут более понятно, почему это называют поднятием.
var a = 'Привет';
function b() {
var a; // поэтому это и называется поднятие
var b; // наши переменные будто бы поднялись на самый верх
if(false) {
a = 'Дима';
} else {
b = 'Сашенька ювелир';
}
console.log(a); // undefined
console.log(b); // Сашенька ювелир
}
b();
С переменной let
все намного логичнее:
let x = 1;
console.log(x + " " + y);
let y = 2;
// будет ошибка
С переменной let
нам нужно обязательно сначала ее объявить, а уже потом использовать. Вспоминаем про TDZ
.
let x; // любой пустой переменной присваивается undefined
console.log(x); // undefined
x = 1;
console.log(x); // 1
Пример 2
Пример с функцией как и с var
:
let a = 'Привет';
function b() {
if(false) {
// let не может уйти за рамки блока
let a = 'Дима';
} else {
let b = 'Сашенька ювелир';
}
// ищет переменную в родительском области видимости это переменная a из глобальной области
console.log(a); // Привет
// ищет переменную выше, и находит нашу функцию b
console.log(b); // здесь будет сама функция b
}
b();
А вот что за поиск такой в родительской области видимости и что это за области такие мы уже узнаем в следующем уроке.
Обновлено 20.03.2023