# 92. Переменные в javascript

Переменные – это та вещь, которая упрощает работу программиста. Конечно, при большом желании можно обойтись и без переменных, вычисляя на лету нужные значения и передавая их в поток выполнения, но это особый способ писать код.

Переменная может хранить строку, число, булево значение, ссылку на объект или массив, функцию, объект типа Symbol.

В javascript’е есть пара ключевых слов, чтобы объявить переменную: var и let. На собеседованиях очень любят спрашивать про var, потому что в нём больше подводных камней. Так, например, переменная объявленная через var “поднимается” (по англ. hoisting) по вашему блоку кода (функции в общем случае) к его началу. Посмотрим на следующий код:

function foo() {
    if (!hoisted) {
        console.log('defined undefined');
    }

    var hoisted = true;
}

foo();

Хотелось, чтобы в рантайме было выброшено исключение о том, что переменной hoisted не существует, но этого не произойдёт – переменная будет объявлена и ей присвоится значение undefined.

Если бы мы объявили hoisted с помощью let, то получили бы, как надо, reference error – hoisted is not defined.

Теперь немного усложним:

function foo() {
    if (!hoisted) {
        console.log('defined undefined');
    }

    hoisted = true;
}

foo();

Внезапно этот код много где может отработать, несмотря на то, что hoisted никогда не определяется. Всё потому, что при выполнении кода, такая запись будет рассматриваться интерпретатором как присвоение значения свойству (некоторого объекта). Чтобы определить какому, мы бежим по scope chain до самого корня, не находим это поле в нашей цепочке и создаём его в глобальном объекте, который равен window. Если бы нашли, то присвоили бы найденной переменной значение.

Кроме прочего, объявление переменной без var не позволит сборщику мусора удалить выделенную под неё память, ведь она будет полем объекта, а объект всё ещё нужен для работы.

Всё будет иначе (и, пожалуй, как надо), если сделать так:

'use strict';

function foo() {
    if (!hoisted) {
        console.log('defined undefined');
    }

   hoisted = true;
}

foo();

Замечаем небольшое изменение – ‘use strict’. Эта директива включает особый режим языка, в котором такие вольности не позволяются и будет выброшена ошибка ReferenceError.

А вот задачка:

function foo() {
    if (false) {
        var hoisted = true;
    }

    console.log(hoisted); // что случится в этом месте?
}

foo();

Предлагаю подумать, каким будет вывод и только потом читать следующий параграф.

Если бы мы использовали let, то вывод, как мы обсуждали раньше, привёл бы к ошибке ReferenceError. Здесь же получится, что мы объявили переменную, она всплыла до вершины блока (в данном случае к началу функции foo) и была инициализирована значением undefined.

И под конец, задачка на понимание. Лучше не забивать код сразу в консоль, а попробовать устно проговорить каждый из выводов, исправить при необходимости код и продолжать. Задачка с реального собеседования в одной из очень хороших российских компаний.

'use strict';

x = 5;

console.log(++x);
console.log(x++);

function test() {
    console.log(x);

    x += 10;

    console.log(x);
    console.log(typeof x);

    var x = 15;
    console.log(x);
}

test();
console.log(x);

Интересные ссылки:

  1. https://www.ecma-international.org/ecma-262/8.0/index.html#sec-variable-statement
  2. stackoverflow
Share
Send
2018   javascript
Popular