Кратко
СкопированоПеременные в JavaScript хранят значения, которыми оперирует код. Для создания переменных используются ключевые слова var
, let
и const
.
Что такое переменные
СкопированоПеременные — это именованные контейнеры для хранения данных.
Для создания переменной используется ключевое слово let
, const
или var
. Сразу за ключевым словом идёт название переменной либо перечень переменных через запятую. Создание переменной также называют объявлением переменной. Например:
var singleVariablelet firstVariable, secondVariable, thirdVariable
var singleVariable let firstVariable, secondVariable, thirdVariable
Чаще всего, при объявлении переменной ей устанавливают стартовое значение при помощи оператора присваивания =
. Тип значения может быть абсолютно любым — строка, число, объект, массив и так далее.
// Объявление переменной и присваиваниеlet string = 'foo'const array = ['foo', 'bar', 'baz']var number = 10// Множественное объявление и присваиваниеlet firstNumber = 5, secondNumber = 10
// Объявление переменной и присваивание let string = 'foo' const array = ['foo', 'bar', 'baz'] var number = 10 // Множественное объявление и присваивание let firstNumber = 5, secondNumber = 10
Затем переменную можно использовать как заменитель значения в коде:
let name = 'Уолтер'let surname = 'Вайт'let fullName = name + ' ' + surnameconsole.log(fullName)// Уолтер Вайт
let name = 'Уолтер' let surname = 'Вайт' let fullName = name + ' ' + surname console.log(fullName) // Уолтер Вайт
Правила именования переменных
СкопированоДля имени переменной можно использовать следующие символы:
- буквы латинского алфавита;
- цифры;
- символы
$
и_
.
Первый символ не должен быть цифрой:
let letters, $dollarsign, _underscorelet 1number// SyntaxError: Invalid or unexpected token
let letters, $dollarsign, _underscore let 1number // SyntaxError: Invalid or unexpected token
В качестве названий переменных нельзя использовать зарезервированные языком слова. Например: class
, super
, throw
, yield
, var
, let
, const
и так далее. С полным списком таких слов можно ознакомиться здесь.
Создание переменных
СкопированоПеред выполнением скрипта JavaScript находит код создания переменных и заранее создаёт их. Получается, что в начале выполнения скрипта все переменные, описанные в коде, уже объявлены. В зависимости от браузера, они могут быть равны undefined
(в Chrome и Safari), либо, в случае с let
и const
в браузере Firefox, не равны ничему и иметь специальное состояние uninitialized
:
console.log('Старт')var byVar = 5let byLet = 10const byConst = 15console.log('Конец')
console.log('Старт') var byVar = 5 let byLet = 10 const byConst = 15 console.log('Конец')
В конце скрипта, после того как произошло присвоение стартовых значений, переменные равны 5
, 10
и 15
:
Получается, что некоторое время переменная может содержать значение undefined
и быть доступной для чтения. Этим нужно пользоваться с осторожностью.
Переменные let
и const
СкопированоПеременные let
и const
появились в версии EcmaScript 2015 года (ES6), и сейчас используются намного чаще чем var
.
Объявление
СкопированоИспользуя ключевое слово let
, можно объявить переменную без присвоения ей начального значения. В таком случае она будет равна undefined
:
let aconsole.log(a)// undefineda = 5console.log(a)// 5
let a console.log(a) // undefined a = 5 console.log(a) // 5
При помощи const
нельзя объявлять переменные без значения:
const a// SyntaxError: Missing initializer in const declaration// Правильноconst b = 5
const a // SyntaxError: Missing initializer in const declaration // Правильно const b = 5
К переменным let
и const
нельзя обращаться до их объявления в коде:
console.log(a)// ReferenceError: Cannot access 'a' before initializationconsole.log(b)// ReferenceError: Cannot access 'b' before initializationlet a = 5const a = 5
console.log(a) // ReferenceError: Cannot access 'a' before initialization console.log(b) // ReferenceError: Cannot access 'b' before initialization let a = 5 const a = 5
Почему так?
У let
и const
есть так называемая temporal dead zone (TDZ) — момент выполнения скрипта до объявления переменной. Переменная может использоваться и выше объявления, при условии, что содержащая её часть кода будет выполнена после инициализации:
function foo() { console.log('from foo', a)}Promise.resolve() .then(() => console.log('from promise', a))setTimeout(() => console.log('from timer',a))let a = 10foo()// 'from foo 10', 'from promise 10', 'from timer 10'
function foo() { console.log('from foo', a) } Promise.resolve() .then(() => console.log('from promise', a)) setTimeout(() => console.log('from timer',a)) let a = 10 foo() // 'from foo 10', 'from promise 10', 'from timer 10'
TDZ есть также и у ES6-классов, несмотря на то, что они являются «синтаксическим сахаром» над обычными функциями.
console.log(Foo)class Foo { constructor(bar) { this.bar = bar }}// ReferenceError: Cannot access 'Foo' before initialization
console.log(Foo) class Foo { constructor(bar) { this.bar = bar } } // ReferenceError: Cannot access 'Foo' before initialization
А функции (объявленные как Function Declaration) TDZ не имеют.
console.log(Foo)function Foo() { this.bar = bar}// ƒ Foo() { this.bar = bar}
console.log(Foo) function Foo() { this.bar = bar } // ƒ Foo() { this.bar = bar}
Оба типа переменных имеют блочную область видимости и не становятся частью глобального объекта (window
в браузере, global
в Node.js). Блочная область видимости не даёт получить значение переменной вне блока, где она была объявлена.
Если объявить переменные внутри блока if
, то обращение к ним вне блока будет выбрасывать ошибку:
if (true) { let a = 5 const b = 10 console.log(a) // 5 console.log(b) // 10}console.log(a)// ReferenceError: a is not definedconsole.log(b)// ReferenceError: b is not defined
if (true) { let a = 5 const b = 10 console.log(a) // 5 console.log(b) // 10 } console.log(a) // ReferenceError: a is not defined console.log(b) // ReferenceError: b is not defined
Одинаковые имена переменных
СкопированоОбъявление переменной с именем, которое уже используется в текущей области видимости, приведёт к ошибке:
let a = 5let a = 10// SyntaxError: Identifier 'a' has already been declared
let a = 5 let a = 10 // SyntaxError: Identifier 'a' has already been declared
То же правило работает и при использовании const
, и при использовании смешанного подхода:
const a = 5const a = 10// SyntaxError: Identifier 'a' has already been declaredvar b = 5const b = 10// SyntaxError: Identifier 'b' has already been declared
const a = 5 const a = 10 // SyntaxError: Identifier 'a' has already been declared var b = 5 const b = 10 // SyntaxError: Identifier 'b' has already been declared
В то же время можно объявлять переменные с одинаковым именем в разных областях видимости. В этом случае значение будет зависеть от той области видимости, где происходит чтение:
let name = 'Ольга'if (true) { let name = 'Елена' console.log(name) // Елена}console.log(name)// Ольга
let name = 'Ольга' if (true) { let name = 'Елена' console.log(name) // Елена } console.log(name) // Ольга
Смена значения в let
и const
СкопированоЗначение в переменной, созданной через let
, можно изменять:
let a = 5console.log(a)// 5a = 10console.log(a)// 10
let a = 5 console.log(a) // 5 a = 10 console.log(a) // 10
Стартовое значение const
изменить нельзя, будь то примитивное значение:
const a = 5a = 10// TypeError: Assignment to constant variable
const a = 5 a = 10 // TypeError: Assignment to constant variable
Или ссылка на объект:
const obj = { a: 5,}obj = { a: 10,}// TypeError: Assignment to constant variable
const obj = { a: 5, } obj = { a: 10, } // TypeError: Assignment to constant variable
Однако объект, хранящийся в const
, можно мутировать. Объекты хранятся по ссылке, и изменение объекта не приводит к изменению ссылки на него:
const obj = { a: 5,}obj.a = 10console.log(obj)// { a: 10 }
const obj = { a: 5, } obj.a = 10 console.log(obj) // { a: 10 }
Переменные var
СкопированоОбъявление переменных при помощи ключевого слова var
было в JavaScript с первых версий.
Объявление
СкопированоПеременные var
можно объявлять без присвоения им значения, в таком случае они будут равны undefined
:
var aconsole.log(a)// undefinedvar b = 5console.log(b)// 5
var a console.log(a) // undefined var b = 5 console.log(b) // 5
Переменные, объявленные через var
, имеют функциональную область видимости. Они доступны только в пределах текущей функции или глобального объекта, если функции нет:
if (true) { var a = 5}function foo() { var b = 10}console.log(a)// 5console.log(b)// ReferenceError: b is not defined
if (true) { var a = 5 } function foo() { var b = 10 } console.log(a) // 5 console.log(b) // ReferenceError: b is not defined
Объявление переменных вне функций делает их глобальными переменными. Они доступны как свойства глобального объекта:
var varVariable = 5console.log(window.varVariable)// 5
var varVariable = 5 console.log(window.varVariable) // 5
К переменным, объявленным при помощи ключевого слова var
, можно обращаться до момента объявления. В отличие от let
и const
, ошибки это не вызовет. Такое поведение называется hoisting - «всплытие»:
console.log(a)// undefinedvar a = 5console.log(a)// 5
console.log(a) // undefined var a = 5 console.log(a) // 5
Разберём, как работает функциональная область видимости:
var a = 5function foo() { console.log(a) // undefined var a = 10 console.log(a) // 10}foo()console.log(a)// 5
var a = 5 function foo() { console.log(a) // undefined var a = 10 console.log(a) // 10 } foo() console.log(a) // 5
Перед выполнением функции в глобальной области видимости присутствует переменная a
, равная 5
:
Во время выполнения функции формируется новая функциональная область видимости, в которой тоже присутствует переменная a
. Эта переменная была объявлена с помощью var
внутри функции, в момент выполнения которой она «всплыла» со значением равным undefined
. В строке 4 происходит обращение именно к ней (до её объявления), а не к той, что находится вне функции.
В строке 8 значение переменной a
уже равно 10.
После выполнения функции локальная область видимости была удалена. В консоли выводится глобальная переменная a
.
Более подробно об этом можно прочитать в отдельной статье
Смена значения в var
СкопированоЗначение, хранящееся в переменной var
, можно изменить двумя способами:
- обратиться к имени переменной и присвоить новое значение:
var a = 5console.log(a)// 5a = 10console.log(a)// 10
var a = 5 console.log(a) // 5 a = 10 console.log(a) // 10
- обратиться к имени переменной вместе с ключевым словом
var
:
var a = 5console.log(a)// 5var a = 10console.log(a)// 10
var a = 5 console.log(a) // 5 var a = 10 console.log(a) // 10
На практике
Скопированосоветует Скопировано
🛠 В новом коде используйте только let
или const
. Используйте let
в тех случаях, когда значение переменной меняется. Во всех остальных используйте const
. Проще всего всегда по умолчанию использовать ключевое слово const
и исправлять объявление переменной на let
, если появляется нужда изменить её значение далее в коде.
🛠 Называйте переменные так, чтобы можно было легко понять, что в них хранится. Например:
let url = 'https://doka.guide'const now = Date.now()const user = { name: 'John', age: 30,}
let url = 'https://doka.guide' const now = Date.now() const user = { name: 'John', age: 30, }
Исключением считается именование счётчиков в циклах for
, в которых обычно используются одиночные буквы i
, j
, и так далее.
Имена переменных могут состоять из нескольких слов, поэтому для удобства их чтения в JavaScript принято использовать так называемую «верблюжью нотацию» (camelCase), когда каждое новое слово, начиная со второго, пишется с заглавной буквы:
const fullName = 'John Doe'const arrayOfNumbers = [1, 2, 3]
const fullName = 'John Doe' const arrayOfNumbers = [1, 2, 3]
Имена констант (переменные, которые не меняют своё значение) принято писать, используя screaming_snake_case. В данной нотации все слова пишутся заглавными буквами, а разделителем является символ _
.
const BASE_URL = 'https://doka.guide'const PORT = 3000const UNAUTHORIZED_CODE = 401
const BASE_URL = 'https://doka.guide' const PORT = 3000 const UNAUTHORIZED_CODE = 401
На собеседовании
Скопировано отвечает
Скопированоnull
задаётся переменной явно и означает, что она является объектом, но структура этого объекта ещё не определена. undefined
присваивается переменной (переменная не декларирует объект), когда она была объявлена, но не было определено её начальное значение. Функция может возвращать undefined
или null
. Всё зависит от того, что мы ожидаем в результате работы функции. Если мы ожидаем объект, но по каким-то причинам функция его вернуть не может, то возвращаем null
. Если функция должна вернуть, например, число (главное, не объект), но не может этого сделать, то она возвращает undefined
.
Без начального значения можно оставлять только переменную, объявленную через let
или var
. Если объявить переменную через const
и не задать ей начального значения, будет ошибка: Uncaught SyntaxError
.
Поговорим немного о приведении типов. Для начала, пример:
console.log(null + null); // 0console.log(undefined + undefined); // NaN
console.log(null + null); // 0 console.log(undefined + undefined); // NaN
Почему так?
null
во время сложения приводится к нулю;undefined
во время сложения приводится кNaN
.NaN
это аббревиатура от "not a number" — не число. Результат арифметической операции равенNaN
, если во время операции произошла ошибка и ожидаемый числовой результат не может быть выражен числом.
Есть ещё один хитрый пример:
console.log(null + []); // "null"
console.log(null + []); // "null"
Почему так?
Подсказка, почему так, кроется именно в типе результате: "null" — строка. А не примитивное значение null
.
JavaScript сначала приводит массив к примитивному значению. Для этого вызывается метод to
, который вызывает метод join
. Т.к. массив пустой, то join
вернёт пустую строку ""
. А сложение чего-то со строкой в JS возвращает строку. Поэтому null
уже никуда не приводится, а возращается строка "null"
.
Немного упомяну и про оператор нулевого слияния (?
). В выражении между двумя операндами он будет возвращать первый операнд, если он не равен null
или undefined
. Можно сказать, что ?
приравнивает смысл undefined
и null
к «ничего не содержит», и, в этом случае, кладёт в переменную значение второго операнда.
отвечает
СкопированоПеременные объявленные через var
всплывают (hoisting).
Это значит, что если мы обратимся к переменной ещё до момента её инициализации, то получим undefined
.
console.log(a); // Не может получить доступ к 'a' до её инициализацииconsole.log(b); // Не может получить доступ к 'b' до её инициализацииconsole.log(c); // undefinedlet a = 10;const b = 20;var c = 30;
console.log(a); // Не может получить доступ к 'a' до её инициализации console.log(b); // Не может получить доступ к 'b' до её инициализации console.log(c); // undefined let a = 10; const b = 20; var c = 30;
У переменных let
, const
и var
разная область видимости.
У let
и const
область видимости ограничена блоком, а не функцией.
Другими словами, если переменные let
и const
объявлены внутри {
, то доступны только там и на всех вложенных уровнях. Переменная, объявленная через var
такую область видимости игнорирует и может быть доступна за её пределами.
if (true) { let a = 10; const b = 20; var c = 30;}console.log(a); // ReferenceErrorconsole.log(b); // ReferenceErrorconsole.log(c); // 30
if (true) { let a = 10; const b = 20; var c = 30; } console.log(a); // ReferenceError console.log(b); // ReferenceError console.log(c); // 30
Переменная, объявленная через const
становиться константой и её невозможно переопределить. При попытке это сделать мы получим ошибку.
let a = 10;const b = 20;a = 15 // Все впорядкеb = 40 // TypeError
let a = 10; const b = 20; a = 15 // Все впорядке b = 40 // TypeError