javascript-today

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:

  1. Does the value need to change?
    • No → use const
    • Yes → use let
  2. Never use var in modern code

Golden Rule: Use const by default, let when necessary, never var.