📦 What is a variable?
A labeled box that stores a value so you can use it later.
JavaScript gives you three ways to create one: var, let, and const.
js
var oldWay = "I still work but I'm outdated";
let score = 10;
score = 20; // ✅ totally fine, let allows you to reassign.
const birthYear = 2003;
// birthYear = 2004; // 🚫 throws an error, const doesn't allow reassignment
🕰️ var — old-school, function-scoped,it leaks out of blocks like if statements and loops.Var usually causes weird bugs. Don't use it in new code.
✅ let — modern, block-scoped, use when the value will change.
🔒 const — modern, block-scoped, use by default unless you know the value needs to change.
🧠 Scope: where a variable is allowed to "live".
let and const being block-scoped means → they only exist inside the { } they were created in.
js
if (true) {
var leaky = "I escape the block!";
const trapped = "I stay inside the block";
let alsoTrapped = "Me too, I stay inside the block";
// ✅ all three work fine HERE, still inside the block
console.log(leaky); // "I escape the block!"
console.log(trapped); // "I stay inside the block"
console.log(alsoTrapped); // "Me too, I stay inside the block"
}
// Outside the block now:
console.log(leaky); // ✅ "I escape the block!" — var doesn't respect the block
console.log(trapped); // ❌ ReferenceError — const is block-scoped
console.log(alsoTrapped); // ❌ ReferenceError — let is block-scoped too
🔑 Takeaway: let and const behave identically on scope — both are trapped inside their block. They only differ on reassignment (next section).
🍾 The const bottle analogy — identity vs. contents
const locks the name's connection to a specific bottle — not what's poured inside that bottle.
Picture three layers:
The name (aqua) — the label
The bottle — the actual container in memory
The contents — whatever liquid is inside right now
js
const aqua = { content: "water" };
This line only runs once — it creates a bottle, fills it with water, and permanently glues the name aqua to that one bottle. It's a snapshot of the starting contents, not a permanent rule about what the bottle must always hold.
You CAN refill the same bottle — editing contents is always allowed, even with const:
js
aqua.content = "wine";
console.log(aqua); // { content: "wine" } — same bottle, just refilled
Same bottle, same name, only the liquid inside changed. aqua never stopped pointing at that exact bottle.
You CANNOT swap in a whole new bottle — that's reassignment, and const blocks it:
js
aqua = { content: "juice" }; // ❌ error — this is a brand NEW bottle, not a refill
This fails because you're not pouring juice into the existing bottle — you're trying to make the name aqua point to a completely different container. const only ever locked the name-to-bottle connection, and this line tries to break that connection.
🔑 One-line summary: const = "this name will always point to this exact bottle" — not "this bottle can never change what's inside it."
The same logic applies to arrays, since arrays are objects too:
js
const fruits = ["mango", "banana"];
fruits.push("avocado"); // ✅ allowed — editing contents
fruits[0] = "pineapple"; // ✅ allowed — editing contents
// fruits = ["new", "list"]; // ❌ error — swaps the whole array reference
🔓 let — same bottle rules, but the name can also switch bottles
let allows everything const allows on contents, plus it lets the name walk away and point at a completely different bottle:
js
let aqua = { content: "water" };
aqua.content = "wine"; // ✅ allowed — same bottle, refilled
console.log(aqua); // { content: "wine" }
aqua = { content: "juice" }; // ✅ allowed — a whole NEW bottle, same name reused
console.log(aqua); // { content: "juice" }
The two differences, side by side:
Refill the same bottle (edit contents)? Swap in a whole new bottle (reassign)?
const ✅ Yes ❌ No
let ✅ Yes ✅ Yes
🔑 One-line summary: both let you change what's inside the bottle. The only difference is whether the name is allowed to walk away and grab a different bottle entirely — const says no, let says go for it.
🌀 Hoisting
Before your code runs, JavaScript does a pass over the script and "hoists" (lifts) variable declarations to the top of their scope — but not the value assignment. Only the declaration moves up; the assignment stays where you wrote it.
js
console.log(a); // undefined — not an error!
var a = 5;
console.log(a); // 5
Behind the scenes, this is treated like:
js
var a; // declaration hoisted to the top, auto-set to undefined
console.log(a); // undefined
a = 5; // assignment happens where you originally wrote it
console.log(a); // 5
let and const get hoisted too — but they don't default to undefined. Instead they sit in the Temporal Dead Zone (TDZ): the variable technically exists, but touching it before its declaration line is illegal.
js
console.log(b); // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 5;
js
{
// 🚧 TDZ starts here — b "exists" but is untouchable
console.log(b); // ❌ error, still in the dead zone
let b = 5; // 🚧 TDZ ends here
console.log(b); // ✅ 5, totally fine now
}
🔑 One-line summary: var gets hoisted and pre-filled with undefined. let/const get hoisted but stay locked in the dead zone until your code actually reaches their declaration line.
🔁 Redeclaring
js
var x = 1;
var x = 2; // ✅ allowed, messy but works
let y = 1;
// let y = 2; // ❌ error — can't redeclare a let in the same scope
🎯 The verdict on all three: default to const. Switch to let only when you know the value (or the whole bottle) needs to change. Avoid var entirely in new code.
🏷️ What is a data type?
JavaScript sorts types into two families: primitive (simple, single values) and reference (complex, made of multiple values).
🧱 Primitive types
js
let str = "hello"; // 🔤 String
let num = 42; // 🔢 Number
let isTrue = true; // ✅ Boolean
let nothing = null; // 🚫 Null — "empty on purpose"
let notSet; // ❓ Undefined — "not given a value yet"
let big = 123n; // 🐘 BigInt
let sym = Symbol("id"); // 🔮 Symbol — guaranteed-unique value
🧩 Reference types
js
let person = { name: "Brian", age: 22 }; // 🗂️ Object
let fruits = ["mango", "banana"]; // 📋 Array (secretly an object)
function greet() {} // ⚙️ Function (also technically an object)
⚡ Special number values
js
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log("abc" * 2); // NaN
console.log(typeof NaN); // "number" 🤯
🐛 Famous JS quirk: typeof null returns "object" — a decades-old bug that's now permanent. Just know it exists.
🔍 Checking types
js
console.log(typeof "hi"); // "string"
console.log(typeof 5); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof {}); // "object"
console.log(typeof []); // "object" — arrays are secretly objects
console.log(typeof function(){}); // "function"
🌊 Dynamic typing
js
let thing = "hello"; // starts as a String
thing = 5; // now it's a Number, JS doesn't complain
⚙️ What is an operator?
1️⃣ Arithmetic
js
let total = 5 + 3; // 8
let diff = 5 - 3; // 2
let product = 5 * 3; // 15
let split = 5 / 3; // 1.666...
let leftover = 5 % 3; // 2 — modulus
let power = 5 ** 2; // 25 — exponent
2️⃣ Assignment
js
let x = 10;
x += 5; // 15
x -= 3; // 12
x = 2; // 24
x /= 4; // 6
x %= 5; // 1
x *= 2; // 1
3️⃣ Comparison
js
console.log(5 == "5"); // true — loose, ignores type
console.log(5 === "5"); // false — strict, checks type too ✅ prefer this
console.log(5 != "5"); // false
console.log(5 !== "5"); // true ✅ prefer this
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 >= 5); // true
console.log(5 <= 4); // false
4️⃣ Logical
js
let hasLaptop = true;
let hasWifi = false;
console.log(hasLaptop && hasWifi); // false — AND
console.log(hasLaptop || hasWifi); // true — OR
console.log(!hasLaptop); // false — NOT
5️⃣ Increment / decrement
js
let count = 5;
count++; // 6
count--; // 5
let n = 5;
console.log(n++); // logs 5, THEN increments
console.log(++n); // increments FIRST, then logs 7
6️⃣ Ternary
js
let age = 20;
let canVote = age >= 18 ? "Yes" : "No"; // "Yes"
7️⃣ Nullish coalescing (??)
js
let username = null;
let displayName = username ?? "Guest"; // "Guest"
let score = 0;
console.log(score ?? 100); // 0 — kept, since 0 isn't null/undefined
console.log(score || 100); // 100 — || wrongly treats 0 as falsy ⚠️
8️⃣ Optional chaining (?.)
js
let user = { profile: { name: "Brian" } };
console.log(user?.profile?.name); // "Brian"
console.log(user?.settings?.theme); // undefined — no crash 🙌
9️⃣ Bitwise
js
console.log(5 & 1); // 1 — AND
console.log(5 | 1); // 5 — OR
console.log(5 ^ 1); // 4 — XOR
console.log(~5); // -6 — NOT
console.log(5 << 1); // 10 — shift left
console.log(5 >> 1); // 2 — shift right
🔟 Keyword operators
js
console.log(typeof "hi"); // "string"
let arr = [1, 2, 3];
console.log(arr instanceof Array); // true
let obj = { name: "Brian" };
console.log("name" in obj); // true
delete obj.name;
console.log(obj); // {}
1️⃣1️⃣ Comma operator
js
let a = (1 + 2, 3 + 4); // a = 7 — only the LAST value is kept
🎁 TL;DR
Category Members
🔤 Declarations var, let, const
🧱 Primitive types String, Number, Boolean, Null, Undefined, BigInt, Symbol
🧩 Reference types Object, Array, Function
⚙️ Operators Arithmetic, Assignment, Comparison, Logical, Increment/Decrement, Ternary, Nullish Coalescing, Optional Chaining, Bitwise, Keyword ops, Comma
Top comments (1)
One thing - the
%operator is the 'remainder' operator, not 'modulus'. JS does not have a built-in modulus operation. Remainder and modulus operations are similar but NOT the same.developer.mozilla.org/en-US/docs/W...