Developer Bootcamp · 14 Modules

JavaScript:
Absolute Beginner
to Production-Ready

A comprehensive, project-based guide by a Senior JavaScript Engineer. Practical, modern, and structured like a real-world developer bootcamp — from your first console.log to shipping production code.

14+
Sections
3
Projects
100+
Code Examples
ES2024
Modern JS
§ 01

Introduction to JavaScript

What it is · How it works · Setup
What is JavaScript?

JavaScript (JS) is the world's most widely used programming language. Originally built to make web pages interactive — button clicks, form validation, dropdown menus — today it runs everywhere: browsers, servers (Node.js), mobile apps (React Native), and desktop apps (Electron).

Mental model: HTML is the skeleton, CSS is the skin, and JavaScript is the muscles and brain of a webpage.
JavaScript Engines

Every browser ships with a JS engine — a program that reads and runs your JavaScript. V8 uses JIT (Just-In-Time) compilation — it compiles JS to machine code at runtime for speed.

BrowserEngineNote
Chrome / EdgeV8Also used in Node.js — most dominant
FirefoxSpiderMonkeyMozilla's engine, highly standards-compliant
SafariJavaScriptCore (Nitro)Apple's engine for WebKit
How JS Interacts with HTML & CSS

JavaScript can access and modify the DOM (Document Object Model) — a live tree representation of the HTML page. It can also read and write CSS styles dynamically.

index.html
<!-- Always load scripts at the bottom of <body> or use defer -->
<body>
  <h1 id="title">Hello, World!</h1>
  <button id="btn">Click me</button>
  <script src="app.js"></script>
</body>
app.js
const btn = document.getElementById('btn');

btn.addEventListener('click', function () {
  document.getElementById('title').textContent = 'You clicked it!';
});
Setting Up Your Environment
Install Node.js (LTS version)

Download from nodejs.org — this gives you node and npm commands.

Install VS Code

Download from code.visualstudio.com. Install the ESLint and Prettier extensions.

Create your first project
terminal
mkdir my-js-project
cd my-js-project
code .          # opens VS Code in this folder

# Verify Node.js is installed:
node --version    # e.g., v22.0.0
npm --version     # e.g., 10.8.0
node              # opens the REPL — try: console.log("Hello!")
§ 02

JavaScript Basics — The Foundation

Syntax · Variables · Data Types · Operators
Variables: var, let, const
Rule of thumb: Use const by default. Switch to let only when you need to reassign. Never use var.
javascript
// ❌ var — OLD way. Avoid it. Has confusing scoping rules.
var oldStyle = "avoid me";

// ✅ let — for values that will CHANGE
let score = 0;
score = 10;
score = score + 5;  // score is now 15

// ✅ const — for values that will NOT change (use this by default!)
const PI = 3.14159;
const APP_NAME = "MyApp";
// PI = 3; // ❌ TypeError: Assignment to constant variable

// Why not var? — var is function-scoped, NOT block-scoped
if (true) {
  var leakyVar = "I leak outside the block!";
  let safeVar = "I stay inside.";
}
console.log(leakyVar); // "I leak!" — unexpected bug!
console.log(safeVar);  // ❌ ReferenceError — correct behavior
Data Types
string number boolean null undefined symbol bigint object array
javascript — primitives
// String — text, always in quotes
const firstName = "Alice";
const template = `Hi, ${firstName}!`;  // Template literal (preferred)

// Number — integers and decimals
const age = 28;
const price = 9.99;
const notANumber = NaN;      // result of invalid math: "abc" * 2

// Boolean
const isLoggedIn = true;

// null — intentional absence of value (you set this)
const selectedItem = null;

// undefined — variable declared but not assigned
let userInput;
console.log(userInput); // undefined

// BigInt — for integers larger than 2^53 - 1
const bigNumber = 9007199254740991n;

// Checking types
typeof "hello"      // "string"
typeof 42           // "number"
typeof null         // "object" ⚠️ famous JS bug — null is NOT an object
typeof []           // "object" — arrays are objects too
Common Gotcha: typeof null === "object" is a well-known JavaScript bug that exists for historical reasons. Always use === null to check for null directly.
javascript — reference vs value
// Primitives: copied by VALUE
let a = 10;
let b = a;  // b gets a copy of the value
b = 20;
console.log(a); // 10 — a is unchanged ✅

