JavaScript Variables Complete Master Guide

JavaScript Variables: Complete Overview

JavaScript provides four main ways to declare variables: var, let, const, and global variables. Each has distinct characteristics, scoping rules, and use cases. Understanding how they relate to each other and when to use each type is fundamental to writing effective JavaScript code.

The Four Variable Types in JavaScript

  • var - Function-scoped, hoisted, can be re-declared - the original variable declaration
  • let - Block-scoped, cannot be re-declared, allows reassignment - modern replacement for var
  • const - Block-scoped, cannot be re-declared or reassigned - use by default for constants
  • Global Variables - Declared without any keyword or attached to global object - accessible everywhere

var - The Original Variable Declaration

var was the original way to declare variables in JavaScript. It's function-scoped, hoisted, and allows re-declaration. While largely replaced by let and const in modern code, understanding var is crucial for working with legacy codebases.

javascript
1// Example 1: Basic var usage
2var firstName = "John";
3var age = 30;
4var isActive = true;
5
6// Example 2: Function scoping
7function varExample() {
8    if (true) {
9        var functionScoped = "I'm accessible everywhere in this function";
10    }
11    console.log(functionScoped); // Works - no block scope
12}
13
14// Example 3: Hoisting behavior
15console.log(hoistedVar); // undefined (not error)
16var hoistedVar = "I was hoisted";
17
18// Example 4: Re-declaration allowed
19var counter = 5;
20var counter = 10; // No error - re-declaration permitted
21
22// Example 5: No temporal dead zone
23function hoistingTest() {
24    console.log(x); // undefined
25    var x = 10;
26}
27
28// Example 6: Loop issues with closures
29for (var i = 0; i < 3; i++) {
30    setTimeout(() => console.log(i), 100); // Logs: 3, 3, 3
31}
32
33// Example 7: Comparison with modern alternatives
34var oldWay = "function scoped";
35let modernWay = "block scoped";
36const constantWay = "immutable";

var Key Characteristics

  • Scope - Function-scoped - accessible throughout entire function
  • Hoisting - Fully hoisted - can be accessed before declaration (value: undefined)
  • Re-declaration - Allowed - can declare same variable multiple times
  • Use Case - Legacy code maintenance only - avoid in new code

let - Modern Block-Scoped Variables

let was introduced in ES6 as a modern replacement for var. It provides block scoping, prevents re-declaration, and has more predictable behavior. Use let when you need to reassign variables within block scope.

javascript
1// Example 1: Basic let usage
2let userName = "Alice";
3let score = 95.5;
4let isOnline = false;
5
6// Example 2: Block scoping
7function letExample() {
8    if (true) {
9        let blockScoped = "I'm only accessible in this block";
10        console.log(blockScoped); // Works
11    }
12    // console.log(blockScoped); // Error - not accessible
13}
14
15// Example 3: No hoisting initialization
16// console.log(letVariable); // ReferenceError: Cannot access before initialization
17let letVariable = "I'm not hoisted like var";
18
19// Example 4: No re-declaration
20let uniqueVar = "first declaration";
21// let uniqueVar = "second declaration"; // SyntaxError: Already declared
22
23// Example 5: Reassignment allowed
24let counter = 0;
25counter = 1;      // Valid
26counter++;        // Valid
27counter += 5;     // Valid
28
29// Example 6: Proper loop behavior
30for (let i = 0; i < 3; i++) {
31    setTimeout(() => console.log(i), 100); // Logs: 0, 1, 2
32}
33
34// Example 7: Temporal Dead Zone
35{
36    // console.log(tdzLet); // ReferenceError: TDZ
37    let tdzLet = "now initialized";
38    console.log(tdzLet); // Works
39}

let Key Characteristics

  • Scope - Block-scoped - accessible only within declaring block
  • Hoisting - Hoisted but not initialized - temporal dead zone exists
  • Re-declaration - Not allowed - cannot declare same variable in same scope
  • Use Case - When variable needs reassignment within block scope

const - Immutable Block-Scoped Constants

