Home/ JavaScript/ Crash Course

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;          // true

12. 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");