Всплытие переменных и функций
Hoisting
Поднятие или hoisting — это механизм в JavaScript, в котором переменные и объявления функций, передвигаются вверх своей области видимости перед тем, как код будет выполнен.
Ещё до выполнения кода интерпретатор JavaScript загружает в память функции, объявленные как function declaration
, а также объявления переменных и функций function expression
, но не их значения.
В результате функцию function declaration
можно вызвать ещё до того, как до неё дойдёт построчное выполнение кода.
note
На этапе создания, движок JavaScript просматривает код и, как только он видит ключевое слово var
или ключевое слово function
, он выделяет некоторую память для них.
1add(3, 4); // 723function add(a, b) {4 return a + b;5}678// Функции function expression при таких условиях работать не будут:910multiply(3, 4); // TypeError1112var multiply = function(a, b) {13 return a * b;14}1516// Значение объявленной через var переменной переменной или function expression до того как интерпретатор дойдёт до её кода, равно undefined.
👀 Рассмотрим следующий пример:
1var myname = "John";2function fn() {3 console.log(myname);4 var myname = "Tom";5 console.log(myname);6}78fn();910// undefined11// Tom
Объяснение 🎓
Объявленная через var
переменная myname
всплывает. До того, как выполнение кода дойдёт до неё, значение переменной равно undefined
.
В первую очередь интерпретатор ищет переменную внутри области видимости функции, находит её и дальше уже не ищет.
Поэтому первый результат выведет undefined
- внутри кода функции переменная уже объявлена, но ещё не инициализирована.
Инициализация - присвоение переменной значения.
Поднятие функций
Одним из преимуществ JavaScript является помещение объявления функций в память, перед выполнением любого сегмента кода. Объявления функций поднимаются, но они идут на самый верх, поэтому они будут находиться над всеми объявлениями переменных. Это позволяет нам использовать функцию до того, как мы объявим её в своем коде. Например:
1sayHello("Alex");2function sayHello(name) {3 console.log("Hello, " + name);4}
Undefined vs ReferenceError
В JavaScript, необъявленной переменной при выполнении кода назначается значение undefined
, а так же и тип undefined
.
1console.log(typeof variable); // Выводит: undefined23console.log(variable); // Выводит: ReferenceError: variable is not defined
В JavaScript, ReferenceError
появляется при попытке доступа к предварительно необъявленной переменной.
Важно
JavaScript непреклонно сначала объявляет, а уже затем инициализирует наши переменные.
Поднятие const, let и var
var - это традиционный способ объявления переменных в JavaScript.
ES6 (ECMAScript 6) представил два новых способа объявления переменных:
const
и let
, и, как правило, они рекомендуются во избежание неожиданных осложнений при подъеме.
Ключевое слово var
var
имеет область действия функции;- объявления
var
поднимаются, но не инициализируются.
1console.log(name); // undefined2var name = "Alex";
Код выше, из-за поднятия эквивалентен приведенному коду ниже.
1var name;2console.log(name); // undefined3name = "Alex";
Ключевые слова const / let
const
и let
имеют область видимости блока.
Важно
Мы должны помнить, что концепция поднятия не является буквальным процессом (т. е. сами объявления не перемещаются в начало файла - это просто процесс компилятора JavaScript, который сначала читает их, чтобы освободить для них место в памяти).
Разница между объявлениями var
/ function
и объявлениями let
/ const
заключается в инициализации.
Первые инициализируются с неопределенным значением undefined
.
Однако вторые, лексически объявленные переменные, остаются не инициализированными.
Это означает, что ReferenceError
выбрасывается при попытке доступа к ним.
Они будут инициализированы только после того, как операторы let
/ const
будут определены. Всё что до, называется временной мертвой зоной.
1{2 console.log ( x )3 // [ временная мёртвая зона ]4 let x = 10 // Объявленные с помощью let, будут недоступны до тех пор, пока выполнение кода не дойдёт до места фактического объявления переменной5}
Временная мертвая зона
Это не синтаксическое местоположение, а время между созданием переменной (области) и инициализацией. Ссылка на переменную в коде над объявлением не является ошибкой, если этот код не выполняется (например, тело функции или просто мертвый код), но ошибка будет выдана, если мы запросим доступ к переменной до её инициализации.
Экземпляры var
и let
могут быть инициализированы без значения, в то время как const
выдаст ошибку ReferenceError
,
и если попытаться объявить её без одновременного присвоения ей значения.
Так что const myName = 'Alex'
будет работать, но const myName; myName = 'Alex';
не будет.
С помощью var
можно использовать значение до того, как оно будет присвоено, и оно вернет undefined
.
Если сделать, то же самое с let
- будет ReferenceError
.
1console.log(a); // undefined2var a = 1;34console.log(b); // ReferenceError: Cannot access 'b' before initialization5let b = 2;67console.log(c); // ReferenceError: Cannot access 'c' before initialization8const c = 5;
Если создать переменную с помощью var
на верхнем уровне (глобальный уровень), создастся свойство для глобального объекта; в случае с браузером - это объект window
.
1var myName = 'Alex';23console.log(window.myName) // Alex
Если попробовать повторить тоже самое с let
1let newName = 'Alex';2console.log(window.myName) // undefined3// myName не будет доступно в глобальном объекте window - следовательно, нельзя использовать window.newName в качестве ссылки на 'Alex' ,
Важно
let не создает свойств в глобальном объекте.
Следствием блочной области видимости переменных, объявленных с помощью директивы let
, является замыкание значения переменной цикла на каждой итерации блока for:
1for ( let i of [ 1, 2, 3, 4, 5 ] ) {2 setTimeout ( () => console.log ( i ), 1000 * i )3}
Обратите внимание, что отсутствие явных фигурных скобок не меняет принцип поведения переменных, объявленных с помощью директивы `let
var
1var res = []23for ( var item of [ "alpha", "sigma", "omega" ] )4 res.push ( () => console.log ( item ) )56res[0]() // omega7res[1]() // omega8res[2]() // omega
let
1var res = []23for ( let item of [ "alpha", "sigma", "omega" ] )4 res.push ( () => console.log ( item ) )56res[0]() // alpha7res[1]() // sigma8res[2]() // omega
Самостоятельная работа 👩💻👨💻
Задание :one:
Поверните данную строку в правильном направлении, периодически удаляя одну букву с конца строки и прикрепляя ее к передней части.
1function rotateString(str){23}4rotateString("cat");56/**7 * Output:8 * cat9 * tca10 * atc11 * cat12 */
подсказка: используйте substring()
Задание :two:
Напишите функцию, чтобы найти наиболее частый элемент в заданном массиве.
1function mostFrequent(){2 //3}4 mostFrequent([3, 'c', 'c', 'c', 2, 3, 'c', 3, 'c', 2, 4, 9, 3]); // c встречается 5 раз
Задание :three:
Напишите функцию для поиска и удаления повторяющихся значений в массиве. Функция должна возвращать массив повторяющихся значений.
1function removeDuplicates(){}234removeDuplicates([3, 'a', 'a', 'a', 2, 3, 'a', 3, 'a', 2, 4, 9, 3]) // [3, 'a', 2, 4, 9]