0% found this document useful (0 votes)
13 views64 pages

Internet Vs World Wide Web (WWW)

it will be help for student studing web development as a university course

Uploaded by

saadali.data1
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views64 pages

Internet Vs World Wide Web (WWW)

it will be help for student studing web development as a university course

Uploaded by

saadali.data1
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 64

1.

Internet vs World Wide Web (WWW)


●​ Internet:​

○​ A global network of interconnected computers and devices.​

○​ Infrastructure allowing data exchange via protocols like TCP/IP.​

○​ Includes hardware (routers, cables, servers) and software protocols.​

●​ World Wide Web (WWW):​

○​ A service running on the internet.​

○​ Uses HTTP protocol to access webpages.​

○​ Collection of interlinked hypertext documents accessed via browsers.​

●​ Key difference:​

○​ Internet = physical & logical network.​

○​ WWW = system of webpages/documents on the internet.

2. HTTP Protocol Basics


●​ HTTP (HyperText Transfer Protocol):​

○​ Application layer protocol used by WWW.​

○​ Defines rules for client-server communication.​

●​ Request-Response model:​

○​ Client (browser) sends HTTP request to server.​

○​ Server processes and sends HTTP response back.​

●​ Common HTTP methods:​

○​ GET: Retrieve data/resource.​

○​ POST: Submit data to server.​

○​ PUT: Update resource.​

○​ DELETE: Remove resource.​

●​ Stateless protocol:​
○​ Each request is independent; server does not store client state between
requests.​

●​ HTTP Headers:​

○​ Metadata about the request/response (content-type, authorization, cookies).

3. Client-Server Architecture
●​ Client:​

○​ The device or software requesting services (e.g., browser).​

●​ Server:​

○​ Provides services or resources to clients (e.g., web server, database server).​

●​ How it works:​

○​ Client sends request → Server processes → Server sends response.​

●​ Advantages:​

○​ Centralized resources.​

○​ Easy to maintain and secure.​

●​ Disadvantages:​

○​ Server overload risk.​

○​ Single point of failure if server crashes.

4. DNS (Domain Name System)


●​ Purpose:​

○​ Translates human-readable domain names (e.g., www.example.com) to IP


addresses.​

●​ Why needed:​

○​ Humans remember names better than IPs.​

○​ Machines communicate via IP addresses.​

●​ Working:​

○​ Client queries DNS server for IP of domain name.​

○​ DNS server responds with the IP address.​

○​ Browser uses IP to connect to server.


5. IP Addresses
●​ Definition:​

○​ Unique numerical identifier for devices on a network.​

●​ Types:​

○​ IPv4: 32-bit, e.g., 192.168.1.1​

○​ IPv6: 128-bit, for larger address space.​

●​ Static vs Dynamic IP:​

○​ Static: fixed address assigned.​

○​ Dynamic: assigned temporarily by DHCP.

6. Ports
●​ Definition:​

○​ Numerical identifiers for specific services/processes on a device.​

○​ Range: 0–65535.​

●​ Common ports:​

○​ HTTP: 80​

○​ HTTPS: 443​

○​ FTP: 21​

○​ SSH: 22​

●​ Why needed:​

○​ Allows multiple network services on one IP address.

7. HTML Basics
●​ HTML (HyperText Markup Language):​

○​ Standard language for creating webpages.​

○​ Uses tags to structure content.​

Basic Structure of HTML document:​


<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<!-- Content goes here -->
</body>
</html>

8. Semantic HTML
●​ Purpose:​

○​ Use meaningful tags that describe content purpose and structure.​

○​ Improves accessibility, SEO, and code readability.​

●​ Common semantic tags:​

○​ <header>: Introductory content or navigational links.​

○​ <nav>: Navigation links.​

○​ <main>: Main content of the document.​

○​ <article>: Independent, self-contained content (e.g., blog post).​

○​ <section>: Thematic grouping of content.​

○​ <footer>: Footer of the document or section.​

○​ <aside>: Content indirectly related to main content (e.g., sidebar).​

●​ Benefits:​

○​ Search engines better understand page structure.​

○​ Assistive technologies like screen readers perform better.​

JavaScript Runtime & Environment (V8 Engine + Event


Loop Basics)
1. What is JavaScript Runtime?
●​ JavaScript runtime is the environment where your JavaScript code runs.​

●​ It provides everything needed for JavaScript code execution outside of the language
itself, such as:​
○​ Memory allocation​

○​ Execution stack (call stack)​

○​ APIs like timers (setTimeout), DOM manipulation, fetch requests, etc.​

●​ This runtime differs depending on the platform:​

○​ Browsers have their runtime environment (with browser-specific APIs).​

○​ Node.js provides a runtime environment on servers.

2. What is V8 Engine?
●​ V8 is Google's open-source JavaScript engine.​

●​ It runs your JavaScript code inside Google Chrome and Node.js.​

●​ Written in C++, it compiles JavaScript directly to machine code for fast execution.​

How V8 works at a high level:


1.​ Parsing: Takes your JavaScript code and parses it into an Abstract Syntax Tree
(AST).​

2.​ Compilation: Converts AST into optimized machine code using Just-In-Time (JIT)
compilation.​

3.​ Execution: Runs the compiled machine code.

Components of JavaScript Runtime (in V8)


This section will help you understand how JavaScript runs under the hood, specifically in
environments like Chrome (browser) and Node.js, which both use the V8 engine.

We'll break this down into 4 main components:

1.​ Call Stack​

2.​ Heap​

3.​ Web APIs​

4.​ Callback Queue + Event Loop​

Each component works together to execute JavaScript code, especially asynchronous code
(like setTimeout, fetch, etc.).

✅ 1. Call Stack
The Call Stack is like a to-do list for your JavaScript code.

💡 What is it?
●​ A stack is a data structure that works on the Last In, First Out (LIFO) principle.​

●​ It keeps track of function calls – when a function is called, it is pushed onto the
stack.​

●​ When the function finishes, it is popped off the stack.​

📌 Key Points
●​ Only one thing runs at a time in JavaScript (single-threaded).​

●​ Only synchronous code is placed directly in the call stack.​

●​ If a function calls another function, it nests in the stack.

🔍 Example:
function greet() {
console.log("Hello");
}

function welcome() {
greet();
console.log("Welcome");
}

welcome();
🧠 Step-by-step:
1.​ welcome() is pushed onto the call stack.​

2.​ Inside welcome(), greet() is called → pushed onto the stack.​

3.​ "Hello" is logged → greet() is finished → popped off the stack.​

4.​ "Welcome" is logged → welcome() finishes → popped off the stack.​

So, the stack looked like this:

-> greet()
-> welcome()

✅ 2. Heap
The Heap is where objects and variables are stored in memory.

💡 What is it?
●​ A heap is an unstructured memory space used for storing complex data (like
arrays, objects).​

●​ Unlike the call stack, which is structured, the heap allows data to be stored and
retrieved freely.​

🔍 Example:
const person = {
name: "Ali",
age: 21
};

●​ The variable person is stored in the heap.​

●​ In the call stack, only a reference (pointer) to the memory address of this object is
kept.​

This allows large objects to be accessed without copying them repeatedly

✅ 3. Web APIs (provided by the browser)


Web APIs are features provided by the browser, not by JavaScript itself.

Web APIs are extra features given by the browser that allow JavaScript to interact
with the web page, the user, and the internet.
💡 What is it?
●​ These are APIs provided by the environment (browser or Node.js) to run tasks
asynchronously.​

●​ Examples include:​

○​ setTimeout()​

○​ DOM Events (like click)​

○​ AJAX/fetch​

○​ console.log (actually a wrapper by the environment)​

JavaScript itself doesn't know how to handle time or DOM. The browser helps.

🔍 Example:
console.log("Start");

