Переменные

Переменные

В Javascript под переменной понимается имя, с которым связано значение или место для хранения такого значения.

Название переменной может состоять из одной и более букв, цифр или символов _ и $, при этом первый символ не может быть цифрой. Кроме того, имя переменной не должно совпадать с ключевыми словами, например: var, class, return, export.

Для объявления переменной изначально использовалось только ключевое слово var:

var a;
var a, b, c;
a=5;
b="Привет";

При объявлении переменной ей присваивается специальное значение undefined.

Для краткости можно совместить объявление переменной и запись данных в нее:

var a=5;

Можно объявить несколько переменных сразу:

var a=5, b="Привет", c=0;

или можно в многострочном виде:

var a=5, 
    b="Привет", 
    c=0;

В названии переменных можно использовать и русские буквы, но это считается плохой практикой.

В старом стандарте разрешалось создавать переменную и без var, просто присвоив ей значение, но сейчас это не рекомендуется:

a=5; //не рекомендуется и не работает в режиме "use strict"

Области видимости переменных

Переменные в Javascript бывают глобальными и локальными. Глобальная переменная доступна везде, локальная — только в текущей области видимости.

Глобальные переменные можно изменить в любой части программы, что может приводить к ошибкам в больших программах.

В JavaScript все глобальные переменные и функции являются свойствами специального объекта, который называется «глобальный объект» (global object). В браузере этот объект явно доступен под именем window. Присваивая или читая глобальную переменную, мы, фактически, работаем со свойствами window.

Существует три способа объявления глобальной переменной:

var a=1;
window.b=34; //можно просто добавить новое свойство window
c=4; //не рекомендуется

По умолчанию не объявленные переменные становятся глобальными, что может приводить к ошибкам.

Обратите внимание, что в JavaScript можно сослаться на переменную, которая объявляется позже, и не получить при этом исключения. Эта концепция известна как поднятие (hoisting) переменных; переменные в JavaScript поднимаются в самое начало функции или выражения. Однако, переменные, которые еще не были инициализированы, возвратят значение undefined:

Отличие var a = 1 от a = 1 — в том, что переменная будет создана не на этапе входа в область видимости, а в момент присвоения. Сравните: ``js alert(a); // undefined var a = 1;