// Objects: copied by REFERENCE
const obj1 = { name: "Alice" };
const obj2 = obj1;  // obj2 points to the SAME object in memory!
obj2.name = "Bob";
console.log(obj1.name); // "Bob" — obj1 was also changed! 😱

// ✅ To make a true copy, use the spread operator:
const obj3 = { ...obj1 }; // independent copy
Operators
javascript — comparison & logical
// ✅ Always use === (strict equality) — checks VALUE and TYPE
5 === 5      // true
5 === "5"   // false — different types!
5 == "5"    // true  — ⚠️ AVOID loose equality, coerces types

// Logical operators
const isAdmin = true;
const isActive = false;
isAdmin && isActive;   // false — AND (both must be true)
isAdmin || isActive;   // true  — OR (at least one true)
!isAdmin;             // false — NOT

// Short-circuit: if username is falsy, use "Guest"
const username = null;
const displayName = username || "Guest"; // "Guest"

// Falsy values: false, 0, "", null, undefined, NaN
// Everything else is truthy
§ 03

Control Flow

if/else · switch · Loops · break/continue
if / else / Ternary
javascript
const temperature = 22;

if (temperature > 30) {
  console.log("Hot! Wear sunscreen.");
} else if (temperature > 20) {
  console.log("Warm. T-shirt is fine.");  // ← this runs
} else {
  console.log("Cold! Wear a coat.");
}

// Ternary operator — shorthand for simple if/else
const access = temperature > 20 ? "Enjoy outdoors!" : "Stay inside.";
console.log(access); // "Enjoy outdoors!"
Loops
javascript
// for loop — when you know the number of iterations
for (let i = 1; i <= 5; i++) {
  console.log(`Iteration: ${i}`);
}

// for...of — cleaner array iteration (preferred!)
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
  console.log(fruit);
}

// for...in — iterate over object keys
const person = { name: "Alice", age: 28, city: "Kigali" };
for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}

// while — when you don't know the count upfront
let attempts = 0;
while (attempts < 3) {
  console.log(`Attempt ${attempts + 1}`);
  attempts++;
}

// break & continue
for (let i = 0; i < 10; i++) {
  if (i === 5) break;      // stop at 5
  if (i % 2 === 0) continue; // skip even numbers
  console.log(i);             // prints: 1, 3
}
§ 04

Functions

Declarations · Arrow Functions · Callbacks · Closures
Function Declaration vs Arrow Functions
javascript
// DECLARATION — hoisted: can be called before it's defined
function greet(name) {
  return `Hello, ${name}!`;
}

// ARROW FUNCTIONS (ES6) — modern standard
const add = (a, b) => a + b;              // implicit return
const double = n => n * 2;               // single param, no parens needed
const sayHi = () => "Hi!";               // no params, empty parens required

// Default parameters
function createUser(name, role = "viewer", isActive = true) {
  return { name, role, isActive };  // shorthand: name: name
}
createUser("Alice", "admin"); // { name: "Alice", role: "admin", isActive: true }
createUser("Bob");            // { name: "Bob", role: "viewer", isActive: true }
Scope and Closures

A closure is when an inner function "remembers" variables from its outer scope even after the outer function has finished executing. This is one of JS's most powerful — and initially confusing — features.