const provides block-scoped variables that cannot be reassigned. It requires initialization at declaration and is the preferred choice for variables that shouldn't change. Note: const doesn't make objects/arrays immutable, only prevents reassignment.

javascript
1// Example 1: Basic const usage
2const PI = 3.14159;
3const API_KEY = "abc123def456";
4const MAX_USERS = 1000;
5
6// Example 2: Must be initialized
7// const UNINITIALIZED; // SyntaxError: Missing initializer
8const INITIALIZED = "I have a value";
9
10// Example 3: No reassignment
11const TAX_RATE = 0.07;
12// TAX_RATE = 0.08;     // TypeError: Assignment to constant variable
13// TAX_RATE++;          // Error - would require reassignment
14
15// Example 4: Objects/arrays can be modified
16const user = { name: "John", age: 30 };
17user.age = 31;           // ✅ Allowed - modifying property
18user.city = "Boston";    // ✅ Allowed - adding property
19
20const colors = ["red", "green"];
21colors.push("blue");     // ✅ Allowed - modifying array
22colors[0] = "orange";    // ✅ Allowed - changing element
23
24// Example 5: Block scoping
25{
26    const BLOCK_CONST = "only in this block";
27    console.log(BLOCK_CONST); // Works
28}
29// console.log(BLOCK_CONST); // Error - not accessible
30
31// Example 6: const in loops
32const numbers = [1, 2, 3];
33for (const num of numbers) {
34    console.log(num); // 1, 2, 3 - new binding each iteration
35}
36
37// Example 7: Function expressions
38const calculateArea = (radius) => {
39    const PI = 3.14159;
40    return PI * radius * radius;
41};

Global Variables - Implicit Declarations

Global variables are accessible from anywhere in your code. They can be created by declaring variables without any keyword, attaching to the global object, or declaring outside any function. Global variables should be minimized as they can cause naming conflicts and hard-to-track bugs.

javascript
1// Example 1: Implicit global (avoid this)
2function createGlobal() {
3    accidentalGlobal = "I'm a global variable"; // No declaration keyword
4}
5createGlobal();
6console.log(accidentalGlobal); // "I'm a global variable" - pollutes global scope
7
8// Example 2: Explicit global
9var explicitGlobal = "I'm a global var"; // Declared outside any function
10let modernGlobal = "I'm a global let";   // Also global when declared outside functions
11const GLOBAL_CONST = "I'm a global const"; // Global constant
12
13// Example 3: Attaching to global object
14window.browserGlobal = "I'm on window object"; // Browser
15global.nodeGlobal = "I'm on global object";    // Node.js
16
17// Example 4: Global scope pollution
18var commonName = "original";
19
20function library1() {
21    commonName = "changed by library1"; // Modifies global
22}
23
24function library2() {
25    var commonName = "library2 version"; // Creates local (shadows global)
26}
27
28// Example 5: Strict mode prevents implicit globals
29function strictExample() {
30    "use strict";
31    // undeclaredVar = "error"; // ReferenceError: variable is not defined
32    var properVar = "correct";
33}
34
35// Example 6: Namespacing pattern to avoid pollution
36var MY_APP = MY_APP || {}; // Create namespace
37MY_APP.config = { apiUrl: "https://api.example.com" };
38MY_APP.utils = { calculate: function() {} };
39
40// Example 7: IIFE to avoid globals
41(function() {
42    var privateVar = "I'm not global";
43    // Code here doesn't pollute global scope
44})();

Complete Variable Comparison Table

Understanding the differences between all variable types helps you choose the right tool for each situation. This comprehensive comparison highlights key characteristics and use cases.