setTimeout(() => {
console.log("Timeout finished");
}, 1000);

console.log("End");

🧠 What happens here:


1.​ "Start" is logged (call stack).​

2.​ setTimeout() is called → the browser takes it to Web APIs and starts the
1-second timer.​

3.​ "End" is logged (call stack).​

4.​ After 1 second, the callback () => console.log("Timeout finished") is


sent to the callback queue.​

5.​ Once the call stack is empty, the event loop pushes the callback into the call stack.

✅ 4. Callback Queue + Event Loop


A. Callback Queue (a.k.a Task Queue)

●​ This queue holds callbacks from Web APIs (like setTimeout, fetch) waiting to
be executed.​
●​ FIFO (First In First Out) – the first task added is the first one executed.​

B. Microtask Queue
●​ Special queue for Promises, MutationObserver, etc.​

●​ Has higher priority than callback/macrotask queue.​

C. Event Loop
●​ Constantly checks:​

○​ Is the call stack empty?​

○​ If yes, → pushes a callback from the microtask/callback queue into the call
stack.​

🔍 Example: Microtask vs Macrotask


console.log("Start");

setTimeout(() => {
console.log("setTimeout");
}, 0);

Promise.resolve().then(() => {
console.log("Promise");
});

console.log("End");

🔁 Execution Order:
1.​ "Start" → printed.​

2.​ setTimeout() goes to Web API → callback waits in macrotask queue.​

3.​ Promise.resolve().then() goes to microtask queue.​

4.​ "End" → printed.​

5.​ Microtask queue runs → "Promise" is printed.​

6.​ Then the macrotask queue runs → "setTimeout" is printed.​

✅ Output:
Start
End
Promise
setTimeout

✅ What is a Web API?


A Web API is a set of features or tools that your browser provides in addition to
JavaScript to help you build interactive websites and apps.

📌 JavaScript alone is very basic — it can do logic (if/else, loops, math), but it
cannot interact with the real world (like showing alerts, setting timers, or
changing the web page).

That’s where Web APIs come in — they extend JavaScript's powers.

✅ Why are Web APIs Needed?


JavaScript on its own does not know how to:

●​ Show a popup​

●​ Wait for time​

●​ Handle mouse clicks​

●​ Change text on a page​

●​ Send a request to a server​

●​ Play audio or video​

These tasks need help from the browser, and that's exactly what Web APIs provide.

✅ Where do Web APIs come from?


Web APIs are built into the browser, not into the JavaScript language.

They are created by browser makers (like Chrome, Firefox, Safari) and follow standards
from organizations like W3C.

Your code accesses them through JavaScript, but they are external tools.

✅ Common Types of Web API Features (with


Examples)
Web API Description (In Simple Example Code
Type Words)
✅ DOM API Lets JavaScript interact
with HTML elements
document.getElementById("box")

✅ Timer API Runs code after a delay setTimeout(() => {...}, 1000)
or repeatedly

✅ Fetch API Sends network


requests (e.g., to get
fetch("https://api.example.com")

data from a server)

✅ Event API Detects and responds


to user actions (like
button.addEventListener("click", …)

clicks)

✅ Storage
API
Saves data in browser
(locally)
localStorage.setItem("key", "123")


Audio/Video
Controls media
elements on a web
video.play()

API page

✅ Canvas
API
Draws graphics or
games in browser
ctx.fillRect(0, 0, 100, 100)


Geolocation
Finds your location
(with permission)
navigator.geolocation.getCurrentPos
ition()
API


Notification
Shows desktop
notifications from the
new Notification("Hi there!")

API browser

✅ Example: DOM API


html
Copy
<button onclick="changeText()">Click me</button>
<p id="text">Hello</p>

<script>
function changeText() {
document.getElementById("text").innerText = "You clicked the
button!";
}
</script>

✅ document.getElementById() is a DOM Web API that JavaScript uses to find and


change HTML elements.
✅ Example: Timer API (setTimeout)
javascript
Copy
console.log("Start");

setTimeout(() => {
console.log("Hello after 2 seconds");
}, 2000);

console.log("End");

✅ setTimeout() is a Timer Web API — JavaScript sends it to the browser, the browser
waits, then sends the callback back to JavaScript when time is up.

What is an Arrow Function in JavaScript?


An arrow function is a shorter and cleaner way to write a function in JavaScript.

It was introduced in ES6 (2015) and is often used in modern JavaScript, especially with
callbacks, array methods, and React components.

An arrow function is a modern, short-hand syntax for writing functions in JavaScript.​


It does not bind its own this and is perfect for writing short, simple, and context-inheriting
functions.

✅ Basic Syntax
🔹 Traditional Function:
function greet() {
return "Hello";
}

🔹 Arrow Function (equivalent):


const greet = () => {
return "Hello";
};

🧠 It behaves the same but uses arrow syntax: () => {}


✅ Key Syntax Rules
1. No Parameters:
const sayHi = () => {
console.log("Hi");
};

2. One Parameter (no parentheses needed):


const greet = name => {
console.log("Hello, " + name);
};

3. Multiple Parameters (use parentheses):


const add = (a, b) => {
return a + b;
};

4. Single expression (no {} or return needed):


const square = x => x * x;

👆 This is called an implicit return. The result is returned automatically without needing
return.

✅ Example in setTimeout:
setTimeout(() => {
console.log("Hello after 2 seconds");
}, 2000);

●​ () => { ... } is an arrow function.​

●​ It is passed as a callback to setTimeout.​

●​ After 2 seconds, the function runs and prints "Hello after 2 seconds".

✅ How Is It Different from a Regular Function?


Feature Arrow Function Regular Function

Syntax Short, cleaner Longer

this keyword Does not bind its own Has its own this
this

Can be used as a
constructor
❌ No ✅ Yes
arguments object ❌ Not available ✅ Available
For simple callbacks ✅ Preferred 👌 Still works
✅ Important: this Binding
Arrow functions do not create their own this. Instead, they inherit this from the
surrounding code (lexical scoping).

Example:
const user = {
name: "Ali",
greet: function () {
setTimeout(() => {
console.log("Hi, " + this.name);
}, 1000);
}
};

user.greet();

●​ Inside setTimeout, we use an arrow function.​

●​ this.name refers to "Ali" because the arrow function uses this from greet(),
which belongs to user.​

If we used a regular function instead:

setTimeout(function() {
console.log("Hi, " + this.name); // ❌ undefined
}, 1000);

●​ Here, this would refer to the global object, not user.​

✅ This is why arrow functions are preferred in callbacks where you want to preserve the
parent context.

✅ When to Use Arrow Functions


✅ Use arrow functions for:
●​ Callbacks (e.g., setTimeout, .map(), .filter())​

●​ Short, simple functions​


●​ Functions where you don’t want to bind your own this​

❌ Avoid arrow functions when:


●​ You need your own this (e.g., inside class methods or event handlers in some
cases)​

●​ You need the arguments object​

●​ You want to use it as a constructor

The code snippet:

setTimeout(() => {
console.log("Hi, " + this.name);
}, 1000);

is correct, but whether it works as you expect depends on where it is used.

Why?

●​ Arrow functions do not have their own this. Instead, they use the this value from
the surrounding (lexical) scope.​

●​ So, inside this arrow function, this.name refers to the this of the outer context
where this setTimeout is called.

Example 1: Correct use inside an object method


const user = {
name: "Ali",
greet() {
setTimeout(() => {
console.log("Hi, " + this.name); // 'this' here refers to
'user'
}, 1000);
}
};

user.greet(); // After 1 second: Hi, Ali

Explanation:

●​ this inside the arrow function refers to the user object because it inherits this
from greet()'s context.​

●​ So this.name is "Ali".
Example 2: Not working as expected in global context
setTimeout(() => {
console.log("Hi, " + this.name); // 'this' here refers to global
object (window in browsers)
}, 1000);

