Добавить в корзинуПозвонить
Найти в Дзене
ovnoCod

Язык JavaScript - Устаревшее ключевое слово "var"

Если вы откроете любой учебник JavaScript десятилетней давности, вы увидите его. Он везде: в циклах, в функциях, в глобальном пространстве. var был королём переменных. Но времена изменились. В 2015 году в мире JavaScript произошла революция. Появились let и const, и старый добрый var внезапно стал... опасным. Не потому, что он сломался. Он работает как работал. Просто мы поняли, что его поведение - это источник багов, а не фича. Сегодня мы разберём, почему var - это анахронизм, какие проблемы он создаёт и почему вы должны использовать let и const всегда. И, конечно, посмотрим на legacy-код, где var всё ещё жив, чтобы понимать, что там происходит. var - это способ объявления переменных в JavaScript с момента создания языка в 1995 году. Он был единственным вариантом до появления ES6. javascript var name = "Анна";
var age = 25;
var isActive = true; Всё просто. Но за этой простотой скрываются странности. Самая большая проблема var: он не знает про блоки { }. Для него существует только функ
Оглавление
картинка взята с ya.ru
картинка взята с ya.ru

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

Если вы откроете любой учебник JavaScript десятилетней давности, вы увидите его. Он везде: в циклах, в функциях, в глобальном пространстве. var был королём переменных. Но времена изменились.

В 2015 году в мире JavaScript произошла революция. Появились let и const, и старый добрый var внезапно стал... опасным. Не потому, что он сломался. Он работает как работал. Просто мы поняли, что его поведение - это источник багов, а не фича.

Сегодня мы разберём, почему var - это анахронизм, какие проблемы он создаёт и почему вы должны использовать let и const всегда. И, конечно, посмотрим на legacy-код, где var всё ещё жив, чтобы понимать, что там происходит.

Часть 1. Что такое var? Краткий экскурс в историю

var - это способ объявления переменных в JavaScript с момента создания языка в 1995 году. Он был единственным вариантом до появления ES6.

javascript

var name = "Анна";
var age = 25;
var isActive = true;

Всё просто. Но за этой простотой скрываются странности.

Часть 2. Проблема №1: Отсутствие блочной области видимости

Самая большая проблема var: он не знает про блоки { }. Для него существует только функциональная область видимости.

javascript

if (true) {
var message = "Привет";
}
console.log(message);
// "Привет" (вылезло из блока!)

Почему это плохо? Переменная, которую вы создали внутри условия или цикла, "вытекает" наружу. Это приводит к неожиданным багам и конфликтам имён.

javascript

for (var i = 0; i < 3; i++) {
// i видна не только внутри цикла
}
console.log(i);
// 3 (доступна после цикла!)

Сравнение с let:

javascript

if (true) {
let message = "Привет";
}
console.log(message);
// ReferenceError: message is not defined

Часть 3. Проблема №2: Hoisting (поднятие) с undefined

Переменные, объявленные через var, "поднимаются" в начало своей области видимости. Но не их значение, а само объявление.

javascript

console.log(name); // undefined (не ошибка!)
var name = "Анна";

Как это видит движок:

javascript

var name; // объявление поднято
console.log(name);
// undefined
name = "Анна";
// инициализация осталась на месте

Почему это плохо? Вы можете использовать переменную до её объявления, получить undefined и гадать, почему код не работает. В больших функциях это особенно коварно.

Сравнение с let (Temporal Dead Zone):

javascript

console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "Анна";

let и const тоже поднимаются, но не инициализируются. До строки объявления переменная существует, но находится в "временной мёртвой зоне" (Temporal Dead Zone, TDZ). Любое обращение к ней вызывает ошибку. Это хорошо - ошибка лучше, чем undefined.

Часть 4. Проблема №3: Повторное объявление

var позволяет объявить одну и ту же переменную несколько раз. Без ошибок. Без предупреждений.

javascript