и
```js
alert(a); // ошибка, переменной ещё не существует
a = 1;

Локальной областью видимости называют любую область видимости, определённую после глобальной. Обычно у нас есть одна Глобальная область видимости, и каждая определяемая функция несёт в себе локальную область видимости. Каждая функция, определённая внутри другой функции, имеет свою локальную область видимости, связанную с областью видимости внешней функции.

// область видимости A: глобальная
function myfunc() {
  // область видимости B: локальная
};

Все переменные из Локальной области видимости не видны в Глобальной области видимости. К ним нельзя получить доступ снаружи напрямую. Пример:

function myfunc() {
  var a = 'Вася';
  console.log(a); // Вася
};
myfunc();
// ReferenceError: a is not defined
console.log(a);

Все Локальные области видимости создаются только в функциях.

// Глобальная область видимости
function  myfunc1() {
  // область видимости myfunc1
  function () myfunc2{
    // область видимости myfunc2
  };
};

Если одна функция определена внутри другой, внутренняя имеет доступ к область видимости внешней. Это называется «лексической областью видимости», или «замыканием», или ещё «статической областью видимости».

function myfunc() {
  var a = 'Вася';
  function myfunc2() {
    console.log('Мое имя ' + a);
  };
  console.log(a);
  myfunc2(); // вызов функции
};
myfunc();

С лексической областью видимости довольно просто работать — всё, что определено в область видимости родителя, доступно в области видимости ребенка. К примеру:

var a = 'Вася';
function myfunc1() {
  console.log('myfunc1: '+a); // a доступно здесь
  function myfunc2() {
    console.log('myfunc2: '+a); // a и здесь
    function myfunc3() {
      console.log('myfunc3: '+a); // a и даже здесь!
    };
    myfunc3();
  };
  myfunc2();
};
myfunc1();

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

В отличие от других языков в Javascript нет блочной области видимости. Новые области видимости не создаются циклами типа for или while или директивами типа if или switch.

В JavaScript нет разницы между объявлением вне блока:

var a;
{
  a = 1;
}  

и вне его:

a = 1;
{
  var a;
}  

Также нет разницы между объявлением в цикле и вне его:

for (var i = 0; i < 10; i++) { }

аналогичен:

var i;
for (i = 0; i < 10; i++) { }

Не важно, где и сколько раз объявлена переменная. Объявлений var может быть сколько угодно:

var i;
for (var i = 0; i < 10; i++) { }
var i=100;

В ES3 выражение catch в конструкции try/catch имеет блочную область видимости, что означает, что у этого выражения есть собственная область видимости. Важно отметить, что выражение try не имеет блочной области видимости, она есть только у выражения catch.

Объявление переменной через let

Для устранения недостатков объявления переменной через var в современном стандарте используется определение через let.

Переменная, объявленная через let, видна только в рамках блока {...}, в котором объявлена.

let a = 1; 
if (true) {
  let a = 10;
  console.log(a); // 10 (внутри блока)
}
console.log(a); // 1 (снаружи блока значение не изменилось)

Переменная, объявленная с помощью let, видна только после объявления. Переменные var существуют и до объявления. Они равны undefined.

alert(a); // undefined
var a = 1;

Переменные let до объявления вообще не существуют. Т.е. переменные let не поднимаются вверх блока.

alert(a); // ошибка, нет такой переменной
let a = 1;

Переменные let нельзя повторно объявлять.

let a;
let a; // ошибка: переменная a уже объявлена

При использовании в цикле, для каждой итерации создаётся своя переменная. Это позволяет легко решить классическую проблему с замыканиями:

var Mes = ['Первая','Вторая','Третья',"что-то иное"];
function myonclick(){
  for(var i=1; i<=3;i++){
  var t=String (i);
    var item=document.getElementById(String (i))
    item.onclick = function() {
    alert(Mes[i-1]);
    } 
  }
}
myonclick();

Если вы запустите этот код, то увидите, что он работает не так, как мы хотели. Какую бы кнопку вы не выбрали, всегда будет высвечиваться сообщение, что это "что-то иное". Проблема в том, что функции, присвоенные как обработчики события onclick, являются замыканиями. Они состоят из описания функции и контекста исполнения (окружения), унаследованного от функции myonclick(). Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения (окружением). К моменту возникновения события onclick цикл уже давно отработал, а значит переменная i (одна и та же для всех трех замыканий) равна 4, а Mes[4-1] указывает на последний элемент массива, который как раз в"что-то иное"..

Объявление let i создаёт для каждого повторения блока в цикле свою переменную, которую функция и получает из замыкания в последних строках.

var Mes = ['Первая','Вторая','Третья',"что-то иное"];
function myonclick(){
  for(let i=1; i<=3;i++){
  var t=String (i);
    var item=document.getElementById(String (i))
    item.onclick = function() {
    alert(Mes[i-1]);
    } 
  }
}
myonclick();

В качестве самого простого решения при использовании объявления var можно предложить использование фабричной функции (function factory). Функция onclickcallback() вместо того, чтобы делить на всех одно окружение, создает каждому из замыканий свое собственное, в котором переменная i указывает на правильный элемент массива Mes.

var Mes = ['Первая','Вторая','Третья',"что-то иное"];
function onclickcallback(m) {
    return function() {
    alert(m);
  }
} 
function myonclick(){
  for(var i=1; i<=3;i++){
  var t=String (i);
    var item=document.getElementById(String(i))
    item.onclick = onclickcallback(Mes[i-1]);
  }
}
myonclick();

Объявление констант

Объявление const задаёт константу, то есть переменную, которую нельзя менять

const a = 1;
a = 5; // ошибка

В остальном объявление const полностью аналогично let.

Если в константу присвоен объект, то от изменения защищена сама константа, но не свойства внутри неё:

const colors = {
  red: "#ff0000",
  green: "#00ff00",
  blue:"#0000ff",
};
colors.red = "#ff2400"; // допустимо
colors = "yellow"; // нельзя, будет ошибка