javascript — closures
function makeCounter() {
  let count = 0; // this variable is "enclosed" in the returned function

  return function () {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

// Each makeCounter() call creates an INDEPENDENT closure
const counterA = makeCounter();
const counterB = makeCounter();
counterA(); // 1
counterA(); // 2
counterB(); // 1 ← independent!

// Real-world: factory functions
function createMultiplier(factor) {
  return (number) => number * factor; // closes over 'factor'
}
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(triple(5));    // 15
console.log(quadruple(5)); // 20
§ 05

Arrays & Objects

map · filter · reduce · Destructuring · Spread
The Big Four Array Methods
Master these four and you'll write clean, modern JS. They are the bread and butter of frontend development.
javascript
const products = [
  { id: 1, name: "Laptop",  price: 999,  inStock: true  },
  { id: 2, name: "Mouse",   price: 29,   inStock: true  },
  { id: 3, name: "Monitor", price: 399,  inStock: false },
  { id: 4, name: "Keyboard",price: 79,   inStock: true  },
];

// map() — transform every element, returns a new array SAME length
const names = products.map(p => p.name);
// ["Laptop", "Mouse", "Monitor", "Keyboard"]

// filter() — keep only elements that pass a test
const available = products.filter(p => p.inStock);
// 3 products (Laptop, Mouse, Keyboard)

// reduce() — accumulate all elements into a SINGLE value
const total = products.reduce((sum, p) => sum + p.price, 0);
console.log(total); // 1506

// forEach() — side effects only, returns NOTHING
products.forEach(p => {
  if (!p.inStock) console.log(`⚠️ ${p.name} out of stock`);
});
Destructuring & Spread/Rest
javascript
const user = { name: "Alice", age: 28, city: "Kigali", role: "admin" };

// Object destructuring
const { name, age } = user;  // extract specific keys
const { name: userName } = user; // rename while destructuring

// Array destructuring
const [first, second, , fourth] = [10, 20, 30, 40]; // skip index 2

// Swap without temp variable
let x = 1, y = 2;
[x, y] = [y, x];

// SPREAD operator — expand an iterable
const a = [1, 2, 3];
const copy = [...a];         // independent copy
const merged = [...a, 4, 5]; // [1, 2, 3, 4, 5]

const prodConfig = { ...{ host: "localhost", port: 3000 }, host: "api.myapp.com" };
// { host: "api.myapp.com", port: 3000 }

// REST operator — collect remaining into array
function sum(...nums) {
  return nums.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4, 5); // 15

const [head, ...tail] = [1, 2, 3, 4];
// head = 1, tail = [2, 3, 4]
§ 06

DOM Manipulation

Select · Modify · Events · Dynamic UI
Selecting & Modifying Elements
javascript
// Selecting elements
const title = document.getElementById("title");
const btn = document.querySelector("#submit-btn");  // first match
const cards = document.querySelectorAll(".card");   // all matches
const cardArray = [...cards]; // convert NodeList to true array

// Content & attributes
title.textContent = "Safe text (no HTML parsing)";
title.innerHTML = "Text with <strong>bold</strong>"; // ⚠️ XSS risk

// CSS classes (preferred over inline styles)
title.classList.add("active");
title.classList.remove("hidden");
title.classList.toggle("dark-mode");  // add if missing, remove if present
title.classList.contains("active");  // true/false

// Dataset attributes — data-* in HTML
// <button data-user-id="42" data-role="admin">Edit</button>
const editBtn = document.querySelector("button");
console.log(editBtn.dataset.userId); // "42"
console.log(editBtn.dataset.role);   // "admin"
Event Handling & Delegation
javascript
// Click, input, submit events
btn.addEventListener("click", (event) => {
  console.log("Clicked!", event.target);
});

form.addEventListener("submit", (event) => {
  event.preventDefault(); // ← CRITICAL: prevents page reload
  const data = Object.fromEntries(new FormData(event.target));
  console.log(data);
});

// EVENT DELEGATION — ONE listener for MANY elements
// Instead of adding listeners to each item, add one to the parent
document.querySelector("#product-list").addEventListener("click", (e) => {
  const item = e.target.closest(".product-item");
  if (item) console.log("Product ID:", item.dataset.id);
});
§ 07

Asynchronous JavaScript

Callbacks · Promises · async/await · Event Loop
The Event Loop

JavaScript is single-threaded but handles async tasks through the event loop. Understanding this model is critical for writing performant, bug-free JS.

javascript — event loop demo
console.log("1 — synchronous");

setTimeout(() => console.log("2 — timeout (0ms)"), 0); // queued, not immediate!

Promise.resolve().then(() => console.log("3 — promise microtask"));

console.log("4 — synchronous");

// Output: 1, 4, 3, 2
// Microtasks (Promises) run BEFORE macrotasks (setTimeout)
Promises → async/await Evolution
❌ Callback Hell
fetchUser(id, (err, user) => {
  fetchPosts(user.id, (err, posts) => {
    fetchComments(posts[0].id,
      (err, comments) => {
        // deeply nested 😰
      }
    );
  });
});
✅ async/await
async function load(id) {
  const user = await fetchUser(id);
  const posts = await fetchPosts(user.id);
  const coms = await fetchComments(
    posts[0].id);
  // flat, readable ✅
}
javascript — async/await with concurrent requests
async function loadDashboard(userId) {
  try {
    const user = await fetchUser(userId); // must happen first

    // Run these CONCURRENTLY (3x faster than sequential!)
    const [orders, notifications] = await Promise.all([
      fetchOrders(user.id),
      fetchNotifications(user.id)
    ]);

    return { user, orders, notifications };

  } catch (error) {
    console.error("Dashboard failed:", error.message);
    throw error;
  } finally {
    // always runs — hide loading spinner, cleanup
    loadingEl.style.display = "none";
  }
}
§ 08

Working with APIs

REST · Fetch · JSON · Error Handling
REST API Conventions
MethodURLActionSuccess Code
GET/api/usersGet all users200 OK
GET/api/users/1Get user with ID 1200 OK
POST/api/usersCreate a new user201 Created
PUT/api/users/1Replace user 1200 OK
PATCH/api/users/1Partially update user 1200 OK
DELETE/api/users/1Delete user 1204 No Content
Fetch API — GET & POST
Common mistake: fetch() only rejects on network errors, NOT HTTP errors (404, 500). Always check response.ok!
javascript
// GET request
async function getUsers() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
  return response.json(); // parse JSON body
}

// POST request — send data
async function createPost(postData) {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${token}`
    },
    body: JSON.stringify(postData)  // JS object → JSON string
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(err.message || "Failed to create post");
  }

  return response.json(); // returns the created resource
}
§ 09

ES6+ Modern JavaScript

Template Literals · Modules · Optional Chaining · Nullish Coalescing
Key Modern Features
javascript — optional chaining & nullish coalescing
const user = { name: "Alice", address: { city: "Kigali" } };

// Optional chaining (?.) — safely access deep properties
const city = user?.address?.city;      // "Kigali"
const phone = user?.phone?.number;     // undefined (no error!)
const firstUser = users?.[0];         // safe even if users is null

// Nullish coalescing (??) — default for null/undefined ONLY
const score1 = 0 || "No score";   // "No score" ← WRONG! 0 is valid
const score2 = 0 ?? "No score";   // 0 ← CORRECT!

const port = config?.port ?? 3000;
const displayName = user?.nickname ?? user?.name ?? "Anonymous";
javascript — modules (import/export)
// math.js — exporting
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default function subtract(a, b) { return a - b; } // default

// main.js — importing
import subtract, { add, PI } from "./math.js";  // named + default
import * as MathUtils from "./math.js";          // namespace import

console.log(add(2, 3));        // 5
console.log(MathUtils.PI);    // 3.14159
§ 10

Error Handling & Debugging

try/catch · Custom Errors · DevTools · Common Pitfalls
Custom Error Classes
javascript
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

class NotFoundError extends Error {
  constructor(resource, id) {
    super(`${resource} with ID ${id} not found`);
    this.name = "NotFoundError";
    this.statusCode = 404;
  }
}

// Differentiated error handling
try {
  validateEmail("not-an-email");
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Field '${error.field}': ${error.message}`);
  } else if (error instanceof NotFoundError) {
    console.error(`404: ${error.message}`);
  } else {
    console.error("Unexpected error:", error);
    // Report to Sentry, Datadog, etc.
  }
}
Common Bugs & Fixes
❌ Common Bug: async without await
async function loadData() {
  const data = fetch("/api/data");
  // ❌ forgot await
  // data is a Promise, not JSON!
  console.log(data.users); // undefined
}
✅ Fixed
async function loadData() {
  const res = await fetch("/api/data");
  const data = await res.json();
  // ✅ data is now the actual object
  console.log(data.users);
}
§ 11

