HTML / CSSJavaScriptNode jsПаттерны проектированияПрактические

Всплытие переменных и функций

hoisting
:pencil:Hoisting

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

Ещё до выполнения кода интерпретатор JavaScript загружает в память функции, объявленные как function declaration, а также объявления переменных и функций function expression, но не их значения. В результате функцию function declaration можно вызвать ещё до того, как до неё дойдёт построчное выполнение кода.

ℹ️note

На этапе создания, движок JavaScript просматривает код и, как только он видит ключевое слово var или ключевое слово function, он выделяет некоторую память для них.

1add(3, 4); // 7
2
3function add(a, b) {
4 return a + b;
5}
6
7
8// Функции function expression при таких условиях работать не будут:
9
10multiply(3, 4); // TypeError
11
12var multiply = function(a, b) {
13 return a * b;
14}
15
16// Значение объявленной через var переменной переменной или function expression до того как интерпретатор дойдёт до её кода, равно undefined.

👀 Рассмотрим следующий пример:

1var myname = "John";
2function fn() {
3 console.log(myname);
4 var myname = "Tom";
5 console.log(myname);
6}
7
8fn();
9
10// undefined
11// 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); // Выводит: undefined
2
3console.log(variable); // Выводит: ReferenceError: variable is not defined

В JavaScript, ReferenceError появляется при попытке доступа к предварительно необъявленной переменной.

❗️Важно

JavaScript непреклонно сначала объявляет, а уже затем инициализирует наши переменные.

Поднятие const, let и var

var - это традиционный способ объявления переменных в JavaScript.

ES6 (ECMAScript 6) представил два новых способа объявления переменных: const и let, и, как правило, они рекомендуются во избежание неожиданных осложнений при подъеме.

scope-hoisting

Ключевое слово var

  • var имеет область действия функции;
  • объявления var поднимаются, но не инициализируются.
1console.log(name); // undefined
2var name = "Alex";

Код выше, из-за поднятия эквивалентен приведенному коду ниже.

1var name;
2console.log(name); // undefined
3name = "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}
:pencil:Временная мертвая зона

Это не синтаксическое местоположение, а время между созданием переменной (области) и инициализацией. Ссылка на переменную в коде над объявлением не является ошибкой, если этот код не выполняется (например, тело функции или просто мертвый код), но ошибка будет выдана, если мы запросим доступ к переменной до её инициализации.

Экземпляры var и let могут быть инициализированы без значения, в то время как const выдаст ошибку ReferenceError, и если попытаться объявить её без одновременного присвоения ей значения.

Так что const myName = 'Alex' будет работать, но const myName; myName = 'Alex'; не будет.

С помощью var можно использовать значение до того, как оно будет присвоено, и оно вернет undefined. Если сделать, то же самое с let - будет ReferenceError.

1console.log(a); // undefined
2var a = 1;
3
4console.log(b); // ReferenceError: Cannot access 'b' before initialization
5let b = 2;
6
7console.log(c); // ReferenceError: Cannot access 'c' before initialization
8const c = 5;

Если создать переменную с помощью var на верхнем уровне (глобальный уровень), создастся свойство для глобального объекта; в случае с браузером - это объект window.

1var myName = 'Alex';
2
3console.log(window.myName) // Alex

Если попробовать повторить тоже самое с let

1let newName = 'Alex';
2console.log(window.myName) // undefined
3// 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 = []
2
3for ( var item of [ "alpha", "sigma", "omega" ] )
4 res.push ( () => console.log ( item ) )
5
6res[0]() // omega
7res[1]() // omega
8res[2]() // omega

let

1var res = []
2
3for ( let item of [ "alpha", "sigma", "omega" ] )
4 res.push ( () => console.log ( item ) )
5
6res[0]() // alpha
7res[1]() // sigma
8res[2]() // omega

Самостоятельная работа 👩‍💻👨‍💻

Задание :one:

Поверните данную строку в правильном направлении, периодически удаляя одну букву с конца строки и прикрепляя ее к передней части.

1function rotateString(str){
2
3}
4rotateString("cat");
5
6/**
7 * Output:
8 * cat
9 * tca
10 * atc
11 * cat
12 */

подсказка: используйте 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(){}
2
3
4removeDuplicates([3, 'a', 'a', 'a', 2, 3, 'a', 3, 'a', 2, 4, 9, 3]) // [3, 'a', 2, 4, 9]
Hello