var name = "Global";

Output:

Hi, Global

Explanation:

●​ Here, this is the global object (window in browsers).​

●​ If name is declared as a global variable (var name = "Global"), it becomes


window.name.​

●​ So it logs "Hi, Global".​

If you run this in strict mode, this will be undefined and accessing this.name will print
undefined.

Example 3: Using regular function — different this


const user = {
name: "Ali",
greet() {
setTimeout(function() {
console.log("Hi, " + this.name); // 'this' here is NOT 'user'
}, 1000);
}
};

user.greet(); // After 1 second: Hi, undefined

Explanation:

●​ Regular functions get their own this depending on how they are called.​

●​ Inside setTimeout, this refers to the global object (or undefined in strict mode),
not the user object.​

●​ So this.name is undefined.​
What is the Event Loop in JavaScript?​
Event Loop: An Event Loop in JavaScript is said to be a constantly running process that
keeps a tab on the call stack. Its main function is to check whether the call stack is empty or
not. If the call stack turns out to be empty, the event loop proceeds to execute all the
callbacks waiting in the task queue. Inside the task queue, the tasks are broadly classified
into two categories, namely micro-tasks and macro-tasks.

1. Background: Why Do We Need an Event Loop?


●​ JavaScript is single-threaded, meaning it can do only one thing at a time (one call
stack).​

●​ But modern web apps need to handle asynchronous tasks: timers, user input,
network requests, etc.​

●​ To handle async without blocking the main thread, JavaScript uses the Event Loop
to manage when to run code.

2. What is the Event Loop?


●​ The Event Loop is a process that keeps checking:​

○​ If the call stack is empty,​

○​ And if there are any tasks waiting in the task queues (macrotasks or
microtasks).​

●​ If the call stack is empty and there are waiting tasks, the event loop moves the next
task from the queue to the call stack for execution.​

●​ This process repeats forever while the page/app is alive.

3. How Does It Work? The Big Picture


Components involved:
●​ Call Stack: Runs currently executing functions synchronously.​

●​ Web APIs: Browser or environment features like timers, network requests, DOM
events.​

●​ Task Queues:​

○​ Macrotask queue: Timers, I/O, UI events.​

○​ Microtask queue: Promises, queueMicrotask, MutationObserver.​

●​ Event Loop: Coordinates when tasks move from queues to the call stack.

4. Step-by-Step Flow
1.​ JavaScript starts running synchronous code (everything goes to call stack).​

2.​ When an asynchronous task is encountered (like setTimeout or a Promise), it's


handed off to Web APIs to wait for completion.​

3.​ Once the async task finishes, its callback function is placed into the appropriate task
queue:​

○​ Promises → microtask queue.​

○​ Timers, events → macrotask queue.​

4.​ The call stack finishes running all synchronous code and becomes empty.​

5.​ The event loop checks the microtask queue first:​

○​ Runs all microtasks in order until the microtask queue is empty.​

6.​ Then the event loop picks one macrotask from the macrotask queue and pushes it
to the call stack.​

7.​ The process repeats: check microtasks → run macrotask → etc.

5. Why Microtasks Before Macrotasks?


●​ Microtasks are considered higher priority to ensure promise resolutions and other
critical async work happen as soon as possible.​

●​ This helps keep the app responsive and consistent.

6. Example
console.log("Script start");

setTimeout(() => {
console.log("setTimeout");
}, 0);

Promise.resolve().then(() => {
console.log("Promise");
});

console.log("Script end");

Output:
Script start
Script end
Promise
setTimeout
Explanation:

●​ "Script start" and "Script end" log immediately (synchronous).​

●​ Promise callback (microtask) runs after synchronous code, before setTimeout


(macrotask).​

●​ setTimeout callback runs last.

Conclusion:
●​ Your original code snippet is correct as an arrow function, but​

●​ It only works as expected if the surrounding this refers to the object that has the
name property.​

●​ In many cases, using arrow functions inside object methods helps you keep the
correct this.​

●​ If you want a regular function and want to keep the this of the object, you can do:​

Microtask Queue vs Macrotask Queue in JavaScript

1. What are Tasks in JavaScript?


JavaScript uses an event loop to manage tasks that need to be run asynchronously, such
as timers, network requests, or promise callbacks.

These tasks are queued and then executed one at a time on the single-threaded call
stack.

2. Two Types of Task Queues


●​ Macrotask queue (also called task queue)​

●​ Microtask queue​

They are separate queues that the event loop processes with different priorities.

3. Macrotask Queue
Macro-tasks within an event loop: Macro-task represents some discrete and independent
work. These are always the execution of the JavaScript code and the micro-task queue is
empty. Macro-task queue is often considered the same as the task queue or the event
queue. However, the macro-task queue works the same as the task queue. The only small
difference between the two is that the task queue is used for synchronous statements
whereas the macro-task queue is used for asynchronous statements.
What it contains:

●​ Timers (setTimeout, setInterval)​

●​ UI rendering tasks (browser repaints)​

●​ I/O events​

●​ setImmediate (Node.js)​

●​ requestAnimationFrame​

How it works:
●​ When the call stack is empty, the event loop picks the first macrotask in the queue
and pushes it to the call stack.​

●​ The macrotask runs to completion.​

●​ Then the event loop checks the microtask queue before running the next macrotask.

4. Microtask Queue
Micro-tasks within an event loop: A micro-task is said to be a function that is executed
after the function or program which created it exits and only if the JavaScript execution stack
is empty, but before returning control to the event loop being used by the user agent to drive
the script's execution environment.

What it contains:

●​ Promise .then() / .catch() / .finally() callbacks​

●​ queueMicrotask()​

●​ MutationObserver callbacks (DOM changes)​

How it works:
●​ Microtasks run immediately after the currently executing script finishes and
before the event loop processes the next macrotask.​

●​ Microtasks can enqueue other microtasks.​

●​ The event loop drains the entire microtask queue before moving back to the
macrotask queue.

5. Execution Order: Which Runs First?


1.​ JavaScript runs synchronous code (call stack).​
2.​ When call stack is empty, event loop runs all microtasks in the microtask queue, to
completion.​

3.​ Then, it runs one macrotask from the macrotask queue.​

4.​ Repeat from step 2.

6. Why is This Important?


Because microtasks always run before the next macrotask, promise callbacks and other
microtasks are prioritized over timers and UI events.

7. Example to Illustrate
console.log("Script start");

setTimeout(() => {

console.log("setTimeout (macrotask)");

}, 0);

Promise.resolve()

.then(() => {

console.log("Promise 1 (microtask)");

})

.then(() => {

console.log("Promise 2 (microtask)");

});

console.log("Script end");

Execution flow:

●​ "Script start" → synchronous, runs immediately​


●​ setTimeout callback scheduled as macrotask​

●​ Promise.resolve() callbacks scheduled as microtasks​

●​ "Script end" → synchronous, runs immediately

Output order:

Script start

Script end

Promise 1 (microtask)

Promise 2 (microtask)

setTimeout (macrotask)

Explanation:

●​ The synchronous code runs first: "Script start", then "Script end".​

●​ All microtasks (Promise 1 and Promise 2) run before any macrotask.​

●​ Finally, the macrotask from setTimeout runs.

8. Additional Notes
●​ Because microtasks run first, they can cause UI blocking if they keep adding more
microtasks.​

●​ Browser rendering happens after macrotasks but between microtasks and


macrotasks.​

●​ Understanding this helps write better asynchronous code and avoid UI freezes.​

Promises in JavaScript
✅ What is a Promise?
●​ A Promise is an object that represents the eventual completion (or failure) of an
asynchronous operation.​

●​ It lets you write asynchronous code that’s easier to read and manage than traditional
callbacks.

✅ Promise States
A Promise can be in one of these states:

State Meaning