JavaScript in the Browser

LocalStorage · Form Validation · User Interactions
LocalStorage
javascript
// localStorage persists FOREVER, sessionStorage clears on tab close
localStorage.setItem("username", "alice");
localStorage.setItem("prefs", JSON.stringify({ theme: "dark" }));

const username = localStorage.getItem("username");    // "alice"
const prefs = JSON.parse(localStorage.getItem("prefs")); // object

// Handle missing key gracefully — always provide a fallback
const settings = JSON.parse(localStorage.getItem("settings") || "{}");

localStorage.removeItem("username");
localStorage.clear(); // ⚠️ removes ALL keys

// Persist user theme preference
function saveTheme(theme) {
  localStorage.setItem("theme", theme);
  document.body.classList.toggle("dark", theme === "dark");
}

document.addEventListener("DOMContentLoaded", () => {
  const saved = localStorage.getItem("theme") || "light";
  document.body.classList.toggle("dark", saved === "dark");
});
§ 12

JavaScript with a Backend

Node.js · Express.js · Full REST API
Express.js REST API
javascript — server.js (install: npm install express cors)
const express = require("express");
const cors = require("cors");

const app = express();
app.use(cors());             // allow cross-origin requests
app.use(express.json());   // parse incoming JSON bodies

let products = [
  { id: 1, name: "Laptop", price: 999, inStock: true },
];

