JavaScript Course
JavaScript Crash Course
The Essential Reference Guide — Modern ES2024
1. Variables & Types
JavaScript has three variable declarations: const, let, and var. Use const by default, let when the value needs to change, and never var in modern code.
// const — block-scoped, cannot be reassigned
const name = "Ayman";
const score = 95;
const active = true;
// let — block-scoped, can be reassigned
let count = 0;
count = count + 1;
// Primitive types
const str = "Hello"; // string
const num = 42; // number (no int/float distinction)
const bool = true; // boolean
const n = null; // null — intentional absence
const u = undefined; // undefined — not yet assigned
const big = 9007199254740991n; // BigInt
// Type checking
typeof "hello" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof null // "object" — known JS quirk
typeof [] // "object" — use Array.isArray() instead
// Template literals
const msg = `Hello, ${name}! Score: ${score}`;
const multiline = `
Line 1
Line 2
`;Always use === not ==
== does type coercion and produces surprising results. "5" == 5 is true. Use === (strict equality) everywhere — it checks both value and type.
2. Functions
// Function declaration — hoisted (can call before definition)
function greet(name) {
return `Hello, ${name}!`;
}
// Function expression — not hoisted
const square = function(n) { return n * n; };
// Arrow function — concise, no own `this`
const double = n => n * 2;
const add = (a, b) => a + b;
const square2 = n => {
const result = n * n;
return result;
};
// Default parameters
function power(base, exp = 2) {
return base ** exp;
}
// Rest parameters — collects remaining args into array
function sum(...nums) {
return nums.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
// Immediately Invoked Function Expression (IIFE)
(function() {
// runs immediately, private scope
})();3. Arrays
const nums = [1, 2, 3, 4, 5];
// Access and modify
nums[0]; // 1 — first element
nums.push(6); // add to end → [1,2,3,4,5,6]
nums.pop(); // remove from end
nums.unshift(0); // add to start
nums.shift(); // remove from start
nums.length; // 5
// Higher-order array methods — the most important ones
const doubled = nums.map(n => n * 2);
const evens = nums.filter(n => n % 2 === 0);
const total = nums.reduce((acc, n) => acc + n, 0);
const found = nums.find(n => n > 3); // 4
const hasLarge = nums.some(n => n > 4); // true
const allPos = nums.every(n => n > 0); // true
const idx = nums.indexOf(3); // 2
const sorted = [3,1,2].sort((a,b) => a - b); // [1,2,3]
// Spread — copy or combine arrays
const copy = [...nums];
const combined = [...nums, ...[6,7]];
// Destructuring
const [first, second, ...rest] = nums;
// first=1, second=2, rest=[3,4,5]
// for...of loop
for (const n of nums) {
console.log(n);
}4. Objects
// Object literal
const student = {
name: "Ayman",
grade: 95,
active: true,
letterGrade() {
return this.grade >= 90 ? "A" : "B";
}
};
// Access
student.name; // "Ayman"
student["grade"]; // 95 — bracket notation
student.letterGrade(); // "A"
// Destructuring
const { name, grade } = student;
const { name: studentName } = student; // rename
// Spread — copy and extend
const updated = { ...student, grade: 98 };
// Shorthand property names
const x = 10, y = 20;
const point = { x, y }; // same as { x: x, y: y }
// Optional chaining — safe nested access
const city = student?.address?.city; // undefined, not error
// Nullish coalescing — fallback for null/undefined only
const display = student.nickname ?? "No nickname";
// Object methods
Object.keys(student); // ["name", "grade", "active", ...]
Object.values(student); // ["Ayman", 95, true, ...]
Object.entries(student); // [["name","Ayman"], ["grade",95], ...]5. Control Flow
// if / else if / else
const score = 85;
if (score >= 90) console.log("A");
else if (score >= 80) console.log("B");
else console.log("F");
// Ternary
const result = score >= 60 ? "Pass" : "Fail";
// Switch
switch (score >= 90) {
case true: console.log("A"); break;
default: console.log("Other");
}
// Loops
for (let i = 0; i < 5; i++) console.log(i);
let i = 0;
while (i < 3) { console.log(i++); }
// for...of — iterate array values
for (const item of ["a", "b", "c"]) console.log(item);
// for...in — iterate object keys
const obj = { a: 1, b: 2 };
for (const key in obj) console.log(key, obj[key]);
// Falsy values in JS: false, 0, "", null, undefined, NaN
if ("") console.log("never runs");
if (0) console.log("never runs");
if ("hello") console.log("truthy — runs");6. DOM Manipulation
The Document Object Model (DOM) represents your HTML as a tree of JavaScript objects. JavaScript can read, create, modify, and delete any element on the page.
// Selecting elements
const el = document.getElementById("myId");
const el2 = document.querySelector(".my-class"); // first match
const all = document.querySelectorAll("p"); // NodeList
// Reading and writing content
el.textContent = "New text"; // safe — no HTML parsing
el.innerHTML = "<strong>Bold</strong>"; // parses HTML
el.value; // for input elements
// Modifying attributes and styles
el.setAttribute("href", "https://example.com");
el.getAttribute("href");
el.style.color = "#f97316";
el.style.display = "none";
// CSS classes
el.classList.add("active");
el.classList.remove("hidden");
el.classList.toggle("open");
el.classList.contains("active"); // true/false
// Creating and inserting elements
const div = document.createElement("div");
div.textContent = "New element";
div.classList.add("card");
document.body.appendChild(div);
el.insertAdjacentHTML("beforeend", "<p>Added</p>");
// Removing elements
el.remove();7. Events
// addEventListener — preferred approach
const btn = document.querySelector("#myBtn");
btn.addEventListener("click", function(e) {
console.log("Clicked!", e.target);
});
// Common events
el.addEventListener("click", handler);
el.addEventListener("input", e => console.log(e.target.value));
el.addEventListener("submit", e => { e.preventDefault(); });
el.addEventListener("keydown", e => console.log(e.key));
el.addEventListener("mouseover", handler);
el.addEventListener("DOMContentLoaded", handler);
// Event delegation — listen on parent, target children
document.querySelector(".list").addEventListener("click", e => {
if (e.target.matches("li")) {
console.log("List item clicked:", e.target.textContent);
}
});
// Remove listener
const handler = () => console.log("once");
btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);
// Run only once
btn.addEventListener("click", handler, { once: true });Best Practice
Always call e.preventDefault() on form submit handlers to stop the page from reloading. Use event delegation on parent elements instead of adding listeners to every child.
8. Promises & Fetch
// Promise — represents a future value
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) resolve("Data loaded");
else reject(new Error("Load failed"));
});
promise
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log("Done"));
// Fetch API — HTTP requests
fetch("https://api.example.com/users")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
// POST request with fetch
fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Ayman", score: 95 })
});
// Promise.all — run multiple in parallel
const [users, posts] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json()),
]);9. Async / Await
async/await is syntactic sugar over Promises — it makes asynchronous code read like synchronous code. An async function always returns a Promise.
// async function — always returns a Promise
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP error: ${res.status}`);
const user = await res.json();
return user;
} catch (err) {
console.error("Failed to load user:", err);
throw err; // re-throw if caller needs to handle it
}
}
// Arrow async function
const getData = async (url) => {
const res = await fetch(url);
return res.json();
};
// await at top level (ES2022 modules)
const data = await getData("/api/data");
// Sequential vs parallel
// Sequential (slower — waits for each)
const a = await fetchA();
const b = await fetchB();
// Parallel (faster — runs simultaneously)
const [a, b] = await Promise.all([fetchA(), fetchB()]);10. ES6+ Features
// Destructuring (ES6)
const [a, b] = [1, 2];
const { name, age = 18 } = person; // default value
function greet({ name, role = "student" }) { return `${name} is a ${role}`; }
// Spread and rest (ES6)
const arr2 = [...arr1, 4, 5];
const obj2 = { ...obj1, newProp: "value" };
// Map — key/value pairs (any key type)
const map = new Map();
map.set("name", "Ayman");
map.get("name");
map.has("name");
map.delete("name");
// Set — unique values
const set = new Set([1, 2, 2, 3]); // {1, 2, 3}
set.add(4);
set.has(2); // true
// Symbol — unique identifier
const id = Symbol("id");
// Nullish coalescing and optional chaining
const val = data ?? "default";
const city = user?.address?.city;
// Logical assignment (ES2021)
a ||= "default"; // a = a || "default"
a &&= 42; // a = a && 42
a ??= "fallback"; // a = a ?? "fallback"11. Classes
class Student {
// Private fields (ES2022)
#grade;
constructor(name, grade) {
this.name = name;
this.#grade = grade;
}
// Getter
get grade() { return this.#grade; }
// Setter
set grade(val) {
if (val < 0 || val > 100) throw new Error("Invalid grade");
this.#grade = val;
}
letterGrade() {
if (this.#grade >= 90) return "A";
if (this.#grade >= 80) return "B";
return "F";
}
// Static method — called on the class, not instance
static compare(a, b) { return a.grade - b.grade; }
}
// Inheritance
class GradStudent extends Student {
constructor(name, grade, thesis) {
super(name, grade); // call parent constructor
this.thesis = thesis;
}
letterGrade() {
return super.letterGrade() + "+";
}
}
const s = new Student("Ayman", 95);
console.log(s.letterGrade()); // "A"
s instanceof Student; // true12. Modules & Error Handling
// ── Modules (ES6) ──
// math.js — named exports
export const PI = 3.14159;
export function square(n) { return n * n; }
// app.js — named import
import { PI, square } from "./math.js";
// Default export (one per file)
export default class Calculator { /* ... */ }
// Default import
import Calculator from "./Calculator.js";
// Dynamic import — load on demand
const { square } = await import("./math.js");
// ── Error Handling ──
try {
const data = JSON.parse(invalidJSON);
} catch (err) {
console.error(err.name, err.message);
} finally {
console.log("Always runs");
}
// Custom error types
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
throw new ValidationError("Email is required");