Pending Initial state, operation not finished yet

Fulfilled Operation completed successfully, has


a value

Rejected Operation failed, has a reason (error)

✅ Creating a Promise
const myPromise = new Promise((resolve, reject) => {

let success = true; // example condition

if(success) {

resolve("Task completed!");

} else {

reject("Task failed!");

});

●​ The executor function (passed to new Promise) runs immediately.​

●​ It receives two functions as parameters:​

○​ resolve(value) to fulfill the promise.​

○​ reject(reason) to reject the promise.

✅ Using a Promise
myPromise

.then(result => {

console.log("Success:", result);
})

.catch(error => {

console.log("Error:", error);

});

●​ .then() runs if the promise is fulfilled.​

●​ .catch() runs if the promise is rejected.​

●​ You can chain .then() calls for sequential async operations.

✅ Example: Simulating an API call


function fakeApiCall() {

return new Promise((resolve, reject) => {

setTimeout(() => {

const success = true;

if(success) {

resolve("Data received!");

} else {

reject("Error occurred");

}, 2000);

});

fakeApiCall()

.then(data => console.log(data))

.catch(err => console.error(err));


●​ This simulates waiting 2 seconds and then returning data or error.​

●​ then handles success, catch handles failure.

Why use Promises?


●​ Avoid callback hell (deeply nested callbacks).​

●​ Clean, readable async code.​

●​ Built-in support for chaining async tasks.​

●​ Supported by async/await syntax for even cleaner code.​

What is async/await?
Async and Await in JavaScript is used to simplify handling asynchronous operations using
promises. By enabling asynchronous code to appear synchronous, they enhance code
readability and make it easier to manage complex asynchronous flows.

1. async Function

The async function allows us to write promise-based code as if it were synchronous. This
ensures that the execution thread is not blocked. Async functions always return a promise. If
a value is returned that is not a promise, JavaScript automatically wraps it in a resolved
promise.

An async function is a function that always returns a promise.

●​ If the function has a return value, it is automatically wrapped in a resolved


promise.​

●​ If the function throws an error, it is automatically wrapped in a rejected promise.​

Syntax:

async function myFunction() {

// Code here

Example:

async function greet() {

return "Hello";
}

greet().then(result => console.log(result)); // "Hello"

●​ The greet function is asynchronous because of the async keyword.​

●​ The return value "Hello" is wrapped in a resolved promise.

2. await Expression
The await keyword is used to wait for a promise to resolve. It can only be used within an
async block. Await makes the code wait until the promise returns a result, allowing for
cleaner and more manageable asynchronous code.

●​ The await keyword can only be used inside an async function.​

●​ It pauses the execution of the function until the Promise is resolved or rejected,
and returns the resolved value.​

●​ If the Promise is rejected, it throws an error that you can catch with a try/catch
block.​

Syntax:

const result = await promise;

●​ The await expression unwraps the value from the promise when it resolves.​

●​ It does not block the entire thread but only pauses the execution of the async
function, allowing other tasks to run.​

Example:

async function greet() {

let greeting = await Promise.resolve("Hello");

console.log(greeting);

greet(); // "Hello"
●​ The await here waits for the Promise.resolve("Hello") to resolve before
logging it.

3. async/await with Promises

async/await makes working with Promises much more straightforward. Instead of


chaining .then() and .catch(), you can write asynchronous code like synchronous
code.

Using async/await with fetch() (common use case)

async function fetchData() {

try {

const response = await


fetch('https://jsonplaceholder.typicode.com/posts');

const data = await response.json(); // Wait for the JSON to be


parsed

console.log(data);

} catch (error) {

console.log("Error:", error);

fetchData();

Explanation:

●​ fetch() returns a Promise that resolves to a Response object.​

●​ We use await to pause and wait for the response.​

●​ Once the response is resolved, we use await again to parse the JSON data.​

●​ If any step fails (for example, if the network request fails), the error is caught by the
catch() block.

4. Why Use async/await?


Advantages over Promises:
1.​ Cleaner Code: It eliminates the need for nested .then() and makes the
asynchronous code look like synchronous code.​

2.​ Error Handling: With async/await, error handling becomes simpler with
try/catch.​

3.​ Better Debugging: Asynchronous errors are easier to trace because they behave
like normal errors in a synchronous function.

5. Example: Comparing Promises with async/await

Without async/await (using Promises directly):

function fetchData() {

return fetch('https://jsonplaceholder.typicode.com/posts')

.then(response => response.json())

.then(data => console.log(data))

.catch(error => console.log("Error:", error));

fetchData();

With async/await:

async function fetchData() {

try {

const response = await


fetch('https://jsonplaceholder.typicode.com/posts');

const data = await response.json();

console.log(data);

} catch (error) {

console.log("Error:", error);

fetchData();
Key Differences:

1.​ Readability: async/await makes the code easier to read and closer to how
synchronous code works.​

2.​ Error handling: async/await uses try/catch to handle errors in a


straightforward way, while Promises use .catch().

6. async/await with Multiple Promises


If you need to await multiple promises sequentially, you can chain them:

async function fetchData() {

try {

const response1 = await


fetch('https://jsonplaceholder.typicode.com/posts');

const data1 = await response1.json();

const response2 = await


fetch('https://jsonplaceholder.typicode.com/users');

const data2 = await response2.json();

console.log(data1, data2); // Logs both results

} catch (error) {

console.log("Error:", error);

fetchData();

Here, response2 will not execute until response1 has finished resolving.

Scoping in JavaScript
✅ What is Scoping?
Scoping refers to the visibility or accessibility of variables within different parts of your
code. It determines where a variable can be accessed or modified.
There are two main types of scopes in JavaScript:

1.1 Global Scope


●​ Variables declared outside of any function are in the global scope.​

●​ They are accessible from anywhere in your code.​

javascript

Copy

let globalVar = "I am global"; // Declared in global scope

function showGlobal() {

console.log(globalVar); // Can access globalVar inside the


function

showGlobal(); // Output: I am global

console.log(globalVar); // Output: I am global

●​ globalVar is accessible both inside and outside the showGlobal() function


because it's declared in the global scope.

1.2 Function Scope (Local Scope)


●​ Variables declared inside a function are in the local scope of that function.​

●​ These variables can only be accessed within that function.

function localScopeExample() {

let localVar = "I am local"; // Declared in function scope

console.log(localVar); // This works inside the function

localScopeExample(); // Output: I am local

console.log(localVar); // Error: localVar is not defined

●​ localVar is only accessible inside the function where it is declared. Outside the
function, it cannot be accessed.​
1.3 Block Scope (ES6)

●​ Introduced with let and const (in ES6), block scope means that variables
declared with let or const inside blocks (like loops or conditionals) are only
available within that block.

if (true) {

let blockScoped = "I am block scoped";

console.log(blockScoped); // Output: I am block scoped

console.log(blockScoped); // Error: blockScoped is not defined

●​ blockScoped is only available within the block (the if statement in this case).

2. Hoisting in JavaScript
✅ What is Hoisting?
Hoisting refers to the JavaScript behavior where variable declarations (using var, let, or
const) and function declarations are moved (or hoisted) to the top of their scope during
the compilation phase before the code is executed. This means that it is possible to use
variables and functions before they are declared in the code. However, it is important to note
that only declarations are hoisted, not initializations.

2.1 Hoisting with var

●​ Variables declared with var are hoisted to the top of their scope (either global or
function scope).​

●​ But only the declaration is hoisted, not the assignment.​

console.log(a); // Output: undefined

var a = 10;

console.log(a); // Output: 10

●​ The declaration var a is hoisted to the top, but its assignment (a = 10) happens
only when the code is executed, so the first console.log(a) prints undefined.​

How this works:


1.​ Step 1: JavaScript knows that var a exists but hasn't been assigned yet.​

2.​ Step 2: The variable a is assigned the value 10 after the hoisting.

2.2 Hoisting with let and const

●​ let and const are also hoisted, but they have a special behavior called the
"temporal dead zone" (TDZ).​

●​ The TDZ refers to the time between the beginning of the scope and the actual line
where the variable is declared and initialized.​

●​ Variables declared with let or const are not accessible before their declaration
line. Accessing them before the declaration results in a ReferenceError.

console.log(b); // Error: Cannot access 'b' before initialization

let b = 20;

●​ Even though let b is hoisted, it cannot be accessed before the line where it is
initialized. This avoids issues caused by hoisting with var.​

2.3 Hoisting with Function Declarations


●​ Function declarations are hoisted completely (both the definition and the body), so
they can be called before their declaration.

greet(); // Output: "Hello!"

function greet() {

console.log("Hello!");

●​ The greet() function is hoisted to the top of its scope, so it can be invoked even
before its actual declaration.​

2.4 Hoisting with Function Expressions


●​ Function expressions (including arrow functions) behave differently:​

○​ They are not hoisted like function declarations.​

○​ The variable to which the function is assigned is hoisted, but its assignment
happens during execution.

console.log(myFunc); // Output: undefined

var myFunc = function() {


console.log("Hello from function expression");

};

myFunc(); // Output: "Hello from function expression"

●​ Here, myFunc is hoisted but is initially undefined. The function is only assigned
during execution, so calling it before the assignment leads to an error.​

Summary

Concept Explanation Example

Scope Determines where variables can be var a = 5; — global scope; let b =


accessed. 10; — block scope

Global Scope Variables are accessible anywhere var x = 100; — accessible globally.
in the program.

Local Scope Variables are accessible only within let y = 20; — only accessible in the
the function or block. function/block

Hoisting The process where variable var a = 5; — hoisted but undefined


declarations and function initially.
declarations are moved to the top.

Hoisting with Declarations are hoisted, but console.log(a); var a = 10; —


var values are not initialized. prints undefined

Hoisting with They are hoisted but not accessible console.log(b); let b = 20; —
let/const before declaration (TDZ). Error

Hoisting with Function declarations are fully greet(); function greet() {


functions hoisted (both declaration and console.log("Hi!"); } — Works
body). fine.
Function Hoisted as undefined; assignment var myFunc = function() {};
Expressions happens during execution. myFunc(); — Cannot be called before
assignment.

1. var (Old way of declaring variables)


✅ Characteristics of var:
●​ Function-scoped (or globally-scoped if declared outside a function).​

●​ Can be redeclared within the same scope.​

●​ Variables declared with var are hoisted to the top of the scope, but initialized to
undefined.

Scope:

●​ var is function-scoped, meaning it is available within the function it is declared in,


or globally if declared outside any function.

function example() {

var x = 10; // 'x' is function-scoped

if (true) {

var y = 20; // 'y' is also function-scoped, not block-scoped

console.log(x, y); // 10 20

example();

●​ y is accessible outside the if block because var is not block-scoped, only


function-scoped.

Hoisting:

●​ Variables declared with var are hoisted, but their value assignment happens only at
runtime.

console.log(a); // Output: undefined

var a = 5;
console.log(a); // Output: 5

●​ Here, a is hoisted, but its value is only assigned when the code is executed. Hence,
the first console.log(a) prints undefined.

Redeclaration:

●​ You can redeclare a variable declared with var without an error.​

var a = 10;

var a = 20; // No error

console.log(a); // Output: 20

2. let (Introduced in ES6)


✅ Characteristics of let:
●​ Block-scoped, meaning it is only accessible within the block it is declared in (e.g.,
within an if statement or a loop).​

●​ Cannot be redeclared within the same scope.​

●​ Variables declared with let are hoisted but not initialized until the line of
declaration, leading to the temporal dead zone (TDZ).

Scope:

●​ let is block-scoped. It is only accessible within the block {} where it is declared.

if (true) {

let x = 10; // 'x' is block-scoped

console.log(x); // Output: 10

console.log(x); // Error: x is not defined

●​ x is not accessible outside the if block because let is block-scoped.

Hoisting:
●​ Hoisted to the top, but not initialized until the declaration is reached, which leads to
the temporal dead zone (TDZ).​
console.log(a); // Error: Cannot access 'a' before initialization

let a = 5;

●​ Here, the variable a is hoisted, but it cannot be accessed before its declaration
because it’s in the temporal dead zone.

Redeclaration:

●​ You cannot redeclare a variable declared with let in the same scope.

let a = 10;

let a = 20; // Error: Identifier 'a' has already been declared

3. const (Also introduced in ES6)


✅ Characteristics of const:
●​ Block-scoped like let.​

●​ Cannot be reassigned once the variable is initialized. This means you cannot
change the value of the variable after it’s been declared.​

●​ Hoisted but not initialized, just like let. The temporal dead zone applies.​

●​ Must be initialized at the time of declaration.

Scope:

●​ const is also block-scoped, just like let.

if (true) {

const x = 10; // 'x' is block-scoped

console.log(x); // Output: 10

console.log(x); // Error: x is not defined

●​ x is not accessible outside the if block because it is declared using const.

Hoisting:
●​ Hoisted to the top, but not initialized until the declaration is reached, resulting in the
temporal dead zone.

console.log(a); // Error: Cannot access 'a' before initialization


const a = 5;

●​ The variable a is hoisted, but it cannot be accessed before the const declaration,
which causes an error due to the temporal dead zone.

Reassignment:

●​ You cannot reassign a variable declared with const.

const a = 10;

a = 20; // Error: Assignment to constant variable.

●​ Once a is assigned 10, it cannot be changed.

Objects and Arrays with const:


●​ While you cannot reassign the entire object or array, you can modify the properties
or elements inside the object or array.

const arr = [1, 2, 3];

arr.push(4); // Works fine

console.log(arr); // Output: [1, 2, 3, 4]

arr = [5, 6]; // Error: Assignment to constant variable

●​ You can modify the contents of arr because the reference to the array object is not
changing.​

●​ But if you try to reassign arr itself (like assigning a new array), you will get an error.​

1. Closures in JavaScript
✅ What is a Closure?
A closure in JavaScript is a function that retains access to its lexical scope even when
the function is executed outside that scope. In simpler terms, closures allow a function to
"remember" the variables from the scope in which it was created.

✅ How Does It Work?


When a function is created, it is lexically scoped, meaning it remembers the environment
(the variables) in which it was defined. This environment sticks with the function, even if
the function is called later in a different context.

✅ Example: Simple Closure


function outerFunction() {

let outerVar = "I am outside!";

function innerFunction() {

console.log(outerVar); // Accesses outerVar from outerFunction

return innerFunction; // Returns the inner function as a closure

const closureFunc = outerFunction();

closureFunc(); // Output: I am outside!

In this example, the closure works by allowing the increment and


decrement methods to remember and access the count variable, even
after the counter() function has finished executing.

Key Points:

1.​counter() creates the count variable within its scope.​

2.​It returns an object with two methods (increment and


decrement), each of which remembers the count variable because
they are defined inside the counter() function.​

3.​When you call myCounter.increment() or myCounter.decrement(),


those methods still have access to the count variable due to
the closure, allowing them to modify and log its value.​

In essence, the closure allows the methods to persistently access


and modify the count variable, which would otherwise be out of scope
after counter() finishes executing.

What happens here?

1.​ outerFunction defines a variable outerVar and returns the innerFunction.​

2.​ innerFunction remembers the scope of outerFunction because it is a


closure. Even when it's executed outside of outerFunction, it still has access to
outerVar.

✅ Why are Closures Useful?


●​ Data encapsulation: Closures allow us to hide variables from the global scope and
keep them private.​

●​ Emulating private methods: Closures are often used to create private variables
and methods.​

Example: Creating Private Variables Using Closures

function counter() {

let count = 0; // private variable

return {

increment: function() {

count++;

console.log(count);

},

decrement: function() {

count--;

console.log(count);

};

const myCounter = counter();

myCounter.increment(); // Output: 1

myCounter.increment(); // Output: 2

myCounter.decrement(); // Output: 1

●​ The count variable is private and cannot be accessed directly from outside the
counter() function.​

●​ The inner functions (increment and decrement) can access and modify the
count variable via closure.
2. Prototype Chain in JavaScript
✅ What is the Prototype Chain?
Every JavaScript object has a property called prototype, which points to another object.
The prototype chain is the series of objects that JavaScript looks through to find properties
or methods that are not directly present in an object.

●​ If you access a property or method on an object and the object doesn't have it,
JavaScript looks for it in the prototype of that object.​

●​ If it's not in the prototype, it looks in the prototype's prototype, and so on, until it
reaches the Object.prototype, which is the root object.​

✅ Example: Prototype Chain


const person = {

name: "John",

greet() {

console.log("Hello " + this.name);

};

const student = Object.create(person); // student inherits from


person

student.name = "Alice";

student.greet(); // Output: Hello Alice

Explanation:

●​ student inherits from person through the prototype chain.​

●​ greet() is defined in person, and even though it's not in student, student can
still use it because of the prototype chain.​

●​ The prototype chain works like this: student → person → Object.prototype.​

✅ Prototype Chain Visualized:


student --> person --> Object.prototype --> null
✅ What is Object.prototype?
●​ Object.prototype is at the top of the prototype chain. All objects in JavaScript
(except null) inherit from Object.prototype.​

●​ It provides built-in methods like toString(), hasOwnProperty(), etc.​

✅ Inheritance Example:
const animal = {

eats: true

};

const dog = Object.create(animal);

dog.barks = true;

console.log(dog.eats); // Output: true (inherited from animal)

console.log(dog.barks); // Output: true (own property of dog)

●​ dog inherits eats from animal, but barks is specific to dog.

3. The 3 JavaScript Pillars (this, Closure, and


**Prototype`)
✅ this in JavaScript
What is this?

●​ this is a special keyword in JavaScript that refers to the context in which a


function is executed.​

●​ The value of this depends on how a function is called. It can refer to different
objects, depending on whether the function is invoked as a method, constructor, or
standalone function.​

✅ Examples of this:
1. this in Global Context

console.log(this); // In browser, 'this' refers to the Window


object
●​ In the global scope, this refers to the global object (window in browsers,
global in Node.js).​

2. this in a Method

const person = {

name: "John",

greet() {

console.log(this.name); // 'this' refers to the person object

};

person.greet(); // Output: John

●​ Inside a method, this refers to the object calling the method (person in this
case).​

3. this in a Constructor Function

function Person(name) {

this.name = name;

const person1 = new Person("Alice");

console.log(person1.name); // Output: Alice

●​ In constructor functions, this refers to the new object being created (here,
person1).​

4. this in Arrow Functions

Arrow functions do not have their own this. Instead, they inherit this from their lexical
scope.

const person = {

name: "John",

greet: () => {
console.log(this.name); // 'this' refers to the global object,
not person

};

person.greet(); // Output: undefined (in the browser, 'this' refers


to the window object)

●​ Since arrow functions do not have their own this, they inherit this from where
they were defined, not from where they are called.

✅ Closure (Covered Earlier)


●​ A closure is a function that retains access to its lexical scope, even after the
function has executed and returned.​

●​ It’s created when a function is defined within another function and retains access to
the outer function's variables.

✅ Prototype (Covered Earlier)


●​ The prototype chain allows objects to inherit properties and methods from other
objects.​

●​ Every object in JavaScript has an internal property called [[Prototype]], which


refers to the object from which it inherits.​

1. Debounce:
✅ What is Debounce?
●​ Debouncing is a technique used to limit the rate at which a function is executed.​

●​ It ensures that the function is executed only after a certain amount of idle time (or
delay) has passed since the last time the function was called.​

●​ Essentially, it delays the execution of the function until a specified period has
elapsed since the last event.​

✅ Use Case for Debounce:


●​ Search inputs: When a user types in a search box, you don’t want to trigger a
search for every keystroke. Instead, you wait until the user stops typing for a brief
period.​

●​ Window resizing: For example, when resizing the window, you don’t want to trigger
expensive calculations or UI updates on every resize event. You can wait until the
user stops resizing the window.
✅ Example Code for Debounce:
Let’s take a search input scenario:

function search(query) {

console.log("Searching for:", query);

function debounce(func, delay) {

let timer;

return function(...args) {

clearTimeout(timer); // Clear the previous timer

timer = setTimeout(() => func(...args), delay); // Set a new


timer

};

const debouncedSearch = debounce(search, 500);

document.getElementById('searchInput').addEventListener('input',
(event) => {

debouncedSearch(event.target.value);

});

Explanation:

●​ When the user types in the search box, the debouncedSearch function is called.​

●​ Each keystroke cancels the previous timer and sets a new one, so the search
function is only called 500ms after the user stops typing.​

●​ This reduces the number of calls to search().

✅ How Debounce Works:


1.​ A timer is set each time the event occurs.​

2.​ If the event is triggered again before the timer ends, the previous timer is cleared,
and a new one is started.​

3.​ The function is executed only when the event stops for the specified delay.
✅ When to Use Debounce:
●​ Form validation or autocomplete for search inputs.​

●​ Resizing or scrolling events where you want to delay action until the user stops
interacting.

2. Throttle:
✅ What is Throttle?
●​ Throttling ensures that a function is executed at most once in a specified time
interval.​

●​ Unlike debouncing, throttle ensures that the function is executed at regular


intervals, even if the event is triggered frequently (e.g., scrolling, resizing).

✅ Use Case for Throttle:


●​ Scroll events: If you want to trigger an action (e.g., lazy loading of content) as the
user scrolls, but don’t want to run the function on every scroll event, you can throttle
the function.​

●​ Resize events: If you want to resize an element in sync with the window, but avoid
continuous resizing, throttling ensures it only happens at regular intervals.​

✅ Example Code for Throttle:


Let’s take a scroll event scenario:

function loadMoreContent() {

console.log("Loading more content...");

function throttle(func, delay) {

let lastTime = 0;

return function(...args) {

const now = new Date().getTime();

if (now - lastTime >= delay) {

lastTime = now;

func(...args);

}
};

const throttledLoadMore = throttle(loadMoreContent, 1000);

window.addEventListener('scroll', throttledLoadMore);

Explanation:

●​ When the user scrolls, the throttledLoadMore function is called.​

●​ The function ensures that loadMoreContent() is called at most once every


second (1000ms), regardless of how many times the scroll event is triggered.

✅ How Throttle Works:


1.​ A timestamp is stored each time the function is called.​

2.​ If the function is called again before the delay has passed, it is ignored.​

3.​ The function is called once every fixed interval (e.g., every 1000ms).

✅ When to Use Throttle:


●​ Scroll events, where you want to trigger an action (like infinite scroll or animations)
at fixed intervals.​

●​ Resize events, where you want to run an action (e.g., adjust the layout) at fixed
intervals, rather than on every resize.​

●​ Mousemove events, where you want to track mouse movement at a manageable


rate.​

1. Higher-Order Functions (HOF)


✅ What is a Higher-Order Function?
A higher-order function is a function that can do at least one of these:

●​ Accept another function as an argument​

●​ Return a function as its result​

This means functions can be passed around and manipulated like variables.

✅ Why are HOFs important?


●​ They allow for flexible, reusable, and composable code.​

●​ They enable functional programming patterns like callbacks, map/filter/reduce,


decorators, etc.

✅ Example: Passing a function as argument


function greet() {

console.log("Hello!");

function caller(fn) {

fn(); // call the passed-in function

caller(greet); // Output: Hello!

●​ caller is a higher-order function because it accepts a function fn as an argument


and calls it.​

✅ Example: Returning a function


function multiplier(factor) {

return function(number) {

return number * factor;

};

const twice = multiplier(2);

console.log(twice(5)); // Output: 10

●​ multiplier returns a function that multiplies a number by factor.​

●​ This returned function remembers the factor via closure (next topic).

2. Closures and Memory Behavior


✅ What is a Closure?
A closure is a function that remembers its surrounding state (variables) even after its
outer function has finished executing.

✅ Memory Behavior of Closures


●​ When a function is created, the JavaScript engine keeps a reference to the
variables in its lexical environment (the scope in which it was defined).​

●​ Even if the outer function returns and its execution context is popped off the
call stack, those variables are not garbage collected as long as the inner function
(closure) still references them.​

●​ This means closures keep variables “alive” in memory for as long as the closure
exists.

✅ Example: Closure memory behavior


function outer() {

let count = 0; // Variable in outer scope

return function inner() {

count++;

console.log(count);

};

const counter = outer(); // `outer` returns `inner`

counter(); // Output: 1

counter(); // Output: 2

●​ Even though outer() finished executing, the variable count remains in memory
because the returned function inner() closes over it.​

●​ Each call to counter() updates and remembers the same count variable.

✅ Why does this matter?


●​ Memory Usage: Variables captured by closures stay in memory until all references
to the closure are gone.​

●​ Data Encapsulation: Closures allow you to create private state.​

●​ Beware Memory Leaks: If you keep references to closures unnecessarily, memory


may not be freed.​
1. Callbacks
●​ A callback is a function passed as an argument to another function, which is then
called (executed) later inside that function.​

●​ Used to handle asynchronous tasks or to customize behavior.​

Example:

function greet(name, callback) {

console.log("Hello " + name);

callback();

greet("Ali", () => console.log("Callback called!"));

●​ Here, the second argument is a callback function that runs after greeting.

2. map, filter, reduce


These are array methods that take callback functions to process arrays.

●​ map: Creates a new array by transforming each element.

const numbers = [1, 2, 3];

const doubled = numbers.map(n => n * 2); // [2, 4, 6]

●​ filter: Creates a new array with elements that pass a test.

const numbers = [1, 2, 3, 4];

const evens = numbers.filter(n => n % 2 === 0); // [2, 4]

●​ reduce: Reduces array to a single value by applying a function cumulatively.

const numbers = [1, 2, 3, 4];

const sum = numbers.reduce((acc, n) => acc + n, 0); // 10

3. Decorators
●​ A decorator is a function that wraps another function to modify or enhance its
behavior without changing the original function’s code.​

●​ Often used in frameworks and advanced JavaScript patterns.​

Simple Example:
function logDecorator(fn) {

return function(...args) {

console.log("Calling function with args:", args);

return fn(...args);

};

function sayHi(name) {

console.log("Hi " + name);

const decoratedSayHi = logDecorator(sayHi);

decoratedSayHi("Ali");

// Logs:

// Calling function with args: ["Ali"]

// Hi Ali

REST API
REST API stands for REpresentational State Transfer API. It is a type of API (Application
Programming Interface) that allows communication between different systems over the
internet. REST APIs work by sending requests and receiving responses, typically in JSON
format, between the client and server.

REST APIs use HTTP methods (such as GET, POST, PUT, DELETE) to define actions that
can be performed on resources. These methods align with CRUD (Create, Read, Update,
Delete) operations.
A request is sent from the client to the server via a web URL, using one of the HTTP
methods. The server then responds with the requested resource, which could be HTML,
XML, Image, or JSON, with JSON being the most commonly used format for modern web
services.

Key Features of REST APIs


Stateless: Each request from a client to a server must contain all the information the server
needs to fulfill the request. No session state is stored on the server.

Client-Server Architecture: RESTful APIs are based on a client-server model, where the
client and server operate independently, allowing scalability.

Cacheable: Responses from the server can be explicitly marked as cacheable or


non-cacheable to improve performance.

Uniform Interface: REST APIs follow a set of conventions and constraints, such as
consistent URL paths, standardized HTTP methods, and status codes, to ensure smooth
communication.

Layered System: REST APIs can be deployed on multiple layers, which helps with
scalability and security.

HTTP Methods (for REST APIs)


In REST, communication between the client and server happens using the HTTP protocol.
The HTTP methods define the actions that can be performed on resources (identified by
URLs) in a REST API.

Common HTTP Methods:

1.​ GET:​

○​ Retrieve data from the server without making any modifications.​

○​ It is used to fetch resources or data, such as getting a list of items or


retrieving a specific item.​

○​ Example: GET /users (Retrieve the list of users)​

2.​ POST:​

○​ Create a new resource on the server.​


○​ It is used to submit data to be processed by the server (e.g., creating a new
user or adding an item).​

○​ Example: POST /users (Create a new user)​

3.​ PUT:​

○​ Update a resource on the server.​

○​ It is used to replace the current representation of a resource with the new


data sent in the request.​

○​ If the resource doesn't exist, it can also create a new resource.​

○​ Example: PUT /users/1 (Update the user with ID 1)​

4.​ PATCH:​

○​ Partially update a resource on the server.​

○​ Unlike PUT, which typically requires the entire resource to be sent, PATCH
allows updating only specific fields.​

○​ Example: PATCH /users/1 (Update the user with ID 1, e.g., just their
email)​

5.​ DELETE:​

○​ Delete a resource from the server.​

○​ It is used to remove a specific resource.​

○​ Example: DELETE /users/1 (Delete the user with ID 1)​

Summary of HTTP Methods:

HTTP Action Use Case


Method

GET Retrieve data Fetch resources (e.g., GET /users to fetch users)
POST Create a new Add new data (e.g., POST /users to create a user)
resource

PUT Update a resource Replace data (e.g., PUT /users/1 to update user
with ID 1)

PATCH Partially update a Modify part of the data (e.g., PATCH /users/1 to
resource update part of user data)

DELETE Delete a resource Remove data (e.g., DELETE /users/1 to delete


user with ID 1)

Middleware:
In web development, middleware refers to functions that run between the request and
response cycle in a web server (e.g., Express in Node.js). Middleware can be used to handle
tasks like authentication, logging, data parsing, or handling errors before sending the final
response back to the client.

2. CORS (Cross-Origin Resource Sharing):

●​ What is CORS?​

○​ CORS is a security feature that allows or restricts resources (like APIs or


data) on a server to be accessed by web pages from different origins
(domains).​

●​ Why does CORS exist?​

○​ Web browsers enforce security by preventing websites from accessing


resources from other domains unless the server explicitly allows it. This is to
prevent cross-site scripting attacks.​

●​ How CORS works?​

○​ If a website from Domain A tries to make a request to a server on Domain B,


the server needs to include certain HTTP headers like
Access-Control-Allow-Origin to tell the browser that it allows the
request.​

Example:​
// In Express.js middleware

app.use((req, res, next) => {

res.header("Access-Control-Allow-Origin", "*"); // Allow all


domains

next(); // Pass control to the next middleware

});

●​ Key Terms:​

○​ Origin: The combination of the protocol (http/https), domain, and port.​

○​ Preflight Request: A CORS check made by the browser before sending the
actual request, to see if the server allows it.

What is the MVC Pattern?

MVC stands for Model-View-Controller. It is an architectural pattern that is commonly used


to structure applications in a way that separates different aspects of the application logic.
This helps in organizing code efficiently, making it easier to maintain, test, and scale the
application.

In simple terms:

●​ Model: Represents the data or the business logic.​

●​ View: Represents the user interface (UI) and displays the data to the user.​

●​ Controller: Handles the user input, manipulates the model, and updates the view.​

MVC Components Explained:

1.​ Model:​

○​ The Model is responsible for managing the data. It communicates directly


with the database or any external data source to fetch, save, or manipulate
the data.​

○​ The Model does not concern itself with the user interface. It only represents
and manages the data.​

○​ Example: In a blog application, the Post model would represent the blog post
data (title, content, author, etc.).​

2.​ View:​

○​ The View is responsible for displaying the data to the user and updating the
UI.​

○​ It is independent of the logic and merely renders what it gets from the
controller or the model.​

○​ Example: In a blog application, the view would display a list of blog posts to
the user.​

3.​ Controller:​

○​ The Controller acts as an intermediary between the Model and the View.​

○​ It processes incoming requests from the user, interacts with the Model to
retrieve or manipulate data, and then updates the View with the results.​

○​ Example: In a blog application, the controller would handle actions like


displaying a post, creating a new post, or editing a post.​

How does the MVC pattern work together?

1.​ User interacts with the View (e.g., by submitting a form).​

2.​ The Controller receives the input from the View and processes it (e.g., validates
form data).​

3.​ The Controller then updates the Model (e.g., adds a new post to the database).​

4.​ After the Model is updated, the Controller refreshes the View with updated data
(e.g., show the new blog post on the page).​

Mapping the MVC Pattern to Express

In an Express.js application, the MVC pattern can be mapped as follows:


1.​ Model: This is typically a data handler (e.g., using MongoDB, SQL, or files) that
communicates with the database to manage application data. In Express, the Model
usually involves defining schemas and data operations (like saving, updating, and
deleting data).​

2.​ View: In the context of Express, the View is responsible for rendering HTML
templates to the user. This could be done using template engines like EJS, Pug, or
Handlebars. The View is rendered by the Controller based on the data it gets from
the Model.​

3.​ Controller: The Controller in Express is implemented as route handlers. These


route handlers handle the HTTP requests (GET, POST, PUT, DELETE), interact with
the Model (performing database operations), and then render the View or send a
response back to the client.​

JSX Syntax (JavaScript XML)

JSX is a syntax extension for JavaScript used with React. It looks very similar to HTML, but
it’s actually syntactic sugar for React.createElement() calls.

What is JSX?

●​ JSX allows you to write HTML-like code within JavaScript files.​

●​ React components are often written using JSX to describe the UI.​

●​ JSX is not valid JavaScript, but React has a compiler that converts JSX into valid
JavaScript.​

Example: JSX Syntax

const element = <h1>Hello, World!</h1>;

●​ Here, <h1> is HTML-like syntax inside JavaScript. When rendered, React converts
this into React.createElement('h1', null, 'Hello, World!').​

JSX with Components

function Greeting(props) {

return <h1>Hello, {props.name}!</h1>;


}

●​ In this example, {props.name} is JavaScript inside JSX. React will replace it with
the value passed through props.​

JSX Rules:

●​ Self-closing tags: Just like in HTML, JSX uses self-closing tags like <img />,
<input />.​

●​ Expression: {} is used to embed JavaScript expressions inside JSX.​

const element = <h1>{2 + 3}</h1>; // This will render: <h1>5</h1>

●​ Attribute values in JSX are passed using camelCase, e.g., className instead of
class.​

2. Props vs State

Both props and state are used to manage data in React components, but they serve
different purposes.

Props (short for Properties)

●​ Props are used to pass data from a parent component to a child component.​

●​ They are read-only and cannot be changed by the child component.​

●​ Props are immutable within the child component, i.e., the child cannot modify its
props.​

Example of Props:

function Greeting(props) {

return <h1>Hello, {props.name}!</h1>;

}
function App() {

return <Greeting name="Alice" />;

●​ Parent (App) passes the name prop to the child (Greeting).​

●​ Inside Greeting, props.name is used to render the name.

State

●​ State is used to manage data that can change over time within a component.​

●​ Unlike props, state is mutable and can be updated by the component itself.​

●​ State is usually used for user input, interactivity, and component-specific data.​

Example of State:

import { useState } from 'react';

function Counter() {

const [count, setCount] = useState(0); // useState hook for state

return (

<div>

<p>Count: {count}</p>

<button onClick={() => setCount(count + 1)}>Increase</button>

</div>

);

}
●​ State (count) starts at 0 and can be updated by calling setCount.​

●​ Clicking the button updates the state, causing the component to re-render with the
updated value.​

React Hooks are functions that allow you to use state and other React features in
functional components. Before Hooks, you could only manage state and lifecycle methods
in class components, but Hooks make it possible to do this in functional components too.

Key Hooks:

1.​ useState():​

○​ Allows you to add state to a functional component.​

Example:​

javascript​
Copy​
const [count, setCount] = useState(0); // Declare a state variable
`count` with initial value 0

○​
○​ count is the state, and setCount is the function used to update it.​

2.​ useEffect():​

○​ Allows you to perform side effects (like fetching data or manipulating the
DOM).​

○​ It runs after the component renders, and you can control when it runs (e.g.,
on mount, update, or unmount).​

Example:​

javascript​
Copy​
useEffect(() => {

console.log('Component mounted or updated');

}, [count]); // Runs when `count` changes


○​
3.​ useContext():​

○​ Allows you to access React context (global state) in functional components.​

Example:​

javascript​
Copy​
const value = useContext(MyContext); // Get the value from context

○​

Why Use Hooks?

●​ Simpler syntax compared to class components.​

●​ Cleaner code with less boilerplate.​

●​ Manage state, side effects, and context in functional components.​

1. Functional Components:

✅ Definition:
●​ Functional components are simple JavaScript functions that return JSX to render the
UI.​

●​ They are stateless by default but can use Hooks (like useState and useEffect)
to manage state and side effects in modern React.​

✅ Key Characteristics:
●​ They are easier to read and write.​

●​ Prior to React 16.8, functional components were stateless and could only receive
props. After the introduction of Hooks, functional components can now handle state,
lifecycle methods, and side effects.

Example of a Functional Component:

jsx
Copy

function Greeting(props) {

return <h1>Hello, {props.name}!</h1>;

function App() {

return <Greeting name="Alice" />;

●​ Here, Greeting is a functional component that takes props and returns JSX.​

✅ Modern Usage (with Hooks):


jsx

Copy

import { useState } from 'react';

function Counter() {

const [count, setCount] = useState(0); // useState hook for state

return (

<div>

<p>Count: {count}</p>

<button onClick={() => setCount(count + 1)}>Increase</button>

</div>

);
}

●​ useState is a hook that allows functional components to manage state.​

2. Class Components:

✅ Definition:
●​ Class components are ES6 JavaScript classes that extend React.Component.​

●​ They were the standard way to create components in React before React 16.8,
which introduced Hooks.​

●​ Class components can have state, lifecycle methods, and event handlers.​

✅ Key Characteristics:
●​ They are more complex than functional components.​

●​ They can manage state and have lifecycle methods like componentDidMount,
componentDidUpdate, etc.​

✅ Example of a Class Component:


jsx

Copy

class Greeting extends React.Component {

render() {

return <h1>Hello, {this.props.name}!</h1>;

function App() {
return <Greeting name="Alice" />;

●​ Here, Greeting is a class component. It uses this.props to access the


properties passed to it.​

✅ Class Component with State:


jsx

Copy

class Counter extends React.Component {

constructor(props) {

super(props);

this.state = { count: 0 }; // State initialization

increment = () => {

this.setState({ count: this.state.count + 1 });

};

render() {

return (

<div>

<p>Count: {this.state.count}</p>

<button onClick={this.increment}>Increase</button>

</div>

);
}

●​ Counter is a class component that uses this.state to manage state.​

●​ this.setState() is used to update the state.​

You might also like