app.get("/api/products", (req, res) => {
  res.json(products);
});

app.get("/api/products/:id", (req, res) => {
  const product = products.find(p => p.id === parseInt(req.params.id));
  if (!product) return res.status(404).json({ message: "Not found" });
  res.json(product);
});

app.post("/api/products", (req, res) => {
  const { name, price, inStock = true } = req.body;
  if (!name || !price) return res.status(400).json({ message: "Required fields missing" });
  const newProduct = { id: products.length + 1, name, price, inStock };
  products.push(newProduct);
  res.status(201).json(newProduct);
});

app.listen(3000, () => console.log("API running at http://localhost:3000"));
§ 13

Mini Projects — Hands-On

Beginner · Intermediate · Advanced
⬤ Beginner

📝 To-Do List App

Skills: DOM manipulation, event handling, LocalStorage persistence, filter/state management

DOM Events LocalStorage State
javascript — todo.js (core logic)
// State
let todos = JSON.parse(localStorage.getItem("todos") || "[]");
let currentFilter = "all";

function addTodo(text) {
  const trimmed = text.trim();
  if (!trimmed) return;
  todos.push({ id: Date.now(), text: trimmed, completed: false });
  localStorage.setItem("todos", JSON.stringify(todos));
  render();
}

function toggleTodo(id) {
  todos = todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t);
  localStorage.setItem("todos", JSON.stringify(todos));
  render();
}

function getFiltered() {
  if (currentFilter === "active") return todos.filter(t => !t.completed);
  if (currentFilter === "completed") return todos.filter(t => t.completed);
  return todos;
}

// Event delegation — ONE listener for all items
document.getElementById("todo-list").addEventListener("click", (e) => {
  const id = Number(e.target.dataset.id);
  if (e.target.matches("input[type='checkbox']")) toggleTodo(id);
  if (e.target.matches("button")) todos = todos.filter(t => t.id !== id);
  localStorage.setItem("todos", JSON.stringify(todos));
  render();
});
⬤ Intermediate

🌤️ Weather App (API Integration)

Skills: Fetch API, async/await, DOM rendering, error handling, search history

Fetch API async/await Error Handling LocalStorage
javascript — weather.js (main logic)
const GEO_API = "https://geocoding-api.open-meteo.com/v1/search";
const WEATHER_API = "https://api.open-meteo.com/v1/forecast"; // free, no key!