var user = "Анна";
var user = "Борис";
// перезаписали
var user = "Вика";
// и ещё раз

console.log(user);
// "Вика"

Почему это плохо? Вы случайно можете перезаписать существующую переменную и даже не заметить этого. Особенно опасно в больших проектах.

Сравнение с let и const:

javascript

let user = "Анна";
let user = "Борис";
// SyntaxError: Identifier 'user' has already been declared

Часть 5. Проблема №4: Глобальные переменные становятся свойствами window

В браузере глобальные переменные, объявленные через var, становятся свойствами глобального объекта window.

javascript

var globalVar = "я глобальный";
console.log(window.globalVar);
// "я глобальный"

Почему это плохо? Вы можете случайно перезаписать существующее свойство window. Например, назвать переменную name или document.

javascript

var name = "Анна"; // перезаписали window.name!
console.log(window.name);
// "Анна" (а должна быть информация об окне)

let и const не добавляют свойства в window:

javascript

let globalLet = "я глобальный";
console.log(window.globalLet);
// undefined

Часть 6. Проблема №5: Замыкания в циклах (классический баг)

Из-за функциональной области видимости var и отсутствия блочной области возникает классическая ошибка с замыканиями.

javascript

for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
// 3, 3, 3 (все три!)
}, 100);
}

Что произошло? Все три функции замыкаются на одну и ту же переменную i. К моменту вызова таймеров цикл уже завершился, i = 3.

С let всё работает правильно:

javascript

for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
// 0, 1, 2
}, 100);
}

let создаёт новую переменную для каждой итерации цикла.

Часть 7. Исторический контекст: почему var такой странный?

JavaScript был создан за 10 дней. У Брендана Эйха не было времени на продумывание тонкостей. Он взял идеи из разных языков:

  • Функциональная область видимости - как в C (где блоки не создают новую область)
  • Hoisting - особенность реализации, которая стала спецификацией
  • Отсутствие блочной области - потому что в 1995 году это казалось ненужным

В 2015 году, с выходом ES6, ошибки прошлого исправили, добавив let и const. Но var оставили для обратной совместимости. Сломанный код 90-х годов всё ещё работает.

Часть 8. var в строгом режиме ("use strict")

Строгий режим не исправляет основные проблемы var, но добавляет некоторые ограничения.

javascript

"use strict";

// Всё ещё работает (к сожалению)
var x = 10;
var x = 20;
// повторное объявление всё ещё разрешено

// Но это больше не создаёт глобальную переменную
function test() {
mistyped = 5;
// ReferenceError (раньше создало бы window.mistyped)
}

Часть 9. Когда var всё ещё может быть полезен? (спойлер: почти никогда)

Сценарий 1: Поддержка очень старых браузеров (IE10 и ниже)

Если вам нужно поддерживать Internet Explorer 10 (который не поддерживает let и const), у вас нет выбора. Но таких проекстов становится всё меньше.

Сценарий 2: Консоль браузера (для быстрых экспериментов)

В консоли браузера var иногда удобнее, потому что переменные, объявленные через let в консоли, ведут себя странно при повторном вводе. Но это крайне специфичный случай.

Сценарий 3: Чтение старого кода

Вам нужно понимать var, потому что вы будете встречать его в legacy-коде. Но не использовать.

Часть 10. Антипаттерны с var, которые нужно знать

10.1 Случайное создание глобальной переменной

javascript

function oops() {
// забыли var/let/const
accidentalGlobal = "я стал глобальным";
}
oops();
console.log(window.accidentalGlobal);
// "я стал глобальным"

10.2 Переменная, переопределённая до объявления

javascript

function confusing() {
console.log(x);
// undefined (не ошибка!)
var x = 10;
console.log(x);
// 10
}

10.3 Переменная, "вытекающая" из цикла

javascript

function processItems(items) {
for (var i = 0; i < items.length; i++) {
// обрабатываем item
}
// i всё ещё доступна и равна items.length
// можно случайно использовать i в другом месте
}

