Module-02-Variables-and-Data-Types
2.1 Variables
š Table of Contents
- ā¢What Are Variables?
- ā¢var - The Original Way
- ā¢let - The Modern Way
- ā¢const - Constants
- ā¢Scope Differences
- ā¢Temporal Dead Zone (TDZ)
- ā¢Best Practices
What Are Variables?
A variable is a named container that stores a value in your program's memory. Think of it as a labeled box where you can put data.
// Creating a variable is like creating a labeled box
let userName = 'John'; // Box labeled "userName" contains "John"
let userAge = 25; // Box labeled "userAge" contains 25
Variable Operations:
| Operation | Description | Example |
|---|---|---|
| Declaration | Creating the variable name | let x; |
| Initialization | Giving initial value | let x = 5; |
| Assignment | Changing the value | x = 10; |
| Reading | Getting the value | console.log(x); |
Declaration vs Initialization vs Assignment:
// Declaration only (no value yet)
let firstName;
console.log(firstName); // undefined
// Declaration with initialization
let lastName = 'Doe';
console.log(lastName); // "Doe"
// Assignment (changing value)
lastName = 'Smith';
console.log(lastName); // "Smith"
// Combined declaration and initialization
let age = 30;
var - The Original Way
var is the original way to declare variables in JavaScript (pre-ES6). It's still valid but has quirks that make it less predictable.
Basic Usage:
var name = 'John';
var age = 30;
var isActive = true;
console.log(name); // "John"
console.log(age); // 30
console.log(isActive); // true
Key Characteristics of var:
1. Function Scope (Not Block Scope)
var is scoped to the nearest function, not the nearest block:
function example() {
if (true) {
var x = 10; // Scoped to the function, not the if block
}
console.log(x); // 10 - x is accessible here!
}
example();
// Contrast with global scope:
if (true) {
var y = 20; // Scoped to global (no function wrapper)
}
console.log(y); // 20 - y is global!
2. Hoisting (Declaration Moved to Top)
var declarations are "hoisted" to the top of their scope:
console.log(hoistedVar); // undefined (not an error!)
var hoistedVar = 'Hello';
console.log(hoistedVar); // "Hello"
// JavaScript interprets this as:
// var hoistedVar; ā Declaration hoisted
// console.log(hoistedVar); ā undefined
// hoistedVar = "Hello"; ā Assignment stays
// console.log(hoistedVar); ā "Hello"
3. Can Be Redeclared
var color = 'red';
var color = 'blue'; // No error! Redeclaration allowed
console.log(color); // "blue"
4. Creates Property on Global Object (When Global)
var globalVar = "I'm global";
console.log(window.globalVar); // "I'm global" (in browser)
Problems with var:
// Problem 1: No block scope leads to confusion
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i); // All print 3, not 0, 1, 2!
}, 100);
}
// Problem 2: Easy to accidentally redeclare
var user = 'John';
// ... 100 lines later
var user = 'Jane'; // Accidentally overwrote!
// Problem 3: Hoisting causes confusion
function confusing() {
console.log(x); // undefined instead of error
var x = 5;
}
let - The Modern Way
let was introduced in ES6 (2015) to fix the problems with var. It's the recommended way to declare variables that need to change.
Basic Usage:
let name = 'John';
let age = 30;
let isActive = true;
console.log(name); // "John"
age = 31; // Can reassign
console.log(age); // 31
Key Characteristics of let:
1. Block Scope
let is scoped to the nearest block (code between {}):
function example() {
if (true) {
let x = 10; // Scoped to the if block
console.log(x); // 10
}
// console.log(x); // ReferenceError: x is not defined
}
for (let i = 0; i < 3; i++) {
console.log(i); // 0, 1, 2
}
// console.log(i); // ReferenceError: i is not defined
2. No Hoisting (Actually, TDZ)
Technically let is hoisted, but it's not initialized until the declaration is reached:
// console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 10;
console.log(x); // 10
3. Cannot Be Redeclared in Same Scope
let color = 'red';
// let color = "blue"; // SyntaxError: Identifier 'color' has already been declared
// But you can declare in different scopes:
let y = 1;
if (true) {
let y = 2; // Different scope - allowed
console.log(y); // 2
}
console.log(y); // 1
4. Does NOT Create Global Object Property
let globalLet = "I'm not on window";
console.log(window.globalLet); // undefined (in browser)
let Solves var's Problems:
// Problem 1 SOLVED: Block scope works correctly
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i); // Correctly prints 0, 1, 2!
}, 100);
}
// Problem 2 SOLVED: Can't accidentally redeclare
let user = 'John';
// let user = "Jane"; // SyntaxError!
// Problem 3 SOLVED: TDZ gives helpful error
function clear() {
// console.log(x); // ReferenceError (helpful!)
let x = 5;
}
const - Constants
const declares variables whose reference cannot be reassigned. Introduced in ES6 alongside let.
Basic Usage:
const PI = 3.14159;
const COMPANY_NAME = 'TechCorp';
const MAX_SIZE = 100;
console.log(PI); // 3.14159
// PI = 3; // TypeError: Assignment to constant variable
Key Characteristics of const:
1. Must Be Initialized When Declared
const x = 10; // Valid
// const y; // SyntaxError: Missing initializer in const declaration
2. Cannot Be Reassigned
const value = 10;
// value = 20; // TypeError: Assignment to constant variable
const name = 'John';
// name = "Jane"; // TypeError
3. Block Scoped (Like let)
if (true) {
const x = 10;
console.log(x); // 10
}
// console.log(x); // ReferenceError
4. Subject to TDZ (Like let)
// console.log(x); // ReferenceError
const x = 10;
IMPORTANT: const Does NOT Mean Immutable!
const prevents reassignment, not mutation:
// Objects can be mutated
const person = { name: 'John', age: 30 };
person.age = 31; // Allowed! We're changing the property, not the reference
person.city = 'NYC'; // Allowed! Adding property
console.log(person); // { name: "John", age: 31, city: "NYC" }
// person = {}; // TypeError! Can't reassign the reference
// Arrays can be mutated
const numbers = [1, 2, 3];
numbers.push(4); // Allowed!
numbers[0] = 100; // Allowed!
console.log(numbers); // [100, 2, 3, 4]
// numbers = [5, 6, 7]; // TypeError! Can't reassign
When to Use const:
// Use const for:
const PI = 3.14159; // Mathematical constants
const API_URL = 'https://api.com'; // Configuration
const CONFIG = { debug: true }; // Objects that won't be reassigned
const DAYS = ['Mon', 'Tue', 'Wed']; // Arrays that won't be reassigned
const getUser = () => {}; // Functions
// The reference stays the same, even if contents change
Scope Differences
Understanding scope is crucial. Here's a comprehensive comparison:
Comparison Table:
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized as undefined) | Yes (but in TDZ) | Yes (but in TDZ) |
| Redeclarable | Yes | No | No |
| Reassignable | Yes | Yes | No |
| Global object property | Yes | No | No |
| TDZ | No | Yes | Yes |
| Must initialize | No | No | Yes |
Visual Scope Comparison:
// =============================================
// FUNCTION SCOPE (var) vs BLOCK SCOPE (let/const)
// =============================================
function scopeDemo() {
// Function creates a scope for all three
var functionVar = "I'm var";
let blockLet = "I'm let";
const blockConst = "I'm const";
if (true) {
// var: accessible, function scoped
var innerVar = 'var in if';
// let/const: only accessible in this block
let innerLet = 'let in if';
const innerConst = 'const in if';
console.log(innerVar); // "var in if"
console.log(innerLet); // "let in if"
console.log(innerConst); // "const in if"
}
console.log(innerVar); // "var in if" ā (function scoped)
// console.log(innerLet); // ReferenceError ā (block scoped)
// console.log(innerConst); // ReferenceError ā (block scoped)
}
Loop Scope:
// var in loops - shared variable
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log('var:', i), 100);
}
// Output: "var: 3", "var: 3", "var: 3" (all same!)
// let in loops - new variable each iteration
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log('let:', j), 100);
}
// Output: "let: 0", "let: 1", "let: 2" (correct!)
Nested Scopes:
let outer = 'outer';
function level1() {
let level1Var = 'level 1';
function level2() {
let level2Var = 'level 2';
// Can access all outer variables
console.log(outer); // "outer"
console.log(level1Var); // "level 1"
console.log(level2Var); // "level 2"
}
level2();
// console.log(level2Var); // ReferenceError
}
level1();
Temporal Dead Zone (TDZ)
The Temporal Dead Zone is the period between entering a scope and the variable declaration being processed.
Understanding TDZ:
{
// TDZ starts here for 'x'
// console.log(x); // ReferenceError: Cannot access 'x' before initialization
// console.log(x); // Still in TDZ
// console.log(x); // Still in TDZ
let x = 10; // TDZ ends here
console.log(x); // 10 - now accessible
}
TDZ Visualization:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā { ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā TEMPORAL DEAD ZONE for 'x' ā ā
ā ā - x exists but can't be accessed ā ā
ā ā - accessing x throws ReferenceError ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā let x = 10; ā TDZ ends, x is initialized ā
ā console.log(x); ā x is now accessible ā
ā } ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
TDZ with var Comparison:
// var - hoisted and initialized as undefined
console.log(varVariable); // undefined (no error)
var varVariable = 'Hello';
// let - hoisted but in TDZ until declaration
// console.log(letVariable); // ReferenceError!
let letVariable = 'Hello';
// const - hoisted but in TDZ until declaration
// console.log(constVariable); // ReferenceError!
const constVariable = 'Hello';
Why TDZ Exists:
TDZ helps catch programming errors:
// Without TDZ (var behavior) - bugs hide
function buggy() {
console.log(x); // undefined - confusing!
var x = 10;
}
// With TDZ (let behavior) - bugs are caught
function better() {
// console.log(x); // ReferenceError - helpful!
let x = 10;
}
TDZ in Different Situations:
// TDZ with default parameters
// function broken(a = b, b = 1) {} // ReferenceError: b is in TDZ
function working(a = 1, b = a) {
return [a, b];
} // Works: a is initialized first
// TDZ with destructuring
// let { x = y, y = 1 } = {}; // ReferenceError: y is in TDZ
let { y = 1, x = y } = {}; // Works: y is initialized first
// TDZ in class
class MyClass {
// x = this.y; // Cannot access 'y' before initialization
y = 10;
x = this.y; // Works if y is declared first
}
Best Practices
1. Default to const
// Start with const - most variables don't need reassignment
const userName = 'John';
const config = { debug: true };
const numbers = [1, 2, 3];
// Change to let only when you need reassignment
let counter = 0;
counter++; // Need to reassign, so use let
2. Never Use var
// ā Avoid var
var oldWay = 'problematic';
// ā
Use let or const
let newWay = 'better';
const bestWay = 'recommended';
3. Declare at the Top of Scope
// ā
Good - declarations at top
function process(items) {
const result = [];
let current;
for (let i = 0; i < items.length; i++) {
current = items[i];
result.push(current * 2);
}
return result;
}
4. One Variable Per Declaration
// ā Multiple declarations on one line
let a = 1,
b = 2,
c = 3;
// ā
One per line - easier to read and modify
let a = 1;
let b = 2;
let c = 3;
5. Use Meaningful Names
// ā Cryptic names
const x = 86400;
let t = true;
// ā
Descriptive names
const SECONDS_PER_DAY = 86400;
let isUserLoggedIn = true;
Quick Decision Guide:
Start with: const
ā
Need to reassign?
ā
YES ā use let
ā
NO ā keep const
Never use var!
šÆ Key Takeaways
- ā¢var - Function scoped, hoisted, can be redeclared (avoid using)
- ā¢let - Block scoped, TDZ applies, cannot be redeclared (use for reassignable variables)
- ā¢const - Block scoped, TDZ applies, cannot be reassigned (use as default)
- ā¢TDZ prevents accessing let/const before declaration (catches bugs)
- ā¢Always use const by default, switch to let when reassignment is needed
ā”ļø Next Topic
Continue to 2.2 Data Types to learn about the different types of values JavaScript can work with!