Variables Deep Dive
Variables are containers for storing data values. Understanding how to declare and use variables correctly is fundamental to writing good JavaScript code.
What are Variables?
Variables let you store and reuse values throughout your program:
let message = 'Hello, World!';
console.log(message); // Hello, World!
message = 'Welcome!';
console.log(message); // Welcome!
Think of variables as labeled boxes where you can:
- Store a value
- Read the value later
- Update the value (for some variable types)
Three Ways to Declare Variables
JavaScript has three keywords for declaring variables:
| Keyword | Mutable | Scope | Hoisting | When to Use |
|---|---|---|---|---|
const |
❌ No | Block | ❌ No | Default choice - use for values that won’t change |
let |
✅ Yes | Block | ❌ No | When you need to reassign the variable |
var |
✅ Yes | Function | ✅ Yes | ⚠️ Avoid - legacy syntax |
const - Immutable Variables
Use const for variables that won’t be reassigned:
Basic Usage
const name = 'Alice';
const age = 25;
const pi = 3.14159;
console.log(name); // Alice
Cannot Reassign
const score = 100;
score = 200; // ❌ TypeError: Assignment to constant variable
Must Initialize
const count; // ❌ SyntaxError: Missing initializer
count = 10; // Too late!
const count = 10; // ✅ Correct - initialize when declaring
Objects and Arrays Can Still Change
Important: const prevents reassignment, but objects/arrays can still be modified:
const person = { name: 'Bob', age: 30 };
// ✅ Can modify properties
person.age = 31;
person.city = 'New York';
console.log(person); // { name: 'Bob', age: 31, city: 'New York' }
// ❌ Cannot reassign entire object
person = { name: 'Alice' }; // TypeError!
const numbers = [1, 2, 3];
// ✅ Can modify array
numbers.push(4);
console.log(numbers); // [1, 2, 3, 4]
// ❌ Cannot reassign entire array
numbers = [5, 6, 7]; // TypeError!
let - Mutable Variables
Use let when you need to reassign the variable:
Basic Usage
let count = 0;
console.log(count); // 0
count = 5;
console.log(count); // 5
count = count + 1;
console.log(count); // 6
Common Patterns
// Counters
let total = 0;
total = total + 10;
total += 5;
console.log(total); // 15
// Loop variables
for (let i = 0; i < 5; i++) {
console.log(i);
}
// Conditional reassignment
let message = 'Hello';
if (isLoggedIn) {
message = 'Welcome back!';
}
Can Declare Without Initializing
let result; // ✅ OK - undefined until assigned
if (someCondition) {
result = 'success';
} else {
result = 'failure';
}
console.log(result);
var - Legacy Syntax (Avoid)
var is the old way to declare variables. Avoid using it in modern code.
Why Avoid var?
// Problem 1: Function scope (not block scope)
if (true) {
var x = 10;
}
console.log(x); // 10 - leaked outside the block!
// Compare with let
if (true) {
let y = 10;
}
console.log(y); // ❌ ReferenceError: y is not defined
// Problem 2: Can redeclare
var count = 5;
var count = 10; // No error - confusing!
console.log(count); // 10
// Compare with let
let total = 5;
let total = 10; // ❌ SyntaxError: Identifier 'total' has already been declared
// Problem 3: Hoisting (covered below)
console.log(name); // undefined - weird!
var name = 'Alice';
Block Scope vs Function Scope
Block scope = variable only exists within { } curly braces
Function scope = variable exists throughout entire function
Block Scope (let and const)
function demo() {
let x = 1;
if (true) {
let x = 2; // Different variable!
console.log(x); // 2
}
console.log(x); // 1
}
demo();
Practical Example: Loop Variables
// ✅ Good - let is block-scoped
for (let i = 0; i < 3; i++) {
console.log(i); // 0, 1, 2
}
console.log(i); // ❌ ReferenceError: i is not defined
// ⚠️ var leaks out of loop
for (var j = 0; j < 3; j++) {
console.log(j); // 0, 1, 2
}
console.log(j); // 3 - leaked!
Nested Blocks
const name = 'Global';
function outer() {
const name = 'Outer';
{
const name = 'Block 1';
console.log(name); // Block 1
}
{
const name = 'Block 2';
console.log(name); // Block 2
}
console.log(name); // Outer
}
outer();
console.log(name); // Global
Hoisting
Hoisting = JavaScript moves declarations to the top of their scope before execution.
var is Hoisted
console.log(x); // undefined (not an error!)
var x = 10;
// Interpreted as:
var x; // Declaration hoisted
console.log(x); // undefined
x = 10; // Assignment stays in place
let and const are NOT Hoisted (Temporal Dead Zone)
console.log(y); // ❌ ReferenceError: Cannot access 'y' before initialization
let y = 10;
console.log(z); // ❌ ReferenceError: Cannot access 'z' before initialization
const z = 20;
Function Hoisting
// ✅ Function declarations are hoisted
greet(); // Hello!
function greet() {
console.log('Hello!');
}
// ❌ Function expressions are NOT hoisted
sayHi(); // ❌ TypeError: sayHi is not a function
const sayHi = function() {
console.log('Hi!');
};
Variable Naming Rules
Rules (must follow)
// ✅ Valid names
let name = 'Alice';
let firstName = 'Bob';
let age2 = 25;
let _private = 'data';
let $jQuery = 'selector';
// ❌ Invalid names
let 2name = 'Alice'; // Cannot start with number
let first-name = 'Bob'; // No hyphens
let let = 'keyword'; // Cannot use reserved words
let first name = 'Eve'; // No spaces
Conventions (should follow)
// ✅ Good - camelCase for variables
let firstName = 'Alice';
let userAge = 25;
let isLoggedIn = true;
// ✅ Good - UPPERCASE for constants
const MAX_SIZE = 100;
const API_KEY = 'abc123';
const PI = 3.14159;
// ✅ Good - descriptive names
let userEmail = 'user@example.com';
let productPrice = 29.99;
let isValid = true;
// ❌ Bad - unclear abbreviations
let u = 'user@example.com';
let p = 29.99;
let f = true;
Reserved Words
Cannot use these as variable names:
break, case, catch, class, const, continue, debugger, default, delete,
do, else, export, extends, finally, for, function, if, import, in,
instanceof, let, new, return, super, switch, this, throw, try, typeof,
var, void, while, with, yield
Common Patterns
1. Swap Variables
let a = 5;
let b = 10;
// Old way - needs temporary variable
let temp = a;
a = b;
b = temp;
console.log(a, b); // 10, 5
// Modern way - destructuring
let x = 5;
let y = 10;
[x, y] = [y, x];
console.log(x, y); // 10, 5
2. Multiple Declarations
// ✅ One per line (more readable)
const firstName = 'Alice';
const lastName = 'Smith';
const age = 25;
// ✅ Multiple on one line (same type)
let x = 0, y = 0, z = 0;
// ❌ Avoid - mixed types
let name = 'Bob', age = 30, isActive = true;
3. Default Values
// Using || (watch out for 0, '', false)
let count = userInput || 0;
// Using ?? (better - only for null/undefined)
let count = userInput ?? 0;
// Conditional assignment
let message;
if (isError) {
message = 'Error occurred';
} else {
message = 'Success';
}
// Ternary operator
let message = isError ? 'Error occurred' : 'Success';
4. Accumulator Pattern
const prices = [10, 20, 30, 40];
let total = 0;
for (const price of prices) {
total += price;
}
console.log(total); // 100
Best Practices
✅ DO:
// Use const by default
const MAX_USERS = 100;
const user = { name: 'Alice' };
// Use let only when you need to reassign
let count = 0;
count++;
// Declare at the narrowest scope possible
function calculate() {
const result = 42; // Only exists in this function
return result;
}
// Initialize variables when declaring
const name = 'Alice';
let count = 0;
// Use descriptive names
const userEmail = 'user@example.com';
const isAuthenticated = true;
❌ DON’T:
// Don't use var
var x = 10; // Use let or const instead
// Don't declare without initialization if possible
let result;
// ... 50 lines later
result = calculateSomething();
// Don't use single letters (except in loops)
let x = 'user@example.com';
let f = true;
// Don't reassign const
const PI = 3.14;
PI = 3.14159; // Error!
// Don't pollute global scope
x = 10; // Forgot let/const - creates global variable!
Common Mistakes
1. Forgetting to Declare
// ❌ Bad - creates global variable
count = 10;
console.log(count); // Works, but bad!
// ✅ Good - explicit declaration
let count = 10;
console.log(count);
2. Confusing const with Immutability
// ❌ Wrong assumption
const person = { name: 'Alice' };
person.age = 25; // ✅ This works! Object is mutable
console.log(person); // { name: 'Alice', age: 25 }
// const prevents reassignment, not modification
const numbers = [1, 2, 3];
numbers.push(4); // ✅ This works!
console.log(numbers); // [1, 2, 3, 4]
3. Using let When const Would Work
// ❌ Unnecessary let
let name = 'Alice';
console.log(name);
// name is never reassigned
// ✅ Better - use const
const name = 'Alice';
console.log(name);
4. Variable Shadowing
const name = 'Global';
function greet() {
const name = 'Local'; // Shadows global name
console.log(name); // Local
}
greet();
console.log(name); // Global
// Can be confusing - use different names
Summary
| Feature | const | let | var |
|---|---|---|---|
| Reassignable | ❌ No | ✅ Yes | ✅ Yes |
| Scope | Block | Block | Function |
| Hoisted | ❌ No (TDZ) | ❌ No (TDZ) | ✅ Yes |
| Redeclarable | ❌ No | ❌ No | ✅ Yes |
| Modern | ✅ Yes | ✅ Yes | ❌ Legacy |
Decision Tree:
- Does the value need to change?
- No → use
const - Yes → use
let
- No → use
- Never use
varin modern code
Golden Rule: Use const by default, let when necessary, never var.
Next Article: Template Literals