Часть 11. Миграция с var на let и const

Пошаговая стратегия

  1. Замените все var на let - это безопасно в 99% случаев.
  2. Найдите переменные, которые никогда не меняются, и замените на const.
  3. Проверьте код на наличие проблем с блочной областью - особенно циклы и условия.
  4. Убедитесь, что нет повторных объявлений - let их запрещает.

javascript

// До
var name = "Анна";
var age = 25;
var isActive = true;

for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}

// После
const name = "Анна";
let age = 25;
let isActive = true;

for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100);
}

Часть 12. Сравнительная таблица: var vs let vs const

-2

Часть 13. Реальные примеры из жизни

13.1 Баг из-за всплытия переменной

javascript

// Плохо (с var)
function calculatePrice(items) {
var total = 0;

for (var i = 0; i < items.length; i++) {
var discount = items[i].discount || 0;
total += items[i].price * (1 - discount);
}

// Ошибка: i и discount доступны здесь!
console.log(`Обработано ${i} товаров со скидкой ${discount}`);
return total;
}

// Хорошо (с let/const)
function calculatePrice(items) {
let total = 0;

for (let i = 0; i < items.length; i++) {
const discount = items[i].discount || 0;
total += items[i].price * (1 - discount);
}

// Ошибка! i и discount не доступны
// console.log(`Обработано ${i} товаров`); // ReferenceError
return total;
}

13.2 Проблема с асинхронностью в цикле

javascript

// Плохо (с var)
for (var i = 1; i <= 5; i++) {
setTimeout(() => {
console.log(`Запрос ${i}`);
// все 6 (или 5? зависит)
}, i * 100);
}
// Вывод: 6, 6, 6, 6, 6 (или 5,5,5,5,5 - зависит от условий)

// Хорошо (с let)
for (let i = 1; i <= 5; i++) {
setTimeout(() => {
console.log(`Запрос ${i}`);
// 1, 2, 3, 4, 5
}, i * 100);
}

13.3 Случайная перезапись глобальной переменной

javascript

// Плохо (с var)
var name = "Анна";
// перезаписывает window.name

function saveUserName() {
// ... какой-то код
name = "Борис";
// ой, изменили глобальную переменную
}
saveUserName();
console.log(name);
// "Борис"

// Хорошо (с let/const)
let userName = "Анна";
// не попадает в window

function saveUserName() {
let userName = "Борис";
// локальная переменная
// глобальная не изменилась
}
saveUserName();
console.log(userName);
// "Анна"

Часть 14. Инструменты для борьбы с var

ESLint: правило no-var

ESLint может запретить использование var в вашем коде.

json

{
"rules": {
"no-var": "error"
}
}

Теперь любой var будет считаться ошибкой.

Автоматическая миграция

bash

# Используйте eslint --fix для автоматической замены var на let/const
npx eslint --fix . --rule 'no-var: error'

Но проверьте результат - иногда var нужно заменить на const, а не на let.

Итог: Манифест современного разработчика

  1. Забудьте var. В современном JavaScript ему нет места.
  2. Используйте const по умолчанию - если переменная не будет меняться.
  3. Используйте let только когда переменная действительно должна меняться.
  4. Никогда не используйте var - даже если кажется, что "так проще".
  5. Понимайте var, чтобы читать старый код - но не пишите на нём.

Финальный тест (что выведет?):

javascript

var a = 1;
var a = 2;
console.log(a);

if (true) {
var b = 3;
}
console.log(b);

for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
console.log(i);

Ответы: 2, 3, 3, 3, 3, 3

var - это исторический артефакт. Как зазубренный край на старом стекле - он был нужен когда-то, но сейчас только режет руки. Современный JavaScript дал нам let и const. Они предсказуемы, безопасны и делают код понятнее. Используйте их. Всегда. А если увидите var в чужом коде - знайте, что это призрак прошлого, который скоро исчезнет навсегда.