async function searchWeather(cityName) {
  const errorEl = document.getElementById("error");
  const loadingEl = document.getElementById("loading");

  loadingEl.style.display = "block";
  errorEl.style.display = "none";

  try {
    // Step 1: city name → coordinates
    const geoRes = await fetch(`${GEO_API}?name=${encodeURIComponent(cityName)}&count=1`);
    const geoData = await geoRes.json();
    if (!geoData.results?.length) throw new Error(`City "${cityName}" not found`);
    const { name, latitude, longitude, country_code } = geoData.results[0];

    // Step 2: fetch weather with coordinates
    const params = new URLSearchParams({
      latitude, longitude,
      current: "temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code"
    });
    const weatherRes = await fetch(`${WEATHER_API}?${params}`);
    const weather = await weatherRes.json();

    renderWeather({ name, country_code }, weather);

  } catch (err) {
    errorEl.textContent = `❌ ${err.message}`;
    errorEl.style.display = "block";
  } finally {
    loadingEl.style.display = "none";
  }
}
⬤ Advanced

🛒 Full CRUD Products Manager

Skills: Full CRUD, API layer, state management, event delegation, modular code

Full CRUD State Mgmt Modular Code API Layer
javascript — products-app.js (architecture)
// CENTRALIZED STATE
const state = {
  products: [],
  editingId: null,
  filter: "",
  sortBy: "name"
};

// API LAYER — all HTTP calls isolated here
const api = {
  getAll: () => apiFetch("/api/products"),
  create: (data) => apiFetch("/api/products", { method: "POST", body: JSON.stringify(data) }),
  update: (id, data) => apiFetch(`/api/products/${id}`, { method: "PUT", body: JSON.stringify(data) }),
  delete: (id) => apiFetch(`/api/products/${id}`, { method: "DELETE" }),
};

// COMPUTED STATE — derived values, not stored
function getDisplayProducts() {
  return state.products
    .filter(p => p.name.toLowerCase().includes(state.filter.toLowerCase()))
    .sort((a, b) => state.sortBy === "price"
      ? a.price - b.price
      : a.name.localeCompare(b.name)
    );
}

// ACTION — modify state + trigger render
async function handleFormSubmit(event) {
  event.preventDefault();
  const data = Object.fromEntries(new FormData(event.target));

  if (state.editingId) {
    const updated = await api.update(state.editingId, data);
    state.products = state.products.map(p => p.id === updated.id ? updated : p);
    state.editingId = null;
  } else {
    const created = await api.create(data);
    state.products.push(created);
  }
  render(); // single render call
}
§ 14

Performance & Best Practices

Debounce · Memoization · Clean Code · Production Checklist
Performance Patterns
javascript — debounce & throttle
// DEBOUNCE — wait until user stops typing
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const handleSearch = debounce(async (query) => {
  const results = await searchProducts(query);
  renderResults(results);
}, 300); // waits 300ms after last keystroke

searchInput.addEventListener("input", (e) => handleSearch(e.target.value));

// MEMOIZATION — cache expensive results
function memoize(fn) {
  const cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

// LAZY LOADING — Intersection Observer for images
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src; // load real image when in viewport
      observer.unobserve(img);
    }
  });
});
document.querySelectorAll("img[data-src]").forEach(img => observer.observe(img));
Production Checklist
  • All console.log / debugger removed
  • API keys in environment variables, never in JS
  • User-facing errors handled gracefully
  • User input validated & sanitized (no raw innerHTML from users)
  • All async functions have try/catch
  • Event listeners cleaned up on unmount
  • const by default, let when needed, var never
  • All == replaced with ===
  • No blocking operations in render paths
  • Images lazy-loaded with Intersection Observer
  • Debounce on all input event handlers
  • LocalStorage reads have JSON fallback (|| "{}")
  • Code split into feature-based modules
  • Concurrent async ops use Promise.all()
What's Next?
⚛️

React

The most in-demand frontend library. Everything here applies directly.

🔷

TypeScript

Adds static typing to JS — catches bugs before runtime.

Vite

Lightning-fast dev server and bundler. The modern standard.

🗄️

Express + PostgreSQL

Full CRUD with a real database and JWT authentication.

🔌

WebSockets

Real-time communication for chats, dashboards, live updates.

🧵

Web Workers

Run heavy computation in background threads without blocking UI.

🎯 The best way to learn JavaScript is to BUILD things. Take each section's concepts and build something with them immediately. Refer back to this guide as a reference as you grow. Happy coding! 🚀