JavaScript is a programming language that runs in browsers and servers. It powers interactive web applications. Think of it as the language that makes websites respond to your clicks, form submissions, and other interactions.
Key characteristics:
- Dynamic typing: Variables can change types (we'll discuss why this is risky)
- Event-driven: Responds to user actions
- Functional: Treats functions as first-class citizens
- Asynchronous: Can perform tasks without blocking execution
2. Linking JavaScript Files Using <script> Tag
-
Inline JavaScript: Place JavaScript code directly within the HTML using a
<script>tag.<script> console.log("Hello, World!"); </script>
-
External JavaScript: Link an external JavaScript file using the
srcattribute.<script src="script.js"></script>
- Place the
<script>tag at the end of the<body>to ensure the HTML is loaded before the script runs. - Single-line:
// This is a comment. - Multi-line:
/* This is a multi-line comment */
- Place the
-
console Logging with JavaScript
console.log(): Logs general information.console.info(): Logs informational messages.console.warn(): Logs warnings.prompt(): Displays a prompt to take user input.alert(): Displays a popup message.
Example:
console.log("Hello"); console.warn("Warning!"); console.info("Info!"); alert("Alert!"); let name = prompt("Enter your name:"); console.log(`Hello, ${name}!`);
When to use const:
- Default choice for all declarations
- Prevents accidental reassignment
- Makes intent clear: "this won't change"
- Works in React/Next.js for props and state
When to use let:
- Loop counters
- Values that change over time
- Conditional assignments
// Primitive: Immutable (e.g., numbers, strings).
// Reference (Relative): Mutable (e.g., objects, arrays).
// Variable declarations
let name = "Claude"; // Block-scoped variable
const PI = 3.14159; // Constant, cannot be reassigned
var oldWay = "legacy"; // Function-scoped (less preferred in modern JS)
// Data types
let string = "Hello"; // String
let number = 42; // Number
let decimal = 3.14; // Number (JavaScript doesn't distinguish integers/floats)
let boolean = true; // Boolean
let nullValue = null; // Null
let undefinedVar; // Undefined
let bigInt = 9007199254740991n; // BigInt for large integers
let symbol = Symbol("id"); // Symbol, unique identifier
// Arrays
let array = [1, 2, 3, "mix", true];
let emptyArray = [];
// Objects
let person = {
name: "Alice",
age: 30,
isStudent: false,
};length: Returns the number of characters in a string.toUpperCase(): Converts the string to uppercase.toLowerCase(): Converts the string to lowercase.indexOf(): Finds the position of the first occurrence of a specified value.lastIndexOf(): Finds the position of the last occurrence of a specified value.slice(): Extracts a part of a string and returns it as a new string. It takes a start index and an optional end index (not inclusive).substring(): Similar to slice(), but it does not accept negative indices.replace(): Replaces the first occurrence of a specified value with another value.replaceAll(): Replaces all occurrences of a specified value.trim(): Removes whitespace from both ends of a string.trimStart(): Removes whitespace from the start of a string.trimEnd(): Removes whitespace from both the end of a string.split(): Splits a string into an array of substrings based on a specified separator.charAt(): Returns the character at a specified index.charCodeAt(): Returns the Unicode of the character at a specified index.includes(): Checks if a string contains a specified value.startsWith(): Checks if a string starts with a specified value.endsWith(): Checks if a string ends with a specified value.
// Arithmetic operators
let sum = 5 + 10; // Addition
let difference = 10 - 5; // Subtraction
let product = 5 * 10; // Multiplication
let quotient = 10 / 5; // Division
let remainder = 10 % 3; // Modulus (remainder)
let exponent = 2 ** 3; // Exponentiation (2^3 = 8)
// Assignment operators
let x = 10;
x += 5; // Same as: x = x + 5
x -= 3; // Same as: x = x - 3
x *= 2; // Same as: x = x * 2
x /= 4; // Same as: x = x / 4
x %= 3; // Same as: x = x % 3
// Comparison operators
let isEqual = 5 == "5"; // Equal (converts types)
let isStrictEqual = 5 === 5; // Strict equal (no type conversion)
let isNotEqual = 5 != "6"; // Not equal
let isStrictNotEqual = 5 !== "5"; // Strict not equal
let isGreater = 10 > 5; // Greater than
let isLess = 5 < 10; // Less than
let isGreaterOrEqual = 5 >= 5; // Greater than or equal
let isLessOrEqual = 5 <= 5; // Less than or equal
// Logical operators
let and = true && false; // Logical AND
let or = true || false; // Logical OR
let not = !true; // Logical NOT
console.log(x && y); // false (AND - both must be true)
console.log(x || y); // true (OR - at least one must be true)
console.log(!x); // false (NOT - negates)
// Nullish coalescing
let username = null;
let displayName = username ?? "Guest"; // "Guest" if username is null/undefined
// Optional chaining
let value = object?.property?.nestedProp; // Avoids errors if a property doesn't exist- Choosing the Right Loop
- Use
forwhen you know the number of iterations - Use
whilewhen the number of iterations is unknown - Use
for...offor arrays and other iterables - Use
for...infor object properties - Use array methods (
map,filter, etc.) when transforming data.
// If statements
if (condition) {
// execute if condition is true
} else if (anotherCondition) {
// execute if anotherCondition is true
} else {
// execute if no conditions are true
}
// Ternary operator
let status = age >= 18 ? "adult" : "minor";
// Switch statements
const day = "Monday";
switch (day) {
case "Monday":
console.log("Start of week");
break; // Don't forget break!
case "Friday":
console.log("Almost weekend");
break;
case "Saturday":
case "Sunday":
console.log("Weekend!");
break;
default:
console.log("Regular day");
}
// Loops
// For loop
for (let i = 0; i < 5; i++) {
console.log(i);
}
// While loop
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
// Do-while loop
do {
console.log(i);
i++;
} while (i < 5);
// For...of loop (for iterables like arrays)
let numbers = [1, 2, 3];
for (let num of numbers) {
console.log(num);
}
// For...in loop (for object properties)
let person = { name: "John", age: 30 };
for (let key in person) {
console.log(key + ": " + person[key]);
}
// Break and continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // Skip this iteration
if (i === 8) break; // Exit the loop
console.log(i);
}
**//** Recursion in JavaScript
function factorial(n) {
if (n === 0) return 1;
return n * factorial(n - 1);
}
console.log(factorial(5)); // Outputs 120const numbers = [1, 2, 3];
// forEach runs a function for each item
numbers.forEach((num) => {
console.log(num * 2); // 2, 4, 6
});
// With index
numbers.forEach((num, index) => {
console.log(`Index ${index}: ${num}`);
});const numbers = [1, 2, 3];
// map creates a NEW array with transformed values
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6]
// Real-world: transform API data
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
const names = users.map((user) => user.name);
console.log(names); // ["Alice", "Bob"]const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
// Real-world: filter active users
const activeUsers = users.filter((user) => user.isActive === true);const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
const user = users.find((u) => u.id === 2);
console.log(user); // { id: 2, name: "Bob" }// Function declaration
function greet(name) {
return "Hello, " + name + "!";
}
// Function expression
const greet = function (name) {
return "Hello, " + name + "!";
};
// Arrow functions
const greet = (name) => {
return "Hello, " + name + "!";
};
// Shorthand arrow function (implicit return)
const greet = (name) => "Hello, " + name + "!";
// Default parameters
function greet(name = "Guest") {
return "Hello, " + name + "!";
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
// Immediately Invoked Function Expression (IIFE)
(function () {
console.log("This runs immediately");
})();// Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
// Function with multiple parameters
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // 8// Long form
const multiply = (a, b) => {
return a * b;
};
// Short form (implicit return)
const multiply = (a, b) => a * b;
// Single parameter (parentheses optional)
const square = (n) => n * n;
// No parameters
const getRandomNumber = () => Math.random();Why arrow functions?
- Cleaner syntax
- Lexical
thisbinding (important for React) - Default choice for modern code
// Default parameters
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
console.log(greet("Alice")); // "Hello, Alice!"
// Rest parameters - collect remaining arguments
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10// Object destructuring
const user = {
name: "Alice",
age: 25,
email: "alice@example.com",
};
// Instead of:
// const name = user.name;
// const age = user.age;
// Do this:
const { name, age, email } = user;
console.log(name); // "Alice"
// Array destructuring
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first); // "red"
// Skip items
const [primary, , tertiary] = colors;
console.log(tertiary); // "blue"
// Function parameters (very common in React)
function displayUser({ name, age }) {
console.log(`${name} is ${age} years old`);
}
displayUser(user);// Spread array
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// Spread object
const obj1 = { name: "Alice", age: 25 };
const obj2 = { city: "NYC", country: "USA" };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { name: "Alice", age: 25, city: "NYC", country: "USA" }
// Override properties
const updated = { ...obj1, age: 26 };
console.log(updated); // { name: "Alice", age: 26 }// String interpolation
let name = "Claude";
let greeting = `Hello, ${name}!`;
// Multi-line strings
let multiLine = `This is a
multi-line
string`;- Adding and Removing Elements:
push(): Adds an item to the end of an array.pop(): Removes the last item from an array.unshift(): Adds elements to the beginning of an array.shift(): Removes the first element from an array.length: Returns the number of elements in the array.
- Array Methods:
splice()andslice():
splice(start, deleteCount, items...): Adds/removes items to/from an array.slice(start, end): Returns a shallow copy of a portion of an array into a new array.
- Looping Through Arrays with
forEach():
- forEach() method executes a provided function for each element in the array.
- What is the difference between
push()andunshift()?
push()adds an element to the end of an array.unshift()adds an element to the beginning of an array.
- What’s the difference between
splice()andslice()?
splice()modifies the original array by adding/removing elements.slice()does not modify the original array; it creates a new array containing the selected portion.
// Creating arrays
let fruits = ["apple", "banana", "orange"];
// Accessing elements
let firstFruit = fruits[0]; // "apple"
// Modifying arrays
fruits.push("grape"); // Add to end
fruits.unshift("strawberry"); // Add to beginning
fruits.pop(); // Remove from end
fruits.shift(); // Remove from beginning
fruits.splice(1, 1, "kiwi"); // Remove and/or insert at position
// Iterating arrays
fruits.forEach((fruit) => console.log(fruit));
// Array transformations
let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map((num) => num * 2); // [2, 4, 6, 8, 10]
let evens = numbers.filter((num) => num % 2 === 0); // [2, 4]
let sum = numbers.reduce((total, num) => total + num, 0); // 15
// Finding elements
let found = numbers.find((num) => num > 3); // 4
let foundIndex = numbers.findIndex((num) => num > 3); // 3
let includes = numbers.includes(3); // true
// Sorting
let sortedNumbers = [...numbers].sort((a, b) => a - b);// Creating objects
let person = {
firstName: "John",
lastName: "Doe",
age: 30,
// Method
fullName() {
return this.firstName + " " + this.lastName;
},
};
// Accessing properties
console.log(person.firstName); // Dot notation
console.log(person["lastName"]); // Bracket notation
// Adding/modifying properties
person.email = "john@example.com";
person["phone"] = "123-456-7890";
// Object destructuring
let { firstName, age } = person;
// Object spread
let personCopy = { ...person };
let personWithTitle = { ...person, title: "Developer" };
// Object methods
let keys = Object.keys(person); // Array of property names
let values = Object.values(person); // Array of property values
let entries = Object.entries(person); // Array of [key, value] pairs
let merged = Object.assign({}, obj1, obj2); // Merge objects// Class declaration
class Person {
// Constructor
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// Method
fullName() {
return this.firstName + " " + this.lastName;
}
// Getter
get name() {
return this.firstName;
}
// Setter
set name(value) {
this.firstName = value;
}
// Static method
static isHuman() {
return true;
}
}
// Inheritance
class Employee extends Person {
constructor(firstName, lastName, jobTitle) {
super(firstName, lastName); // Call parent constructor
this.jobTitle = jobTitle;
}
// Override method
fullName() {
return super.fullName() + ", " + this.jobTitle;
}
}
// Creating instances
let person = new Person("John", "Doe");
let employee = new Employee("Jane", "Smith", "Developer");// Creating a promise
const promise = new Promise((resolve, reject) => {
// Async operation
if (success) {
resolve(value); // Promise fulfilled
} else {
reject(error); // Promise rejected
}
});
// Consuming promises
promise
.then((result) => {
// Handle successful result
return anotherPromise;
})
.then((result2) => {
// Handle result from anotherPromise
})
.catch((error) => {
// Handle any errors
})
.finally(() => {
// Always executes regardless of success/failure
});
// Promise methods
Promise.all([promise1, promise2]); // Waits for all promises
Promise.race([promise1, promise2]); // Waits for first to complete
Promise.allSettled([promise1, promise2]); // Waits for all to settle
Promise.any([promise1, promise2]); // Waits for first to fulfill
// Async/await
async function getData() {
try {
// await pauses execution until promise resolves
const result = await fetchData();
const processed = await processData(result);
return processed;
} catch (error) {
// Handle errors
console.error(error);
}
}JavaScript is single-threaded - it can't do two things at once. But web apps need to do things that take time (fetching data, reading files). Asynchronous code lets JavaScript keep running while waiting for something.
Analogy: Ordering coffee. You don't stand there watching it brew. You get a receipt (promise) and sit down. When it's ready, someone calls you.
- Callbacks
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
fetchData((data) => console.log(data));- Promises
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise resolved!"), 1000);
});
promise.then((result) => console.log(result));- Async/Await
async function fetchData() {
return "Data fetched";
}
fetchData().then(console.log);// Exporting
// file: math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
export default class Calculator {
// Class implementation
}
// Importing
// file: app.js
import Calculator, { add, PI as MathPI } from './math.js';
import * as math from './math.js';
------------------------------------------------
// file1.js
export const greet = name => `Hello, ${name}!`;
// file2.js
import { greet } from './file1.js';
console.log(greet("Alice"));// Try-catch
try {
// Code that might throw an error
throw new Error("Something went wrong");
} catch (error) {
// Handle the error
console.error(error.message);
} finally {
// Always executes
console.log("Cleanup code");
}
// Custom errors
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// Throwing errors
throw new Error("Error message");
throw new ValidationError("Invalid input");- The Document Object Model (DOM)
let element = document.getElementById("myElement");
let elements = document.querySelectorAll(".myClass");
element.textContent = "Updated text";
element.style.color = "red";
element.addEventListener("click", () => alert("Clicked!"));- Fetch API (AJAX)
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.log(error));-
setTimeout(): Executes code after a delay.setTimeout(() => console.log("Hello after 1 second"), 1000);
-
setInterval(): Executes code repeatedly at intervals.let count = 0; let interval = setInterval(() => { count++; console.log(count); if (count === 5) clearInterval(interval); }, 1000);
-
clearTimeout()andclearInterval(): Cancels a timeout or interval.
-
document.getElementById(): Fetches a single element by itsid.const element = document.getElementById("myElement");
-
document.getElementsByTagName(): Fetches all elements by a specific tag name.const paragraphs = document.getElementsByTagName("p");
-
document.getElementsByClassName(): Fetches all elements by a specific class name.const items = document.getElementsByClassName("item");
-
document.querySelector(): Fetches the first matching element for a CSS selector.const firstItem = document.querySelector(".item");
-
document.querySelectorAll(): Fetches all matching elements for a CSS selector.const allItems = document.querySelectorAll(".item");
-
parentNode: Access the parent node of an element.console.log(element.parentNode);
-
childNodes: Access all child nodes (including text and comment nodes).console.log(element.childNodes);
-
firstChild: Access the first child node.console.log(element.firstChild);
-
nextSibling: Access the next sibling node.console.log(element.nextSibling);
-
innerHTML: Set or get the HTML content inside an element.element.innerHTML = "<strong>Hello World</strong>";
-
textContent: Set or get only the text content inside an element.element.textContent = "Hello World";
-
setAttribute(): Set an attribute for an element.element.setAttribute("class", "newClass");
-
getAttribute(): Get the value of an attribute.const value = element.getAttribute("id");
-
styleProperty: Modify inline styles of an element.element.style.color = "red";
-
classList: Manipulate classes on an element.Add a class:
element.classList.add("newClass");Remove a class:
element.classList.remove("oldClass");Toggle a class:
element.classList.toggle("active");-
createElement(): Create a new HTML element.const newElement = document.createElement("div"); newElement.textContent = "Hello!";
-
appendChild(): Add a child element to a parent.document.body.appendChild(newElement);
-
insertBefore(): Insert a child element before another child element.parentElement.insertBefore(newElement, referenceElement);
-
removeChild(): Remove a child element from its parent.parentElement.removeChild(childElement);
Event handling allows developers to create interactive and dynamic web pages by responding to user actions like clicks, key presses, or form submissions.
-
1. Event Handling in JavaScript
-
addEventListener(): Attaches an event listener to an element for a specific event.const button = document.querySelector("#myButton"); button.addEventListener("click", () => { console.log("Button clicked!"); });
-
event.target: Refers to the element that triggered the event.button.addEventListener("click", (event) => { console.log(event.target); // Logs the clicked button element });
-
Event Bubbling: Events propagate upward from the target element to its parent(s).
document.body.addEventListener("click", () => { console.log("Body clicked"); }); button.addEventListener("click", () => { console.log("Button clicked"); }); // Clicking the button triggers both listeners (button -> body)
-
Event Capturing: Events propagate downward from the root to the target element.
document.body.addEventListener( "click", () => { console.log("Capturing: Body clicked"); }, true // Enables capturing );
-
-
2. Scroll Events, Mouse Events, Key Events, and Strict Mode
-
Scroll Events: Triggered when the user scrolls a page or an element.
window.addEventListener("scroll", () => { console.log("Page scrolled"); });
-
Mouse Events:
- Common mouse events:
click,dblclick,mousedown,mouseup,mousemove.
document.addEventListener("mousemove", (event) => { console.log(`Mouse at (${event.clientX}, ${event.clientY})`); });
- Common mouse events:
-
Key Events:
- Common key events:
keydown,keypress,keyup.
document.addEventListener("keydown", (event) => { console.log(`Key pressed: ${event.key}`); });
- Common key events:
-
Strict Mode: Enforces stricter parsing and error handling in JavaScript.
"use strict"; x = 10; // Throws an error (variable not declared)
-
-
3. Working with Forms and Input Elements
-
Accessing Form Data:
const form = document.querySelector("#myForm"); form.addEventListener("submit", (event) => { event.preventDefault(); const inputData = document.querySelector("#myInput").value; console.log(inputData); });
-
Validating Forms: Use JavaScript to check user input.
form.addEventListener("submit", (event) => { const input = document.querySelector("#myInput"); if (!input.value) { event.preventDefault(); console.log("Input cannot be empty"); } });
-
preventDefault(): Prevents the default browser action (e.g., form submission or link navigation).form.addEventListener("submit", (event) => { event.preventDefault(); // Prevents page reload });
-
onsubmitandonchange:onsubmit: Triggered when a form is submitted.onchange: Triggered when an input value changes.
form.onsubmit = (event) => { event.preventDefault(); console.log("Form submitted"); };
-
-
4. Working with Classes
-
Modify CSS classes dynamically using
classList:-
Add a class:
element.classList.add("newClass");
-
Remove a class:
element.classList.remove("oldClass");
-
Toggle a class:
element.classList.toggle("active");
-
Check if a class exists:
console.log(element.classList.contains("active")); // true or false
-
-
-
5. Browser Events
-
DOMContentLoaded: Triggered when the HTML is fully loaded and parsed (before stylesheets and images).document.addEventListener("DOMContentLoaded", () => { console.log("DOM fully loaded"); });
-
load: Triggered when the entire page (including assets like images) is loaded.window.addEventListener("load", () => { console.log("Page fully loaded"); });
-
resize: Triggered when the browser window is resized.window.addEventListener("resize", () => { console.log("Window resized"); });
-
scroll: Triggered when the user scrolls the page.window.addEventListener("scroll", () => { console.log("User scrolled the page"); });
-
JavaScript allows interaction with the browser's various functionalities through the Browser Object Model (BOM), which provides objects for working with the browser window, navigation history, location, and more.
- 1. Browser Object Model
The BOM consists of objects that represent the browser environment:
window: Represents the browser window and is the global object.navigator: Provides information about the browser and the device.history: Allows navigation through the browser's history.location: Represents the URL of the current page and enables URL manipulation.document: Represents the HTML document loaded in the browser.
-
2. Window Object The
windowobject is the global object in the browser. Its properties and methods provide access to BOM functionalities.-
Provides information about the current URL and methods to manipulate it.
console.log(window.location.href); // Full URL console.log(window.location.hostname); // Hostname console.log(window.location.pathname); // Pathname console.log(window.location.search); // Query string // Redirect to another URL window.location.href = "https://example.com"; // Reload the page window.location.reload();
-
Allows navigation through the browser's history.
// Go back to the previous page window.history.back(); // Go forward to the next page window.history.forward(); // Move a specific number of steps in the history window.history.go(-2); // Go back two pages
-
-
3. Working with Storage
-
Stores data with no expiration (persists across sessions).
// Save data localStorage.setItem("key", "value"); // Retrieve data const value = localStorage.getItem("key"); console.log(value); // Remove data localStorage.removeItem("key"); // Clear all data localStorage.clear();
-
Stores data for the duration of the page session (cleared when the browser is closed).
// Save data sessionStorage.setItem("key", "value"); // Retrieve data const value = sessionStorage.getItem("key"); console.log(value); // Remove data sessionStorage.removeItem("key"); // Clear all data sessionStorage.clear();
-
Store small pieces of data with optional expiration.
// Set a cookie document.cookie = "username=John; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/"; // Get cookies console.log(document.cookie); // Delete a cookie document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
-
- 4. Web APIs in DOM
- Used to make network requests.
fetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error));
- Retrieves the user's location (requires user permission).
if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { console.log("Latitude:", position.coords.latitude); console.log("Longitude:", position.coords.longitude); }, (error) => { console.error("Error getting location:", error.message); } ); } else { console.log("Geolocation is not supported by this browser."); }
- Used to make network requests.
Object-Oriented Programming (OOP) in JavaScript is based on the use of objects, which are collections of properties and methods. Modern JavaScript also supports classes, making it easier to create and manage objects.
OOP principles include:
- Encapsulation: Bundling data (properties) and methods (functions) into objects.
- Inheritance: Sharing functionality between objects using prototypes or classes.
- Polymorphism: Overriding or redefining methods in derived classes.
- Abstraction: Hiding complexity by exposing only essential features.
-
A template for creating objects with shared properties and methods.
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 john = new Person("John", 25); john.greet(); // Hi, I'm John, and I'm 25 years old.
-
Instances of classes or standalone structures.
const car = { brand: "Toyota", model: "Corolla", start() { console.log("Car started"); }, }; car.start(); // Car started
-
A special method in a class for initializing objects.
class Animal { constructor(type, sound) { this.type = type; this.sound = sound; } makeSound() { console.log(`${this.type} says ${this.sound}`); } } const dog = new Animal("Dog", "Woof"); dog.makeSound(); // Dog says Woof
-
A mechanism by which objects inherit methods and properties.
function Animal(type, sound) { this.type = type; this.sound = sound; } Animal.prototype.makeSound = function () { console.log(`${this.type} says ${this.sound}`); }; const cat = new Animal("Cat", "Meow"); cat.makeSound(); // Cat says Meow
-
Refers to the object that is executing the current function.
class Car { constructor(brand) { this.brand = brand; } showBrand() { console.log(this.brand); } } const car = new Car("Tesla"); car.showBrand(); // Tesla
-
Invokes a function with a specified
thiscontext and arguments.const obj = { name: "Alice" }; function greet(greeting) { console.log(`${greeting}, ${this.name}`); } greet.call(obj, "Hello"); // Hello, Alice
- Similar to
call, but arguments are passed as an array.greet.apply(obj, ["Hi"]); // Hi, Alice
- Creates a new function with a specified
thiscontext.const boundGreet = greet.bind(obj, "Hey"); boundGreet(); // Hey, Alice
-
Classes can also be created as expressions.
const Vehicle = class { constructor(type) { this.type = type; } displayType() { console.log(`This is a ${this.type}`); } }; const bike = new Vehicle("Bike"); bike.displayType(); // This is a Bike
- Class declarations are not hoisted.
console.log(Person); // ReferenceError class Person {}
-
Extending a class to create a child class.
class Animal { constructor(name) { this.name = name; } eat() { console.log(`${this.name} is eating.`); } } class Dog extends Animal { bark() { console.log("Woof!"); } } const dog = new Dog("Buddy"); dog.eat(); // Buddy is eating. dog.bark(); // Woof!
-
Allow controlled access to object properties.
class Person { constructor(name) { this._name = name; // Private property convention } get name() { return this._name; } set name(newName) { if (newName.length > 0) { this._name = newName; } else { console.log("Invalid name"); } } } const person = new Person("Alice"); console.log(person.name); // Alice person.name = "Bob"; console.log(person.name); // Bob
Asynchronous programming allows JavaScript to handle tasks like fetching data from a server, timers, and user input without blocking the main thread. This enables efficient execution of code while waiting for external resources or events.
JavaScript uses a single-threaded runtime, meaning it can execute one task at a time. Asynchronous programming allows handling tasks like file reading, API calls, or DOM events without waiting for them to finish.
Key mechanisms include:
- Callbacks
- Promises
async/await
The event loop ensures that the JavaScript engine executes code, handles events, and processes asynchronous tasks in a non-blocking manner.
- Call Stack: Executes synchronous tasks.
- Web APIs: External environment for async tasks like
setTimeout. - Task Queue: Holds callbacks for timed or other async tasks (e.g.,
setTimeout). - Microtask Queue: Holds microtasks (e.g.,
Promisecallbacks), processed before tasks in the Task Queue.
- Synchronous code is executed first.
- Async tasks are sent to the respective queues after completion.
- Microtasks (from
Promises) are processed before tasks in the Task Queue.
Callbacks are functions passed as arguments to other functions to be executed later.
Example:
function fetchData(callback) {
setTimeout(() => {
console.log("Data fetched");
callback();
}, 1000);
}
fetchData(() => {
console.log("Callback executed");
});- Callback Hell: Nested callbacks make code difficult to read and maintain.
setTimeout(() => { console.log("Step 1"); setTimeout(() => { console.log("Step 2"); setTimeout(() => { console.log("Step 3"); }, 1000); }, 1000); }, 1000);
A Promise represents a value that will be resolved in the future.
- Pending: Initial state, neither fulfilled nor rejected.
- Resolved: Operation completed successfully.
- Rejected: Operation failed.
Example:
const promise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Promise resolved");
} else {
reject("Promise rejected");
}
});
promise
.then((result) => console.log(result))
.catch((error) => console.error(error));promise
.then((result) => {
console.log(result);
return "Next step";
})
.then((nextResult) => console.log(nextResult))
.catch((error) => console.error(error));Promise.race(): Resolves/rejects with the first settled promise.Promise.race([promise1, promise2]).then(console.log).catch(console.error);
Promise.all(): Resolves only if all promises are resolved; rejects if one fails.Promise.all([promise1, promise2]).then(console.log).catch(console.error);
Promise.any(): Resolves with the first fulfilled promise; rejects if all fail.Promise.any([promise1, promise2]).then(console.log).catch(console.error);
Promise.allSettled(): Resolves with the status of all promises, regardless of success/failure.Promise.allSettled([promise1, promise2]).then(console.log);
The async keyword makes a function return a Promise, and await pauses execution until the Promise resolves.
Example:
async function fetchData() {
try {
const result = await new Promise((resolve, reject) => {
setTimeout(() => resolve("Data fetched"), 1000);
});
console.log(result);
} catch (error) {
console.error(error);
}
}
fetchData();-
setTimeout: Executes code after a delay.setTimeout(() => console.log("Executed after 2 seconds"), 2000);
-
setInterval: Repeats execution at specified intervals.const interval = setInterval( () => console.log("Repeating every 1 second"), 1000 ); // Clear interval setTimeout(() => clearInterval(interval), 5000);
Web APIs provide additional functionality beyond JavaScript’s core language.
- Fetches data from a server.
fetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error));
- Retrieves the user's location.
if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { console.log("Latitude:", position.coords.latitude); console.log("Longitude:", position.coords.longitude); }, (error) => console.error(error.message) ); } else { console.log("Geolocation not supported."); }
Error handling ensures that code can gracefully handle unexpected issues, preventing crashes and improving user experience.
Errors are inevitable in programming, but with error handling, you can:
- Identify and resolve bugs.
- Ensure that the application continues running smoothly.
- Provide meaningful feedback to users.
-
Syntax Errors:
-
Occur when code violates JavaScript syntax rules.
-
Example:
console.log("Hello World // Missing closing quote
-
-
Runtime Errors:
- Occur during code execution due to invalid operations.
- Example:
let result = nonExistentFunction();
-
Logical Errors:
- Errors in logic that lead to incorrect behavior.
- Example:
const add = (a, b) => a - b; // Logical error in operation console.log(add(2, 3)); // Outputs -1 instead of 5
JavaScript provides the Error object to represent errors.
message: A string describing the error.name: Type of error (e.g.,ReferenceError,TypeError).stack: A stack trace for debugging.
Example:
try {
let result = undefinedVariable;
} catch (error) {
console.log("Error Name:", error.name); // ReferenceError
console.log("Error Message:", error.message); // undefinedVariable is not defined
console.log("Stack Trace:", error.stack);
}-
try-catch:- Use
tryto write code that might throw an error andcatchto handle the error.
try { let result = 10 / 0; console.log(result); } catch (error) { console.error("An error occurred:", error.message); }
- Use
-
try-catch-finally:- The
finallyblock always executes, regardless of an error.
try { let result = JSON.parse("Invalid JSON"); } catch (error) { console.error("Caught an error:", error.message); } finally { console.log("Cleanup actions, if any."); }
- The
Use the throw statement to create custom errors.
Example:
function divide(a, b) {
if (b === 0) {
throw new Error("Division by zero is not allowed.");
}
return a / b;
}
try {
console.log(divide(10, 0));
} catch (error) {
console.error(error.message); // Division by zero is not allowed.
}Extend the Error object to create custom error types.
Example:
class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
}
}
try {
throw new CustomError("This is a custom error.");
} catch (error) {
console.error(error.name); // CustomError
console.error(error.message); // This is a custom error.
}-
Using
try-catchwithasync/await:async function fetchData() { try { let response = await fetch("invalid-url"); let data = await response.json(); console.log(data); } catch (error) { console.error("Error during fetch:", error.message); } } fetchData();
-
Handling Errors in Promises:
fetch("invalid-url") .then((response) => response.json()) .catch((error) => { console.error("Error in promise:", error.message); });
- Use meaningful error messages.
- Handle specific errors where possible.
- Avoid suppressing errors silently.
- Log errors for debugging and tracking.
- Gracefully recover from errors to improve user experience.
Throttling and Debouncing are techniques to control the rate of execution of a function, often used to optimize performance when handling events like scrolling, resizing, or typing.
Throttling ensures that a function executes at most once in a specified period, regardless of how many times the event is triggered.
-
Use Cases:
- Handling window resize events.
- Limiting API requests during user input.
-
Implementation Example:
function throttle(func, limit) { let lastFunc; let lastRan; return function () { const context = this; const args = arguments; if (!lastRan) { func.apply(context, args); lastRan = Date.now(); } else { clearTimeout(lastFunc); lastFunc = setTimeout(function () { if (Date.now() - lastRan >= limit) { func.apply(context, args); lastRan = Date.now(); } }, limit - (Date.now() - lastRan)); } }; } const throttledFunc = throttle(() => console.log("Throttled!"), 1000); window.addEventListener("resize", throttledFunc);
Debouncing ensures that a function is executed only after a specified delay has passed since the last time it was invoked. If the event is triggered again within the delay, the timer resets.
-
Use Cases:
- Auto-saving form data after typing stops.
- Search bar suggestions after the user stops typing.
-
Implementation Example:
function debounce(func, delay) { let timeout; return function () { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), delay); }; } const debouncedFunc = debounce(() => console.log("Debounced!"), 1000); document.getElementById("input").addEventListener("input", debouncedFunc);
| Feature | Throttling | Debouncing |
|---|---|---|
| Execution | Executes at regular intervals. | Executes after a delay. |
| Use Case | Scroll events, resize events. | Typing in a search bar. |
| Frequency | Limits execution rate. | Delays execution until inactivity. |
JSON (JavaScript Object Notation) is a lightweight data-interchange format used for data exchange between systems.
Converts a JSON string into a JavaScript object.
- Example:
const jsonString = '{"name": "John", "age": 30}'; const jsonObject = JSON.parse(jsonString); console.log(jsonObject.name); // Output: John console.log(jsonObject.age); // Output: 30
- Use Cases:
- Reading data from APIs or files.
Converts a JavaScript object into a JSON string.
- Example:
const jsonObject = { name: "John", age: 30 }; const jsonString = JSON.stringify(jsonObject); console.log(jsonString); // Output: {"name":"John","age":30}
- Use Cases:
- Sending data to APIs.
- Storing data in localStorage or sessionStorage.
-
Convert an object to JSON and back:
const originalObject = { name: "John", age: 30 }; const jsonString = JSON.stringify(originalObject); const parsedObject = JSON.parse(jsonString); console.log(parsedObject); // Output: { name: 'John', age: 30 }