javascript
1// Side-by-side comparison in code
2
3// SCOPE COMPARISON
4function scopeComparison() {
5    if (true) {
6        var functionScoped = "var: function scope";
7        let blockScopedLet = "let: block scope";
8        const blockScopedConst = "const: block scope";
9        implicitGlobal = "global: becomes global";
10    }
11    
12    console.log(functionScoped);     // ✅ Works - function scoped
13    // console.log(blockScopedLet);   // ❌ Error - block scoped
14    // console.log(blockScopedConst); // ❌ Error - block scoped
15    console.log(implicitGlobal);     // ✅ Works - global
16}
17
18// HOISTING COMPARISON
19console.log(varHoisted);        // ✅ undefined (hoisted)
20// console.log(letHoisted);     // ❌ ReferenceError (TDZ)
21// console.log(constHoisted);   // ❌ ReferenceError (TDZ)
22// console.log(globalHoisted);  // ❌ ReferenceError
23
24var varHoisted = "hoisted";
25let letHoisted = "not hoisted";
26const constHoisted = "not hoisted";
27globalHoisted = "not hoisted";
28
29// RE-DECLARATION COMPARISON
30var canRedeclare = "first";
31var canRedeclare = "second";     // ✅ Allowed
32
33let noRedeclare = "first";
34// let noRedeclare = "second";  // ❌ SyntaxError
35
36const noRedeclareConst = "first";
37// const noRedeclareConst = "second"; // ❌ SyntaxError
38
39// Can shadow globals with same name
40var globalVar = "global";
41function test() {
42    var globalVar = "local"; // ✅ Shadows global
43    console.log(globalVar); // "local"
44}

Variable Types Comparison Matrix

  • Scope - var: Function | let: Block | const: Block | Global: Global
  • Hoisting - var: Yes (undefined) | let: Yes (TDZ) | const: Yes (TDZ) | Global: No
  • Re-declaration - var: Allowed | let: Not allowed | const: Not allowed | Global: Allowed
  • Reassignment - var: Allowed | let: Allowed | const: Not allowed | Global: Allowed
  • Initialization - var: Optional | let: Optional | const: Required | Global: Required
  • Use Case - var: Legacy code | let: Reassignable | const: Constants | Global: Avoid

How Variables Relate and Connect

Understanding the relationships between different variable types helps you see how JavaScript has evolved and how to migrate between different declaration styles effectively.

javascript
1// RELATIONSHIP 1: Evolution from var to let/const
2// Old way with var (ES5 and earlier)
3var oldVariables = "function scoped";
4for (var i = 0; i < 3; i++) {
5    // i is shared across all iterations
6}
7
8// Modern way with let/const (ES6+)
9let modernVariables = "block scoped";
10for (let i = 0; i < 3; i++) {
11    // i is unique to each iteration
12}
13
14// RELATIONSHIP 2: Scope chain connections
15const globalConstant = "I'm global";
16
17function outerFunction() {
18    var outerVar = "I'm in outer function";
19    
20    function innerFunction() {
21        let innerLet = "I'm in inner function";
22        
23        // All outer variables are accessible
24        console.log(globalConstant); // ✅ Accessible
25        console.log(outerVar);       // ✅ Accessible (closure)
26        console.log(innerLet);       // ✅ Accessible
27    }
28    
29    innerFunction();
30}
31
32// RELATIONSHIP 3: Migration path from var to const/let
33// Before: var for everything
34var counter = 0;
35var user = { name: "John" };
36var isActive = true;
37
38// After: Appropriate declarations
39let counter = 0;                    // let - will change
40const user = { name: "John" };      // const - object reference won't change
41const isActive = true;              // const - boolean won't change
42
43// RELATIONSHIP 4: Global pollution prevention
44// Problem: Implicit globals
45function problematic() {
46    globalPolluter = "I pollute global scope";
47}
48
49// Solution: Explicit declarations
50function clean() {
51    const localConstant = "I stay local";
52    let localVariable = "I also stay local";
53    var localOldStyle = "I'm function scoped";
54}
55
56// RELATIONSHIP 5: Temporal Dead Zone awareness
57{
58    // Temporal Dead Zone for let/const
59    // console.log(tdzLet); // ❌ ReferenceError
60    // console.log(tdzConst); // ❌ ReferenceError
61    
62    console.log(tdzVar); // ✅ undefined (var is hoisted)
63    
64    let tdzLet = "now safe";
65    const tdzConst = "now safe";
66    var tdzVar = "hoisted";
67}

Migration Patterns and Best Practices

Learn how to migrate from older patterns to modern JavaScript variable declarations, and understand the best practices for using each variable type in contemporary code.

