The Complete
JavaScript Course
From variables and functions to the DOM, async programming, and modern ES6+
syntax.
A PRACTICAL, PROJECT-BASED GUIDE
Table of Contents
01 Introduction to JavaScript
02 Variables, Data Types & Operators
03 Control Flow — Conditionals & Loops
04 Functions
05 Arrays
06 Objects
07 The DOM — Reading & Changing a Page
08 Events
09 ES6+ Features
010 Asynchronous JavaScript
011 Fetching Data & Working with APIs
012 Error Handling & Debugging
013 Final Project & Where to Go Next
CHAPTER 1
Introduction to JavaScript
JavaScript is the programming language of the web. While HTML provides structure and CSS provides style, JavaScript makes a page
interactive — responding to clicks, updating content, validating forms, and talking to servers.
1.1 Adding JavaScript to a Page
<!-- external file (recommended) -->
<script src="[Link]"></script>
<!-- inline -->
<script>
[Link]("Hello, world!");
</script>
Best practice: Place your <script> tag just before </body> , or use the defer attribute in the <head> , so the page's HTML loads before
your script runs.
<script src="[Link]" defer></script>
1.2 The Console
The browser console (DevTools) is your primary tool for testing and debugging.
[Link]("Debug message");
[Link]("Something went wrong");
[Link]([{ name: "Ana", age: 30 }]);
1.3 Statements & Comments
// single-line comment
/* multi-line
comment */
let greeting = "Hi there"; // a statement, ended with ;
Try it: Create an HTML file with a linked [Link] . Log your name, your age, and a short sentence to the console using three separate
[Link]() calls.
CHAPTER 2
Variables, Data Types & Operators
2.1 Declaring Variables
let age = 25; // can be reassigned
const name = "Sam"; // cannot be reassigned
var oldStyle = true; // legacy, avoid in new code
Rule of thumb: Default to const . Use let only when you know the value will change. Avoid var — it has confusing scoping rules that
let / const fix.
2.2 Primitive Data Types
Type Example
String "hello" , 'hi' , `template`
Number 42 , 3.14 , -7
Boolean true , false
Undefined A declared variable with no assigned value
Null An intentional "no value"
Symbol / BigInt Less common, special-purpose types
2.3 Template Literals
const name = "Sam";
const age = 30;
[Link](`My name is ${name} and I'm ${age}.`);
2.4 Operators
5 + 3 // 8 (addition)
5 - 3 // 2
5 * 3 // 15
5 / 3 // 1.666...
5 % 3 // 2 (remainder)
5 ** 2 // 25 (exponent)
5 === 5 // true (strict equality: value AND type)
5 === "5" // false
5 == "5" // true (loose equality — avoid this)
age > 18 && hasID // AND
isWeekend || isHoliday // OR
!isLoggedIn // NOT
Always use === and !== instead of == / != . Loose equality performs type coercion that produces surprising results (e.g. 0 == false is
true ).
Try it: Declare const variables for a product's name, price, and quantity. Use a template literal to log a sentence like "3 x Notebook = $15".
CHAPTER 3
Control Flow — Conditionals & Loops
3.1 if / else
if (age >= 18) {
[Link]("Adult");
} else if (age >= 13) {
[Link]("Teenager");
} else {
[Link]("Child");
}
3.2 switch
switch (day) {
case "Mon":
[Link]("Start of week");
break;
case "Fri":
[Link]("Almost weekend");
break;
default:
[Link]("Regular day");
}
3.3 Ternary Operator
const status = age >= 18 ? "adult" : "minor";
3.4 Loops
// for loop
for (let i = 0; i < 5; i++) {
[Link](i);
}
// while loop
let count = 0;
while (count < 3) {
[Link](count);
count++;
}
// for...of — iterate over values (arrays, strings)
for (const fruit of ["apple", "pear"]) {
[Link](fruit);
}
// for...in — iterate over object keys
for (const key in { a: 1, b: 2 }) {
[Link](key);
}
Try it: Write a loop that prints numbers 1–20, but prints "Fizz" for multiples of 3, "Buzz" for multiples of 5, and "FizzBuzz" for multiples of both
(the classic FizzBuzz exercise).
CHAPTER 4
Functions
4.1 Function Declarations & Expressions
// declaration — hoisted, can be called before its definition
function greet(name) {
return `Hello, ${name}!`;
}
// expression — not hoisted
const greet2 = function(name) {
return `Hi, ${name}!`;
};
4.2 Arrow Functions
const add = (a, b) => a + b;
const square = n => n * n; // single param: parens optional
const logAndAdd = (a, b) => {
[Link]("adding...");
return a + b;
};
Arrow functions don't have their own this — they inherit it from the surrounding scope. This makes them especially useful inside callbacks and
class methods.
4.3 Default & Rest Parameters
function greet(name = "stranger") {
return `Hello, ${name}`;
}
function sum(...numbers) {
return [Link]((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
4.4 Scope & Closures
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
counter(); // 1
counter(); // 2 — the inner function "remembers" count
A closure is a function that remembers the variables from where it was created, even after that outer function has finished running.
Closures power patterns like private counters, memoization, and event handlers with saved state.
Try it: Write a function calculateArea(shape, ...dimensions) that returns the area for "rectangle" (width, height) or "circle" (radius)
using a switch statement.
CHAPTER 5
Arrays
5.1 Creating & Accessing Arrays
const fruits = ["apple", "banana", "cherry"];
fruits[0]; // "apple"
[Link]; // 3
5.2 Mutating Methods
[Link]("date"); // add to end
[Link](); // remove from end
[Link]("apricot"); // add to start
[Link](); // remove from start
[Link](1, 1, "kiwi"); // remove/insert at index
5.3 The Big Three: map, filter, reduce
const nums = [1, 2, 3, 4, 5];
const doubled = [Link](n => n * 2);
// [2, 4, 6, 8, 10]
const evens = [Link](n => n % 2 === 0);
// [2, 4]
const total = [Link]((sum, n) => sum + n, 0);
// 15
Why these matter: map , filter , and reduce don't mutate the original array — they return new arrays or values. This "functional" style
leads to more predictable, easier-to-debug code than manual loops with mutation.
5.4 Other Common Methods
[Link](n => n > 3); // 4 (first match)
[Link](n => n > 4); // true (at least one)
[Link](n => n > 0); // true (all match)
[Link](3); // true
[Link]((a, b) => a - b); // ascending sort
[Link](", "); // "1, 2, 3, 4, 5"
Try it: Given an array of order totals, use filter to keep only orders over $50, then reduce to sum them into a single grand total — all in one
chain.
CHAPTER 6
Objects
6.1 Creating & Accessing Objects
const user = {
name: "Ana",
age: 28,
isAdmin: false,
greet() {
return `Hi, I'm ${[Link]}`;
}
};
[Link]; // dot notation
user["age"]; // bracket notation (needed for dynamic keys)
[Link](); // "Hi, I'm Ana"
6.2 Adding, Updating & Deleting Properties
[Link] = "ana@[Link]"; // add
[Link] = 29; // update
delete [Link]; // remove
6.3 Destructuring
const { name, age } = user;
[Link](name, age); // "Ana" 29
// with renaming and defaults
const { email: contact = "n/a" } = user;
6.4 Spread & [Link]
const updatedUser = { ...user, age: 30 }; // shallow copy + override
[Link](user); // ["name", "age", "email"]
[Link](user); // ["Ana", 30, "ana@[Link]"]
[Link](user); // [["name","Ana"], ["age",30], ...]
Arrays of objects are the most common data shape in real apps — e.g. a list of users or products, each represented as an object. Combining
destructuring with map / filter is a core everyday pattern.
Try it: Create an array of 3 product objects (name, price, inStock). Use filter to get only in-stock products, then map with destructuring to
produce an array of strings like "Notebook — $5" .
CHAPTER 7
The DOM — Reading & Changing a Page
The DOM (Document Object Model) is the browser's live, in-memory representation of your HTML. JavaScript uses it to read and
change what's on screen.
7.1 Selecting Elements
[Link](".card"); // first match
[Link](".card"); // NodeList of all matches
[Link]("main"); // by id
7.2 Changing Content & Attributes
const title = [Link]("h1");
[Link] = "New Title"; // plain text
[Link] = "<em>New</em>"; // parsed as HTML — sanitize user input!
const link = [Link]("a");
[Link]("href", "[Link]
[Link]("active");
[Link]("hidden");
7.3 Changing Styles
[Link] = "crimson";
[Link] = "2rem";
Prefer toggling CSS classes over setting individual inline styles from JS — it keeps styling logic in your stylesheet.
7.4 Creating & Removing Elements
const li = [Link]("li");
[Link] = "New item";
[Link]("ul").appendChild(li);
[Link]();
Try it: Build an HTML page with an empty <ul> and a button. On each click, create and append a new <li> with incrementing text ("Item 1",
"Item 2"...).
CHAPTER 8
Events
8.1 Listening for Events
const button = [Link]("button");
[Link]("click", () => {
[Link]("Button clicked!");
});
8.2 The Event Object
[Link]("form").addEventListener("submit", (event) => {
[Link](); // stop the page from reloading
[Link]([Link]); // the element that fired the event
});
8.3 Common Event Types
Event Fires when...
click An element is clicked
input A form field's value changes (live)
submit A form is submitted
keydown / keyup A key is pressed / released
mouseenter / mouseleave Mouse enters/leaves an element
DOMContentLoaded The HTML has fully loaded
8.4 Event Delegation
// instead of adding a listener to every <li>, listen on the parent
[Link]("ul").addEventListener("click", (event) => {
if ([Link] === "LI") {
[Link]("done");
}
});
Delegation relies on event bubbling — events fired on a child also fire on its ancestors. It's more efficient and works automatically for
elements added later.
Try it: Build a simple to-do list: a text input, an "Add" button, and a <ul>. Clicking a list item should toggle a "completed" class (strike-through
text) using event delegation.
CHAPTER 9
ES6+ Features
9.1 let/const, Arrow Functions, Template Literals
Covered in earlier chapters — these are core ES6 (2015) features now considered standard practice.
9.2 Spread & Rest
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1,2,3,4,5]
const obj2 = { ...obj1, extra: true };
9.3 Optional Chaining & Nullish Coalescing
const city = [Link]?.city; // undefined instead of throwing, if address is missing
const name = [Link] ?? "Anonymous"; // fallback only for null/undefined
9.4 Classes
class Animal {
constructor(name) {
[Link] = name;
}
speak() {
return `${[Link]} makes a sound.`;
}
}
class Dog extends Animal {
speak() {
return `${[Link]} barks.`;
}
}
const rex = new Dog("Rex");
[Link](); // "Rex barks."
9.5 Modules
// [Link]
export function add(a, b) { return a + b; }
export const PI = 3.14159;
// [Link]
import { add, PI } from "./[Link]";
To use modules in the browser, add type="module" to your script tag: <script type="module" src="[Link]"></script> .
Try it: Create a Shape class with a describe() method, then a Circle subclass that overrides it to include the radius.
CHAPTER 10
Asynchronous JavaScript
10.1 The Problem: Blocking vs. Non-Blocking
JavaScript runs on a single thread. Slow operations (network requests, timers, file reads) run in the background so they don't freeze the
page — but that means their results arrive later, which is where async patterns come in.
10.2 Callbacks (the old way)
setTimeout(() => {
[Link]("3 seconds later");
}, 3000);
10.3 Promises
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) resolve("It worked!");
else reject("It failed.");
});
promise
.then(result => [Link](result))
.catch(error => [Link](error));
10.4 async/await (the modern way)
async function getData() {
try {
const result = await somePromiseFn();
[Link](result);
} catch (error) {
[Link]("Failed:", error);
}
}
await pauses execution inside an async function until the promise resolves, letting asynchronous code read like straightforward, top-to-
bottom synchronous code.
10.5 Running Things in Parallel
const [a, b] = await [Link]([fetchA(), fetchB()]);
Awaiting requests one at a time when they don't depend on each other wastes time. Use [Link]() to run independent async operations
concurrently.
Try it: Write a function wait(ms) that returns a Promise resolving after ms milliseconds using setTimeout . Then write an async function that
awaits it and logs "Done" after 2 seconds.
CHAPTER 11
Fetching Data & Working with APIs
11.1 The fetch() API
async function getUsers() {
const response = await fetch("[Link]
if (![Link]) {
throw new Error(`HTTP error: ${[Link]}`);
}
const data = await [Link]();
return data;
}
11.2 Sending Data (POST)
await fetch("[Link] {
method: "POST",
headers: { "Content-Type": "application/json" },
body: [Link]({ name: "Ana", age: 28 })
});
11.3 Putting It Together: Rendering Fetched Data
async function renderUsers() {
const users = await getUsers();
const list = [Link]("#user-list");
[Link] = users
.map(u => `<li>${[Link]}</li>`)
.join("");
}
Security note: Be careful inserting fetched data with innerHTML — if the data could contain user-supplied text, sanitize it or use textContent
to avoid injection attacks.
Try it: Fetch data from [Link] (a free test API) and render each user's name and email into a <ul> on
the page.
CHAPTER 12
Error Handling & Debugging
12.1 try/catch/finally
try {
const data = [Link](invalidJson);
} catch (error) {
[Link]("Parsing failed:", [Link]);
} finally {
[Link]("This always runs.");
}
12.2 Throwing Custom Errors
function withdraw(balance, amount) {
if (amount > balance) {
throw new Error("Insufficient funds");
}
return balance - amount;
}
12.3 Debugging Tools
Breakpoints — pause execution in DevTools' Sources panel to inspect variables line by line.
debugger; — a statement that pauses execution when DevTools is open.
[Link] / [Link] — quick, low-effort inspection of values.
Network tab — inspect requests, responses, and status codes for fetch calls.
12.4 Common Mistakes to Watch For
Mistake Symptom
Using = instead of === Accidental reassignment inside an if
Forgetting await Getting a Promise object instead of the value
Mutating state directly UI doesn't update / bugs in frameworks like React
Off-by-one loop errors Skips the first/last item or runs one time too many
Try it: Wrap your fetch call from Chapter 11 in a try/catch. Deliberately break the URL and confirm your catch block logs a helpful error
instead of crashing the page.
CHAPTER 13
Final Project & Where to Go Next
13.1 Final Project: A To-Do App with Persistence
Combine everything from this course into one small app:
1. An input + "Add" button to create new to-do items (DOM + Events)
2. Click a to-do to mark it complete (event delegation + classList)
3. A "Delete" button per item (DOM manipulation)
4. Store the list in an array of objects: { text, done }
5. Save/load that array to localStorage as JSON so it survives a page refresh
6. Wrap storage reads in try/catch in case the saved data is corrupted
13.2 Suggested Skeleton
let todos = [Link]([Link]("todos")) || [];
function save() {
[Link]("todos", [Link](todos));
}
function render() {
// rebuild the <ul> from the todos array
}
13.3 Where to Go Next
TypeScript — adds static types on top of JavaScript, catching bugs earlier.
A frontend framework — React, Vue, or Svelte, which build on the DOM concepts here.
[Link] — run JavaScript outside the browser, for servers and tooling.
Testing — tools like Jest or Vitest for writing automated tests.
Practice — rebuild small apps (calculator, quiz, weather app) from scratch; repetition builds real fluency.
Challenge: Build the final project from scratch without looking back at earlier chapters. Refer back only when you get stuck — that struggle is
where the learning sticks.
End of Course — Happy coding!