The Ultimate Guide to JavaScript Programming (From Beginner to Pro)
1. What is JavaScript and Why It Matters
JavaScript is the language of the web. If HTML is the structure and CSS is the style, JavaScript is the behavior — the logic that makes a page interactive, dynamic, and alive.
You know when a button clicks and triggers an animation?
Or when a form validates your input before sending it?
Or when you type something and suggestions appear in real-time?
That’s JavaScript.
🌐 A Brief History
- Invented by: Brendan Eich in 1995 (in just 10 days!)
- Originally called: Mocha, then LiveScript
- Renamed to: JavaScript (as a marketing move to ride the wave of Java’s popularity)
Fun fact: Despite the name, JavaScript has nothing to do with Java. It’s an entirely different language with different goals and syntax.
🧠 What Makes JavaScript Special?
- Client-Side by Default: Runs directly in your browser, no installation needed.
- Event-Driven: It waits for user actions like clicks, scrolls, and keystrokes.
- Interpreted: Runs line-by-line — no need to compile.
- Dynamic and Weakly Typed: You can assign anything to a variable and change it later.
- Ubiquitous: It runs in every browser on every device.
📦 Where is JavaScript Used?
| Area | Use |
|---|---|
| Web Development | Creating interactive websites, SPAs (Single Page Applications) |
| Backend Servers | Node.js enables server-side JavaScript |
| Mobile Apps | React Native, Ionic for cross-platform mobile development |
| Desktop Apps | Electron (used by VS Code, Slack, Discord) |
| Game Development | HTML5 Canvas and WebGL for browser games |
| IoT Devices | Controlling physical devices with JS via Johnny-Five, Espruino |
| Machine Learning | TensorFlow.js for ML in the browser |
🧱 JavaScript is Everywhere
“Any application that can be written in JavaScript, will eventually be written in JavaScript.”
— Jeff Atwood, Co-founder of Stack Overflow
It’s not just a scripting language anymore. JavaScript now powers full-fledged apps, from tiny browser animations to real-time collaborative platforms like Google Docs.
💡 What You Can Do With JavaScript
- Create games that run in the browser
- Build full-stack web apps using the MERN stack (MongoDB, Express, React, Node)
- Develop native mobile apps (React Native)
- Animate and transform webpages (GSAP, anime.js)
- Write Chrome extensions
- Automate browser actions
- Work with real-time data and APIs
🔧 Tools You’ll Use With JavaScript
| Tool | Purpose |
|---|---|
| Browser Console | Test JS instantly |
| VS Code | Writing and debugging code |
| npm | Package manager for installing libraries |
| Node.js | To run JavaScript outside the browser |
| Webpack / Vite | Bundling and building your app |
| ESLint | Keeping your code clean and error-free |
⚙️ JavaScript in the Real World
- Netflix: UI interactivity & streaming logic
- Facebook: React-based frontend rendering
- Uber: Real-time maps & ride coordination
- Slack: Desktop app built with Electron
- Google Docs: Live collaboration using websockets
- Shopify: Storefronts with custom JavaScript logic
2. JavaScript Syntax and Your First Program
Every language has a way to say “Hello.” In JavaScript, that’s done through the console.log() function.
👋 Your First Line of JavaScript
console.log("Hello, World!");
This sends output to the browser’s developer console. You can open the console in:
- Chrome: Right-click → “Inspect” → Console tab
- Firefox: Ctrl + Shift + K
- Safari: Enable Developer tools in settings
🧱 JavaScript Syntax Basics
Let’s look at the essentials that define JavaScript’s grammar.
🟨 Statements and Semicolons
Each action is typically a statement, and you can end it with a ; — though it’s optional thanks to Automatic Semicolon Insertion (ASI).
let name = "Alice";
console.log(name)
➡ Both are valid, but semicolons are recommended for clarity and consistency.
📦 Comments
// This is a single-line comment
/*
This is a
multi-line comment
*/
Use comments to document your code and make it understandable to other humans (or your future self).
🧮 Declaring Variables
JavaScript has three main ways to declare a variable:
✅ let – Preferred for variables that change
let age = 25;
age = 26;
❌ var – Old and quirky, avoid in modern code
var city = "Paris";
var has function scope and hoisting issues — we’ll explain those later.
🔒 const – For constants that should never change
const pi = 3.14159;
Trying to reassign a const throws an error.
🧠 JavaScript Data Types
JavaScript is dynamically typed — you don’t have to declare types explicitly. A variable can hold any type, and change it later.
let x = 42; // Number
x = "hello"; // Now a String
📋 The 7 Primitive Types
| Type | Example |
|---|---|
| Number | 42, 3.14, -100 |
| String | "hello", 'JS' |
| Boolean | true, false |
| Null | null |
| Undefined | undefined |
| BigInt | 12345678901234567890n |
| Symbol | Symbol("id") |
📦 Non-Primitives: Objects
let person = {
name: "Alice",
age: 30
};
You’ll work with objects a lot — they’re the backbone of most JS structures.
📋 typeof Operator
Use typeof to check the data type:
console.log(typeof "hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof null); // "object" (quirky JS behavior)
✅ Quick Recap
- Use
letandconst, avoidvar - JavaScript is case-sensitive (
Name≠name) - Semicolons are optional but recommended
- Use
console.log()to test and debug - Data types are dynamic but loosely checked
3. Operators and Expressions in JavaScript
Operators are the building blocks of logic in any programming language. JavaScript has many, and understanding how they behave — especially when mixed with dynamic typing — is critical.
🔢 Arithmetic Operators
These work just like you’d expect:
let x = 10;
let y = 3;
console.log(x + y); // 13
console.log(x - y); // 7
console.log(x * y); // 30
console.log(x / y); // 3.333...
console.log(x % y); // 1 (remainder)
console.log(x ** y); // 1000 (exponentiation)
🔄 Assignment Operators
let count = 1;
count += 2; // count = count + 2 → 3
count *= 3; // count = count * 3 → 9
count++; // increment → 10
count--; // decrement → 9
🔍 Comparison Operators
These return a Boolean (true or false).
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 >= 5); // true
console.log(5 == "5"); // true (loose equality!)
console.log(5 === "5"); // false (strict equality)
console.log(5 !== 6); // true
❗ == vs === (Type Coercion Nightmare)
==compares value after coercing types===compares value and type strictly
console.log(0 == false); // true 😬
console.log(0 === false); // false ✅
Rule of thumb:
Always use
===and!==unless you have a very specific reason not to.
⚙️ Logical Operators
&& // AND
|| // OR
! // NOT
let age = 20;
if (age >= 18 && age <= 65) {
console.log("Eligible to work");
}
if (age < 18 || age > 65) {
console.log("Not in working age");
}
console.log(!true); // false
🧠 Truthy and Falsy Values
JavaScript has a set of values that are treated as false in logical contexts:
❌ Falsy values:
false0""(empty string)nullundefinedNaN
Everything else is truthy.
if ("hello") {
console.log("This is truthy");
}
if (0) {
console.log("This won’t run");
}
🔄 Ternary Operator
A shorthand for if...else
let age = 18;
let status = (age >= 18) ? "adult" : "minor";
console.log(status); // "adult"
🔁 Operator Precedence
Some operators are evaluated before others:
let result = 5 + 10 * 2; // result = 25, not 30
You can force order with parentheses:
let result = (5 + 10) * 2; // result = 30
✅ Quick Recap
- Use
===and!==for safe comparisons - Remember that
0,"",null,undefined,NaNare falsy - Logical operators let you combine conditions
- The ternary operator is useful for simple
if...else
4. Conditional Statements in JavaScript
Conditional statements allow you to make decisions in your code. They let your program choose different paths based on truthy or falsy conditions.
✅ if, else if, else
let temperature = 28;
if (temperature > 30) {
console.log("It's hot outside.");
} else if (temperature > 20) {
console.log("It's warm.");
} else {
console.log("It's cool.");
}
JavaScript evaluates the conditions from top to bottom and executes the first true block.
🔁 Nested if Statements
let age = 25;
let hasID = true;
if (age >= 18) {
if (hasID) {
console.log("Access granted.");
} else {
console.log("ID required.");
}
} else {
console.log("Underage.");
}
Too much nesting can hurt readability — prefer && when possible:
if (age >= 18 && hasID) {
console.log("Access granted.");
}
🔄 switch Statement
Best used when checking one variable against many values.
let fruit = "apple";
switch (fruit) {
case "banana":
console.log("Banana is yellow.");
break;
case "apple":
console.log("Apple is red.");
break;
case "grape":
console.log("Grape is purple.");
break;
default:
console.log("Unknown fruit.");
}
Without break, the code will “fall through” to the next case — which can lead to bugs or intentional chaining.
🧠 Truthy/Falsy in Conditions
You can write conditions without === true:
let loggedIn = false;
if (!loggedIn) {
console.log("Please log in.");
}
or use shorthand:
if (user) {
// Checks if user is not null/undefined/falsy
console.log(user.name);
}
❗ Common Mistakes
- Forgetting
breakinswitch - Using
=(assignment) instead of==in conditionals
if (x = 5) // WRONG: always true
- Deep nesting — try to flatten logic using early
returnor combining conditions
🔄 Ternary Recap
Use the ternary operator for short conditional expressions:
let access = age >= 18 ? "Allowed" : "Denied";
Avoid complex ternaries with multiple layers — they hurt readability.
✅ Quick Recap
- Use
if/else if/elsefor branching logic - Use
switchwhen comparing one value against many options - Conditions evaluate truthy/falsy, not just strict
true - Use
&&and||to combine conditions logically
5. Loops and Iteration in JavaScript
Loops let your program repeat actions — whether it’s printing numbers, processing items in an array, or running a block of code until a condition is met.
🔁 for Loop
The classic loop — good when you know exactly how many times to repeat:
for (let i = 0; i < 5; i++) {
console.log("Count:", i);
}
Structure:
for ([init]; [condition]; [increment]) {
// loop body
}
🔂 while Loop
Runs as long as the condition is true:
let i = 0;
while (i < 3) {
console.log("While:", i);
i++;
}
🔁 do...while Loop
Same as while, but runs at least once before checking the condition:
let i = 0;
do {
console.log("Do while:", i);
i++;
} while (i < 3);
🧺 Looping Through Arrays
✅ Traditional for Loop
let colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
✅ for...of — Clean and Modern
for (let color of colors) {
console.log(color);
}
- Works on arrays, strings, sets, maps, etc.
⚠ for...in — Looping Through Keys
let person = { name: "Alice", age: 30 };
for (let key in person) {
console.log(key, person[key]);
}
Use for...in for objects, not arrays — it loops over property names (keys), not values.
🔄 Control Keywords: break and continue
🚫 break
Stops the loop completely:
for (let i = 0; i < 10; i++) {
if (i === 3) break;
console.log(i); // 0, 1, 2
}
⏭ continue
Skips the current iteration:
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0, 1, 3, 4
}
🧠 Common Pitfalls
- Forgetting to update the loop variable (infinite loop!)
- Using
for...inon arrays (can loop in unexpected order) - Nesting too many loops — use helper functions where possible
✅ Quick Recap
- Use
forwhen you know the number of iterations - Use
whileordo...whilefor condition-based loops - Prefer
for...offor arrays,for...infor objects - Use
breakto stop a loop,continueto skip one turn
6. Functions and Arrow Functions
Functions are reusable blocks of code designed to perform specific tasks. In JavaScript, they’re first-class citizens — meaning they can be assigned to variables, passed around as arguments, or even returned from other functions.
✅ Function Declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
- Uses the
functionkeyword - Can be called before they’re defined due to hoisting
✅ Function Expression
const greet = function(name) {
return `Hi, ${name}`;
};
- Stored in a variable
- Not hoisted — must be defined before calling
🔥 Arrow Functions (ES6)
Arrow functions are a shorter syntax, often used in modern JS code.
const greet = (name) => {
return `Hey, ${name}`;
};
If the function has only one expression, you can simplify further:
const greet = name => `Hey, ${name}`;
⚠ Arrow Function Quirks
- No own
thisbinding - Cannot be used as constructors (
new) - Don’t have
argumentsobject
More on this in the OOP section.
🎯 Parameters and Arguments
You can pass as many arguments as you want — or none at all.
function sum(x, y) {
return x + y;
}
console.log(sum(2, 3)); // 5
📦 Default Parameters
function greet(name = "Guest") {
return `Welcome, ${name}`;
}
console.log(greet()); // "Welcome, Guest"
📦 Rest Parameters (...)
Gathers all extra arguments into an array:
function addAll(...nums) {
return nums.reduce((a, b) => a + b);
}
console.log(addAll(1, 2, 3, 4)); // 10
💡 Return Values
Functions can return any type of value:
function getUser() {
return { name: "Bob", age: 42 };
}
If no return, the function returns undefined.
🧠 Anonymous Functions
Used as inline callbacks:
setTimeout(function() {
console.log("Hello after 2 seconds");
}, 2000);
Or with arrow syntax:
setTimeout(() => {
console.log("Hello again!");
}, 2000);
🔁 Functions as First-Class Citizens
You can:
- Assign them to variables
- Pass them as arguments
- Return them from other functions
function shout(fn) {
return function(msg) {
return fn(msg.toUpperCase());
};
}
const log = message => console.log(message);
const loudLog = shout(log);
loudLog("hello"); // "HELLO"
✅ Quick Recap
- Use function declarations when hoisting is needed
- Prefer arrow functions for short, anonymous functions
- Default and rest parameters make your code flexible
- Functions can be passed around like variables
7. Arrays and Objects in JavaScript
In JavaScript, data is often organized using arrays and objects. Arrays store ordered lists, while objects store key–value pairs — and mastering both is essential for real-world coding.
🧺 Arrays
✅ Creating an Array
let colors = ["red", "green", "blue"];
🧮 Accessing Elements
console.log(colors[0]); // "red"
console.log(colors.length); // 3
🔧 Modifying Arrays
colors.push("yellow"); // Add to end
colors.pop(); // Remove from end
colors.unshift("purple"); // Add to start
colors.shift(); // Remove from start
🔁 Iterating Over Arrays
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
✅ With for...of
for (let color of colors) {
console.log(color);
}
🧰 Array Methods You’ll Use Constantly
🔎 .find()
let numbers = [1, 2, 3, 4];
let found = numbers.find(num => num > 2); // 3
🔁 .map()
Transforms every item:
let doubled = numbers.map(num => num * 2); // [2, 4, 6, 8]
🧹 .filter()
Filters items based on a condition:
let evens = numbers.filter(num => num % 2 === 0); // [2, 4]
🧮 .reduce()
Accumulates a result:
let sum = numbers.reduce((acc, curr) => acc + curr, 0); // 10
📦 Objects
Objects use key–value pairs for structured data.
✅ Creating an Object
let person = {
name: "Alice",
age: 30,
isEmployed: true
};
🧠 Accessing Object Properties
console.log(person.name); // "Alice"
console.log(person["age"]); // 30
✏ Modifying & Adding Properties
person.age = 31;
person.city = "New York";
🔄 Looping Through an Object
for (let key in person) {
console.log(key, person[key]);
}
🧪 Arrays vs Objects
| Feature | Array | Object |
|---|---|---|
| Ordered? | Yes | No |
| Indexed by | Numbers (0, 1, 2…) | Strings or Symbols |
| Ideal for | Lists, sequences | Structured data, records |
✂️ Destructuring (ES6)
✅ Array Destructuring
let [first, second] = ["apple", "banana"];
console.log(first); // "apple"
✅ Object Destructuring
let { name, age } = person;
console.log(name); // "Alice"
You can even rename keys:
let { age: yearsOld } = person;
console.log(yearsOld); // 31
✅ Quick Recap
- Arrays = ordered lists (
[]), Objects = key–value maps ({}) - Use
.map(),.filter(),.reduce()for elegant array transformations - Use
for...offor arrays,for...infor objects - Destructuring helps pull out values cleanly and quickly
8. DOM Manipulation in JavaScript
The DOM (Document Object Model) is the in-memory structure of your web page. JavaScript lets you access, modify, and respond to that structure in real time.
Every element on a page — headers, paragraphs, buttons, forms — is a node in the DOM.
🧭 Selecting Elements
✅ document.querySelector()
Selects the first element that matches a CSS selector:
const heading = document.querySelector("h1");
✅ document.querySelectorAll()
Selects all matching elements (returns a NodeList):
const allParagraphs = document.querySelectorAll("p");
🧱 Other Selectors
document.getElementById("main");
document.getElementsByClassName("btn");
document.getElementsByTagName("div");
📝 Changing Text and HTML
✅ .textContent
const title = document.querySelector("h1");
title.textContent = "Updated Title";
✅ .innerHTML
const container = document.querySelector(".box");
container.innerHTML = "<strong>Bold Text</strong>";
⚠️ Use .textContent if you don’t want to insert raw HTML.
🎨 Changing CSS Styles
const box = document.querySelector(".box");
box.style.backgroundColor = "blue";
box.style.padding = "20px";
CSS property names are camelCased (backgroundColor, not background-color).
🧩 Working with Classes
box.classList.add("active");
box.classList.remove("hidden");
box.classList.toggle("highlight");
box.classList.contains("active"); // true/false
This is great for triggering animations, transitions, and styles.
✏️ Creating and Appending Elements
const newPara = document.createElement("p");
newPara.textContent = "This was created by JavaScript!";
document.body.appendChild(newPara);
❌ Removing Elements
newPara.remove(); // Instantly deletes the node
🧠 Dynamic Attributes
const link = document.querySelector("a");
link.setAttribute("href", "https://example.com");
link.getAttribute("href"); // returns the current value
link.removeAttribute("target");
🧼 Form Input Example
HTML:
<input id="nameInput" />
<button id="submitBtn">Submit</button>
<p id="output"></p>
JS:
const input = document.getElementById("nameInput");
const btn = document.getElementById("submitBtn");
const output = document.getElementById("output");
btn.addEventListener("click", () => {
output.textContent = `Hello, ${input.value}!`;
});
✅ Quick Recap
querySelectorgrabs any element using CSS selectors- Use
.textContent,.innerHTML,.style, and.classListto change things - Create and remove elements dynamically with
createElement()and.remove() - Use
setAttribute()to work with element attributes
9. Events and Event Listeners in JavaScript
JavaScript is event-driven. That means it responds to user actions: clicks, mouse moves, key presses, form submissions, and more. Events are the bridge between HTML and JavaScript logic.
🎯 Listening to Events
✅ addEventListener()
Attach behavior to any DOM element:
const button = document.querySelector("#myBtn");
button.addEventListener("click", () => {
console.log("Button was clicked!");
});
You can listen to many types of events:click, input, submit, keydown, mousemove, change, scroll, dblclick, etc.
📋 Event Object (event)
You can access details about the event using the event parameter:
button.addEventListener("click", (event) => {
console.log(event.target); // element that triggered the event
});
🧾 Input Events
HTML:
<input type="text" id="nameInput" />
<p id="livePreview"></p>
JS:
const input = document.getElementById("nameInput");
const preview = document.getElementById("livePreview");
input.addEventListener("input", () => {
preview.textContent = input.value;
});
This updates the paragraph live as you type.
🧪 submit Events
HTML:
<form id="signupForm">
<input type="email" />
<button type="submit">Sign up</button>
</form>
JS:
const form = document.getElementById("signupForm");
form.addEventListener("submit", (e) => {
e.preventDefault(); // Prevents page reload
console.log("Form submitted!");
});
Always use e.preventDefault() if you want to handle a form without reloading the page.
🧠 Keyboard Events
document.addEventListener("keydown", (e) => {
console.log("Key pressed:", e.key);
});
Useful for game controls, shortcuts, or accessibility enhancements.
🎨 Mouse Events
element.addEventListener("mouseenter", () => {
element.style.backgroundColor = "lightblue";
});
element.addEventListener("mouseleave", () => {
element.style.backgroundColor = "";
});
🔄 Removing an Event Listener
function logClick() {
console.log("Clicked");
}
button.addEventListener("click", logClick);
button.removeEventListener("click", logClick);
The function must be named (not anonymous) to remove it later.
🧘 Bonus: Debouncing Input
Useful to prevent rapid firing of input events (e.g. search box):
function debounce(fn, delay) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), delay);
};
}
const logInput = debounce((e) => {
console.log("Search:", e.target.value);
}, 500);
searchBox.addEventListener("input", logInput);
✅ Quick Recap
- Use
addEventListener()to attach behavior - Access the event with
eventore - Always
preventDefault()when handling form submissions - Use named functions if you plan to remove listeners later
- Debounce expensive event handlers to optimize performance
10. Scope, Hoisting, and Closures in JavaScript
Understanding scope, hoisting, and closures is critical to writing clean, bug-free code in JavaScript. These concepts explain how variables behave, when they’re available, and how functions “remember” values.
🌍 Scope: Where Variables Live
🔹 Global Scope
Declared outside any function or block — accessible everywhere.
let globalVar = "I’m global";
function show() {
console.log(globalVar); // Accessible
}
🔹 Function Scope
Variables declared inside a function are only accessible inside that function.
function greet() {
let name = "Alice";
console.log(name);
}
console.log(name); // ❌ Error: name is not defined
🔹 Block Scope (with let and const)
let and const are block-scoped, while var is not.
if (true) {
let x = 5;
const y = 10;
}
console.log(x); // ❌ ReferenceError
⚠️ var Ignores Block Scope
if (true) {
var z = 99;
}
console.log(z); // ✅ 99 (not good!)
That’s why let and const are preferred — they respect block boundaries.
🚀 Hoisting: Lifting Declarations
In JavaScript, declarations are hoisted to the top of their scope. But behavior varies:
✅ Functions Are Fully Hoisted
sayHi(); // ✅ Works
function sayHi() {
console.log("Hello");
}
⚠️ var Is Hoisted — Without Value
console.log(a); // undefined
var a = 5;
It’s as if this happened:
var a; // hoisted
console.log(a); // undefined
a = 5;
❌ let and const Are Not Usable Before Declaration
console.log(b); // ❌ ReferenceError
let b = 10;
This is known as the Temporal Dead Zone (TDZ).
🧠 Closures: Functions Remember Their Scope
A closure is a function that remembers variables from where it was created, even if it’s called elsewhere.
🔐 Example:
function outer() {
let secret = "shh!";
return function inner() {
console.log(secret);
};
}
const reveal = outer();
reveal(); // "shh!"
Even though outer() has finished, inner() still remembers secret.
🧮 Closures in Practice: Private Variables
function counter() {
let count = 0;
return {
increment: () => ++count,
get: () => count
};
}
const c = counter();
c.increment(); // 1
c.increment(); // 2
console.log(c.get()); // 2
You’ve just made a simple object with private state!
✅ Quick Recap
- Scope determines where a variable is accessible
letandconstare block-scoped — use them instead ofvar- Hoisting means declarations are processed first
- Closures let functions “remember” variables from their environment
- Closures enable private variables and powerful callback patterns
11. Callbacks, Promises, and Async/Await
JavaScript is non-blocking, meaning it can run multiple tasks simultaneously — even though it’s single-threaded. This is key when dealing with network requests, file access, timers, and anything that takes time.
You’ll learn how to write clean, readable, and bug-free asynchronous code.
🌀 The Problem with Callbacks
A callback is a function passed into another function to be executed later.
function fetchData(callback) {
setTimeout(() => {
callback("Data loaded");
}, 1000);
}
fetchData((data) => {
console.log(data);
});
😖 Callback Hell
When callbacks are nested within callbacks, things get messy:
getUser(id, function(user) {
getPosts(user, function(posts) {
getComments(posts, function(comments) {
// 😵 deeply nested
});
});
});
🔐 Promises to the Rescue
A Promise is an object that represents the result of an async operation.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success!");
// or: reject("Error occurred");
}, 1000);
});
promise.then((data) => {
console.log(data); // "Success!"
});
📜 Chaining Promises
getUser()
.then(user => getPosts(user))
.then(posts => getComments(posts))
.then(comments => console.log(comments))
.catch(err => console.error(err));
.then() handles the success path..catch() handles any errors along the way.
✨ async / await: Syntactic Sugar for Promises
With async functions, you can write asynchronous code that looks synchronous.
async function loadData() {
try {
const user = await getUser();
const posts = await getPosts(user);
const comments = await getComments(posts);
console.log(comments);
} catch (err) {
console.error("Something went wrong:", err);
}
}
loadData();
Much cleaner than nested callbacks or .then() chains.
🧠 async / await Essentials
asyncmakes a function return a Promiseawaitpauses the function until the Promise resolvestry...catchis used for error handling- You can only use
awaitinsideasyncfunctions
✅ Example with Fetch
async function getJoke() {
const response = await fetch("https://api.chucknorris.io/jokes/random");
const data = await response.json();
console.log(data.value);
}
getJoke();
🧪 Mixing Promises and Async
You can use both:
async function main() {
const users = await fetchUsers();
users.forEach(async (user) => {
const details = await fetchUserDetails(user.id);
console.log(details);
});
}
⚠ Common Pitfalls
- Forgetting to return from a
.then()chain - Using
awaitoutside of anasyncfunction - Not handling rejected Promises (
.catch()ortry/catchis a must) - Firing too many awaits sequentially (instead, use
Promise.all())
⚡ Bonus: Run Async Tasks in Parallel
async function loadAll() {
const [user, posts] = await Promise.all([
getUser(),
getPosts()
]);
}
✅ Quick Recap
- Callbacks are useful but can become messy
- Promises make async code more readable and chainable
async/awaitmakes async code look synchronous- Always handle errors using
.catch()ortry...catch - Use
Promise.all()to run async tasks in parallel
12. ES6+ Features and Modern Syntax
In 2015, ECMAScript 6 (ES6) introduced major upgrades to JavaScript — making it cleaner, more expressive, and more powerful. Most modern JavaScript codebases rely on these features.
Let’s explore the most important ones:
🆕 let and const (Block-Scoped Variables)
Replace var entirely.
let count = 1; // can be reassigned
const pi = 3.14159; // cannot be reassigned
⚠ const is not immutable
You can’t reassign the variable, but you can mutate the object:
const user = { name: "Alice" };
user.name = "Bob"; // ✅ allowed
📦 Template Literals (Backticks `)
Allow multiline strings and interpolation:
let name = "Sam";
let greeting = `Hello, ${name}!`;
Supports multiline too:
let text = `
This is
a multi-line
string.
`;
✂️ Destructuring
✅ Array Destructuring
const [first, second] = ["apple", "banana"];
✅ Object Destructuring
const person = { name: "Alice", age: 30 };
const { name, age } = person;
You can rename during destructuring:
const { age: years } = person;
📦 Spread Operator (...)
✅ In Arrays
const nums = [1, 2, 3];
const moreNums = [...nums, 4, 5]; // [1, 2, 3, 4, 5]
✅ In Objects
const user = { name: "Alice" };
const updatedUser = { ...user, age: 25 };
📦 Rest Parameters
Collect remaining values into an array:
function sum(...numbers) {
return numbers.reduce((a, b) => a + b);
}
Used in function parameters or destructuring.
🚀 Arrow Functions (Short Syntax)
You’ve seen this before:
const add = (x, y) => x + y;
More concise, no this binding — great for callbacks and functional patterns.
🧪 for...of and for...in
✅ for...of: Loops through values
for (let val of ["a", "b", "c"]) {
console.log(val);
}
✅ for...in: Loops through keys
const obj = { a: 1, b: 2 };
for (let key in obj) {
console.log(key, obj[key]);
}
🔄 Enhanced Object Literals
Shorthand property and method definitions:
let x = 10;
let y = 20;
let point = {
x, // same as x: x
y,
print() {
console.log(`(${x}, ${y})`);
}
};
🧊 Default Parameters
function greet(name = "Guest") {
return `Hello, ${name}`;
}
🔗 Modules: import and export
✅ Named Exports
// utils.js
export function add(x, y) {
return x + y;
}
// main.js
import { add } from "./utils.js";
✅ Default Exports
// math.js
export default function subtract(x, y) {
return x - y;
}
// main.js
import subtract from "./math.js";
Make sure to include type="module" in your <script> tag when using in HTML.
✅ Quick Recap
letandconstare the new standard- Template literals improve string handling
- Destructuring simplifies variable extraction
- Spread/rest make your code flexible and elegant
- Arrow functions offer cleaner syntax and no
this - ES Modules (
import/export) support modular architecture
13. Error Handling and Debugging in JavaScript
Even the best developers write buggy code — but what separates professionals is how they handle it. JavaScript gives you powerful tools to catch, diagnose, and recover from errors.
🧯 try...catch for Safe Error Handling
try {
// code that might fail
let result = riskyOperation();
console.log(result);
} catch (error) {
console.error("Something went wrong:", error.message);
}
tryblock: code that might throw an errorcatchblock: runs if an error occurserroris an object with.name,.message,.stack
🔄 finally Block
Runs after try and catch — no matter what.
try {
openConnection();
} catch (err) {
console.log("Failed to connect");
} finally {
console.log("Cleanup done");
}
🧨 Throwing Custom Errors
You can create your own errors using throw:
function divide(a, b) {
if (b === 0) {
throw new Error("Division by zero!");
}
return a / b;
}
try {
divide(5, 0);
} catch (e) {
console.error(e.message); // "Division by zero!"
}
🛠 Using console for Debugging
The console is your best friend while writing JavaScript. Useful methods include:
| Method | Description |
|---|---|
console.log() | Standard output |
console.warn() | Yellow warning messages |
console.error() | Red error messages |
console.table() | Displays tabular data nicely |
console.trace() | Shows the call stack |
console.group() | Log groups (expand/collapse) |
🧪 console.table() Example
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 }
];
console.table(users);
🧭 Browser DevTools
All major browsers include robust tools for debugging JavaScript:
- Inspect Element (Right-click → Inspect)
- Go to the Console tab
- Switch to Sources to view your files
- Set Breakpoints in the code
- Use Watch, Call Stack, and Scope panels to trace values
🛑 Setting Breakpoints
In the DevTools Sources tab:
- Click the line number to pause execution at that point
- Use the step-over, step-into, and step-out controls to follow execution flow
🐞 Debugging with debugger Statement
Force a breakpoint in your code:
function test() {
let x = 10;
debugger; // Execution pauses here if DevTools is open
let y = x * 2;
}
⚠ Common Error Types
| Error Type | Description |
|---|---|
ReferenceError | Variable not declared |
TypeError | Invalid operation on a data type |
SyntaxError | Code parsing error |
RangeError | Value not within allowed range |
EvalError | Using eval() incorrectly (rare) |
✅ Quick Recap
- Use
try...catchto handle unexpected failures - Use
finallyfor guaranteed cleanup - Throw custom errors with meaningful messages
- Use
console.*methods anddebuggerto inspect code - Use DevTools breakpoints to step through and understand flow
- Know your error types to fix bugs faster
14. Modules and Code Organization in JavaScript
As your JavaScript projects grow, keeping all your code in a single file quickly becomes unmanageable. That’s where modules come in. They allow you to split code into reusable, maintainable pieces — and make collaboration, testing, and debugging much easier.
📦 Why Use Modules?
- Avoid global scope pollution
- Enable code reuse
- Organize by feature or function
- Allow lazy loading (only load what you need)
- Improve readability and testability
🔁 Old-School: Script Tags and Global Scope
<script src="utils.js"></script>
<script src="main.js"></script>
All code lives in the global namespace — any variable or function can collide. 😬
🚀 Modern JS Modules (ESM)
Using import and export, you can write modular code natively.
✅ Exporting
Named exports:
// math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
Default export:
// greet.js
export default function greet(name) {
return `Hello, ${name}!`;
}
✅ Importing
// main.js
import { add, PI } from './math.js';
import greet from './greet.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
console.log(greet("Sam")); // Hello, Sam!
Make sure to use .js extensions when importing ES Modules.
🔖 Use type="module" in HTML
To use ES Modules in the browser:
<script type="module" src="main.js"></script>
- Makes
import/exportwork - Script runs in strict mode automatically
- Modules are deferred by default
📂 File Structure Example
/project
|- index.html
|- main.js
|- math.js
|- greet.js
This keeps your logic clean and scoped to specific files.
🧱 Module Rules to Remember
- Variables inside a module are scoped to that file
importandexportcan only appear at the top level- You can use
asto rename:
import { add as sum } from './math.js';
🌐 Using Modules in Node.js (v12+)
You can use ES Modules in Node.js by:
- Setting
"type": "module"inpackage.json - Using
.mjsextensions - Or using top-level
awaitin newer Node versions
🔁 CommonJS (require) in Node.js
Legacy module system in Node:
// math.js
exports.add = (a, b) => a + b;
// main.js
const { add } = require('./math');
Still widely used, especially in older packages.
⚙️ Module Bundlers: Webpack, Vite, Parcel
Modern JS apps often use tools to:
- Combine modules into a single bundle
- Minify code for performance
- Handle CSS, images, and other assets
These tools allow you to write modular code, then ship optimized builds for browsers.
✅ Quick Recap
- Modules help organize and reuse code
- Use
exportandimportfor clean modular architecture - Default exports are imported without curly braces
- Use
type="module"in HTML to enable ESM - Node.js supports both CommonJS (
require) and ESM (import)
15. JavaScript Classes and Object-Oriented Programming
OOP is a programming paradigm where code is organized around objects — entities that combine data (properties) and behavior (methods). JavaScript supports OOP via both traditional prototypes and modern ES6 classes.
🧱 Creating Objects with Classes
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);
}
}
const user = new Person("Alice", 30);
user.greet(); // "Hi, I'm Alice and I'm 30 years old."
🔍 Key Terms
class: Blueprint for creating objectsconstructor: Runs when an object is instantiatedthis: Refers to the current object instancenew: Creates a new object based on the class
🧬 Inheritance
Use extends and super to create a subclass.
class Employee extends Person {
constructor(name, age, role) {
super(name, age); // calls the parent constructor
this.role = role;
}
describe() {
console.log(`${this.name} is a ${this.role}`);
}
}
const emp = new Employee("Bob", 40, "Manager");
emp.greet(); // from Person
emp.describe(); // from Employee
🔁 Method Overriding
A subclass can override parent methods:
class Animal {
speak() {
console.log("The animal makes a sound.");
}
}
class Dog extends Animal {
speak() {
console.log("The dog barks.");
}
}
🔐 Encapsulation with Private Fields (#)
class BankAccount {
#balance = 0;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const acct = new BankAccount();
acct.deposit(100);
console.log(acct.getBalance()); // 100
// console.log(acct.#balance); ❌ Error: private field
Private fields keep internal state hidden from outside interference.
🧰 Static Methods
Called on the class itself — not on an instance.
class MathUtil {
static square(x) {
return x * x;
}
}
console.log(MathUtil.square(4)); // 16
🧠 Understanding Prototypes (Behind the Scenes)
Even before class syntax, JavaScript was object-oriented via prototype chains:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound.`);
};
const cat = new Animal("Mittens");
cat.speak(); // "Mittens makes a sound."
Every function in JS has a .prototype object, which is used for shared methods.
Modern class syntax is just syntactic sugar over this model.
📦 Real-World Example: Product Class
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
discount(percent) {
this.price *= 1 - percent / 100;
}
}
const book = new Product("JS Handbook", 20);
book.discount(10);
console.log(book.price); // 18
🧠 When to Use Classes
- Reusable logic across multiple objects
- Consistent structure (like Models, Entities)
- Object hierarchies with shared functionality
- State and behavior in one place (encapsulation)
✅ Quick Recap
classsyntax provides modern OOP in JavaScript- Use
constructor()to set up new instances - Use
extendsto create subclasses super()calls the parent constructor- Private fields use
# - Static methods live on the class, not instances
- All JS objects ultimately inherit from a prototype chain
16. The Event Loop and Concurrency in JavaScript
JavaScript is single-threaded, but it handles asynchronous operations like network requests, file reads, and timers incredibly well. That’s thanks to something called the event loop.
Understanding the event loop is essential for writing performant and non-blocking JavaScript code.
🧠 The JavaScript Runtime Model
JavaScript runs inside an environment (like a browser or Node.js), which includes:
- Call Stack
- Web APIs / Node APIs (timers, DOM, fetch, etc.)
- Callback / Task Queue
- Microtask Queue (Promises, MutationObservers)
- The Event Loop 🌀
🔁 The Call Stack
This is where functions are executed. Each function call is pushed onto the stack.
function sayHello() {
console.log("Hello");
}
sayHello(); // Call stack: [sayHello()]
When sayHello() finishes, it gets popped off.
⏱ setTimeout and Web APIs
When you use setTimeout(), the function does not block the main thread.
console.log("Start");
setTimeout(() => {
console.log("After 2 seconds");
}, 2000);
console.log("End");
Output:
Start
End
After 2 seconds
Why? The timeout is handed off to the Web API, and after 2 seconds, it gets added to the task queue, where the event loop picks it up after the call stack is empty.
🧵 The Event Loop in Action
The event loop constantly checks:
“Is the call stack empty?
If yes, is there anything in the queue?
If yes, push it to the call stack.”
This lets JavaScript execute async tasks without multi-threading.
🔂 Microtasks vs Macrotasks
- Microtasks:
Promise.then(),async/await,queueMicrotask() - Macrotasks:
setTimeout,setInterval,setImmediate
Microtasks have higher priority and run before macrotasks.
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
Output:
1
4
3 ← microtask
2 ← macrotask
🔒 Blocking the Event Loop
Heavy computations block the UI:
// ❌ This will freeze the browser
for (let i = 0; i < 1e9; i++) {}
Use Web Workers or break up the task with setTimeout() or requestIdleCallback() to keep the UI responsive.
📦 async/await vs Event Loop
async function test() {
console.log("A");
await Promise.resolve();
console.log("B");
}
test();
console.log("C");
Output:
A
C
B
Even though await looks like blocking, it actually puts the rest of the function into the microtask queue.
🧪 Real-World Analogy
- Call stack = kitchen counter
- Web APIs = timers, fridges, mailboxes
- Callback queue = waiting list
- Event loop = kitchen assistant checking “Is the counter clear? Can I serve the next plate?”
✅ Quick Recap
- JavaScript is single-threaded, but non-blocking
- The event loop coordinates async code and queues
- Promises/microtasks run before
setTimeout()and friends - Avoid blocking the main thread with heavy code
- Async/await is powered by microtasks — it’s just Promise sugar
17. Working with APIs and fetch()
Modern JavaScript apps constantly communicate with external data sources — weather APIs, databases, authentication services, social media platforms, and more.
The most common way to do this in the browser is through the Fetch API.
🌐 What is an API?
An API (Application Programming Interface) lets your app talk to another service, often over the internet. Most APIs respond in JSON format, which is easy to work with in JavaScript.
🚀 The Fetch API
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error("Error:", error);
});
✅ It returns a Promise
- Use
.then()to wait for the response - Call
.json()to parse the response body - Handle errors with
.catch()
✅ GET Request Example
fetch("https://api.agify.io?name=sam")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error("Failed:", err));
Sample response:
{ "name": "sam", "age": 54, "count": 12345 }
📤 POST Request Example
fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title: "Hello",
body: "This is a post",
userId: 1
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
✨ Using async/await with Fetch
async function getUser() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await res.json();
console.log(user.name);
} catch (err) {
console.error("Fetch failed:", err);
}
}
Much cleaner and easier to read than nested .then() chains.
❗ Common Errors & Fixes
| Issue | Cause/Explanation |
|---|---|
fetch not returning data | You forgot .json() |
| CORS error | Cross-origin request blocked by the server |
| 404/500 errors | Server issue or invalid endpoint |
| Silent failures | Use .catch() or try...catch |
🧪 Handling HTTP Status Codes
async function fetchUser() {
const res = await fetch("/api/user");
if (!res.ok) {
throw new Error(`Server error: ${res.status}`);
}
const data = await res.json();
return data;
}
Always check res.ok or res.status before assuming success.
🧼 Displaying Fetched Data
HTML:
<ul id="postList"></ul>
JS:
async function loadPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const posts = await res.json();
const list = document.getElementById("postList");
posts.forEach(post => {
const li = document.createElement("li");
li.textContent = post.title;
list.appendChild(li);
});
}
loadPosts();
✅ Quick Recap
- Use
fetch()to make HTTP requests - Chain
.then()or useasync/await - Parse JSON with
.json() - Use
try...catchor.catch()to handle errors - Always check response status (
res.ok) - Inject dynamic data into the DOM using
.appendChild()or.innerHTML
18. Web Storage and Cookies in JavaScript
When you want to save small pieces of data on the client side, you can use:
localStorage— persists even after the browser is closedsessionStorage— wiped when the browser or tab is closeddocument.cookie— older, more limited, but can be sent to the server
🗃 localStorage
✅ Set an item
localStorage.setItem("username", "Alice");
✅ Get an item
const name = localStorage.getItem("username"); // "Alice"
✅ Remove an item
localStorage.removeItem("username");
✅ Clear everything
localStorage.clear();
Stored values are always strings, so use JSON.stringify() and JSON.parse() for objects.
const user = { name: "Bob", age: 25 };
localStorage.setItem("user", JSON.stringify(user));
const savedUser = JSON.parse(localStorage.getItem("user"));
🧭 sessionStorage
Identical to localStorage — but data only lives for the current session.
sessionStorage.setItem("tempToken", "abc123");
Use it when you want short-term data that vanishes after tab/browser close.
🍪 Cookies (Old but Useful)
Cookies are small key-value pairs stored in the browser and sent to the server with every request.
✅ Set a cookie
document.cookie = "theme=dark; path=/; max-age=3600";
✅ Read cookies
console.log(document.cookie); // "theme=dark; loggedIn=true"
You can parse this manually or use helper libraries like js-cookie.
⚠ Limitations of Cookies
- Max size ~4KB
- Slower and less secure for client-only needs
- Automatically included in every HTTP request
Use localStorage or sessionStorage unless you need server-side persistence.
🛡 Storing Tokens (Important)
For storing authentication tokens:
localStorage: Persistent but vulnerable to XSSsessionStorage: Safer for temporary accesshttpOnly cookies: Best for server-controlled authentication (not readable by JS)
🧪 Mini Project: Remember Theme
// On page load
const savedTheme = localStorage.getItem("theme");
document.body.className = savedTheme || "light";
// On user toggle
function toggleTheme() {
const newTheme = document.body.className === "light" ? "dark" : "light";
document.body.className = newTheme;
localStorage.setItem("theme", newTheme);
}
✅ Quick Recap
- Use
localStorageto persist data across sessions - Use
sessionStoragefor temporary browser-tab data - Use
JSON.stringify()andJSON.parse()for non-string data - Avoid storing sensitive data in
localStorage - Use cookies if data needs to be sent to the server
19. JavaScript DOM Projects and Real-World Practice
Theory is essential — but nothing beats building. Let’s explore hands-on JavaScript projects to reinforce your DOM skills, event handling, async logic, storage, and more.
Each project below includes a concept breakdown and feature checklist.
🧠 1. To-Do List App
Concepts:
- DOM manipulation
input+ button eventslocalStoragefor persistence
Features:
- Add/delete tasks
- Mark tasks as complete
- Save tasks in
localStorage - Clear all button
// Pseudo flow
1. On page load → load tasks from localStorage
2. On button click → add task to DOM + localStorage
3. On checkbox click → toggle complete
4. On delete → remove from DOM + localStorage
🎲 2. Random Quote Generator
Concepts:
- Array usage
- DOM updates
- Event listeners
Features:
- Show random quote on button click
- Change background color randomly
const quotes = [
"Stay hungry, stay foolish.",
"Talk is cheap. Show me the code.",
"Code never lies, comments sometimes do."
];
function showRandomQuote() {
const quote = quotes[Math.floor(Math.random() * quotes.length)];
document.getElementById("quote").textContent = quote;
}
⏰ 3. Digital Clock
Concepts:
setInterval()- Date/time formatting
- DOM updates
function updateClock() {
const now = new Date();
document.getElementById("clock").textContent = now.toLocaleTimeString();
}
setInterval(updateClock, 1000);
💬 4. Fetch a Joke
Concepts:
- Fetch API
- JSON parsing
- DOM injection
async function loadJoke() {
const res = await fetch("https://api.chucknorris.io/jokes/random");
const data = await res.json();
document.getElementById("joke").textContent = data.value;
}
💡 5. Theme Toggler
Concepts:
- DOM class changes
localStorageclickevent handling
function toggleTheme() {
const current = document.body.className;
const next = current === "light" ? "dark" : "light";
document.body.className = next;
localStorage.setItem("theme", next);
}
📚 Project Design Tips
✅ Break It Down
Think in terms of:
- Data — what are we storing/manipulating?
- Events — what actions can the user take?
- DOM — what elements will be affected?
- Storage — should the app remember anything?
📁 Folder Structure (Simple)
/project
|- index.html
|- style.css
|- script.js
Use separate files for maintainability and clarity.
🧠 Use Comments!
Comment your logic clearly to trace bugs or revisit code later:
// Step 1: Load tasks from storage
// Step 2: Add task element to DOM
🔁 Practice Patterns
These mini-challenges build your muscle memory:
- Create a counter that increases every second
- Build a character counter for an input box
- Add keyboard shortcuts to change background color
- Save form inputs to
localStorageautomatically - Make a quiz app with score tracking
✅ Quick Recap
- Apply your JavaScript skills to small, focused projects
- Practice DOM manipulation, event handling, fetch, and storage
- Break problems into small steps
- Build often, build ugly, then refine
- Store, retrieve, and display dynamic data
20. Final Recap and Next Steps
You’ve just walked through core JavaScript, modern syntax, async programming, the DOM, modules, storage, APIs, and real-world practice. That’s a huge achievement. Here’s what to remember — and where to go next.
🧠 What You’ve Learned
🔤 Basics
- Variables:
let,const,var - Data types: strings, numbers, booleans, objects, arrays
- Type coercion and truthiness
🔁 Logic & Flow
- Conditionals:
if,else,switch - Loops:
for,while,for...of,for...in - Functions: declarations, expressions, arrow functions
🧱 Structures
- Objects: properties, methods
- Arrays: methods like
.map(),.filter(),.reduce() - Destructuring, spread/rest operators
🧰 Advanced Syntax
- Template literals
- Default parameters
- ES Modules (
import/export)
🚀 Async JavaScript
- Callbacks, Promises,
async/await - The Event Loop, Microtasks vs Macrotasks
fetch()and HTTP requests
🌐 Web APIs
- DOM manipulation:
getElementById,.textContent,.classList - Events:
click,submit,keydown - Storage:
localStorage,sessionStorage,cookies
🏗 Projects
- Practical app patterns: To-Do list, Theme toggler, Joke fetcher
- State + event-driven development
- Working with user inputs and saving data
🎯 Where to Go Next
📘 Deepen Core Knowledge
- Closures and scope
- Recursion
- Higher-order functions
- Error handling patterns
- Regular expressions
🌐 Frontend Frameworks
- React – component-based UI
- Vue – declarative and beginner-friendly
- Svelte – compiler-based reactivity
🛠 Tools to Learn
- NPM and package management
- Webpack, Vite, Parcel (bundlers)
- ESLint and Prettier (code quality)
- Testing: Jest, Cypress
📦 APIs and Backend
- Learn to build your own API with Node.js + Express
- Work with databases like MongoDB or PostgreSQL
- Use async patterns for full-stack apps
💼 Building a Portfolio
Create real projects to showcase:
- Weather app using an API
- Budget tracker using localStorage
- Quiz or flashcard app
- Notes app with search and save
- A small full-stack app (Node + Mongo + JS)
🚀 Final Words
JavaScript is the language of the web, and with the foundations you’ve built, you’re ready to build apps, work with real users, and dive into advanced topics.
Keep experimenting. Build projects. Make mistakes. Read docs. Ask questions. Refactor your old code. You are now officially a JavaScript developer.