javascript
1// MIGRATION PATTERN 1: var → const/let
2// Legacy code with var
3var app = app || {};
4var config = {
5    apiUrl: "https://api.example.com",
6    timeout: 5000
7};
8var currentUser = null;
9
10// Modern migration
11const APP_NAMESPACE = window.APP_NAMESPACE || {};
12const CONFIG = Object.freeze({
13    API_URL: "https://api.example.com",
14    TIMEOUT: 5000
15});
16let currentUser = null; // Will be reassigned
17
18// MIGRATION PATTERN 2: Fixing loop issues
19// Problem: var in loops with closures
20for (var i = 0; i < 3; i++) {
21    setTimeout(() => console.log(i), 100); // 3, 3, 3
22}
23
24// Solution 1: Use let
25for (let i = 0; i < 3; i++) {
26    setTimeout(() => console.log(i), 100); // 0, 1, 2
27}
28
29// Solution 2: IIFE with var (legacy fix)
30for (var i = 0; i < 3; i++) {
31    (function(j) {
32        setTimeout(() => console.log(j), 100); // 0, 1, 2
33    })(i);
34}
35
36// MIGRATION PATTERN 3: Global variable containment
37// Before: Polluting global scope
38var globalData = {};
39function init() {
40    globalData.users = [];
41}
42
43// After: Module pattern
44const App = (function() {
45    const privateData = {};
46    
47    return {
48        init: function() {
49            privateData.users = [];
50        },
51        getUsers: function() {
52            return privateData.users;
53        }
54    };
55})();
56
57// BEST PRACTICE 1: Declaration preferences
58// ✅ Preferred order: const → let → avoid var → avoid globals
59const API_BASE = "https://api.example.com"; // Use const by default
60let isLoading = false;                       // Use let for reassignables
61// var oldVariable = "avoid";               // Avoid var in new code
62// implicitGlobal = "never";                // Never use implicit globals
63
64// BEST PRACTICE 2: Naming conventions
65const CONSTANT_VALUES = "UPPER_SNAKE_CASE";
66let regularVariables = "camelCase";
67var legacyVars = "camelCase";
68
69// BEST PRACTICE 3: Scope minimization
70function goodPractice() {
71    const MAX_RETRIES = 3; // Declare close to usage
72    
73    for (let i = 0; i < MAX_RETRIES; i++) {
74        const attemptMessage = `Attempt ${i + 1}`;
75        console.log(attemptMessage);
76    }
77}

JavaScript Variables Comprehensive FAQ

When should I use var vs let vs const?

Use const by default for all variables that won't be reassigned. Use let when you need to reassign a variable. Avoid var in new code - only use it when maintaining legacy codebases.

What's the main difference between function scope and block scope?

Function scope (var) means the variable is accessible throughout the entire function. Block scope (let/const) means the variable is only accessible within the specific block (if, for, while, etc.) where it's declared.

Why are global variables considered bad practice?

Global variables can cause naming conflicts, make code harder to test and debug, create hidden dependencies, and lead to unpredictable behavior when modified from multiple places in your code.

Can I make objects and arrays truly immutable with const?

No, const only prevents reassignment of the variable itself. To make objects/arrays immutable, use Object.freeze() for shallow immutability or implement deep freezing for complete immutability.

What is the temporal dead zone (TDZ)?

The TDZ is the period between entering a scope and the actual declaration of a let or const variable. Accessing variables in the TDZ causes a ReferenceError. This helps catch bugs by preventing access before initialization.

How do I migrate legacy var code to modern let/const?

1. Replace var with const for variables that don't change. 2. Replace var with let for variables that need reassignment. 3. Fix any scope issues that arise from the change to block scoping. 4. Use linters to help identify problematic patterns.

What happens if I mix different variable types?

You can use different variable types together, but be mindful of their scoping rules. let and const are block-scoped while var is function-scoped. This can lead to unexpected behavior if you're not careful about scope boundaries.

Are there performance differences between var, let, and const?

In modern JavaScript engines, the performance differences are negligible. The primary reasons for choosing between them are code clarity, maintainability, and preventing bugs, not performance optimization.