0% found this document useful (0 votes)
3 views123 pages

JavaScript

The document covers the basics of JavaScript arrays and objects, including how to declare, access, and manipulate them. It explains concepts like shallow vs deep copying of arrays, merging arrays, and the use of destructuring for cleaner code. Additionally, it introduces functions, their definitions, and the importance of parameters and return values.

Uploaded by

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

JavaScript

The document covers the basics of JavaScript arrays and objects, including how to declare, access, and manipulate them. It explains concepts like shallow vs deep copying of arrays, merging arrays, and the use of destructuring for cleaner code. Additionally, it introduces functions, their definitions, and the importance of parameters and return values.

Uploaded by

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

🎥 JavaScript Arrays

📌 Why Arrays?
 Before diving into if/else and loops, it's important to
understand how to structure and store data.
 Arrays are fundamental data structures, not in the DSA
(competitive) sense, but as a way to organize multiple
values in one variable.

Declaring an Array
const myArray = [0, 1, 2, 3, 4, 5];
🧠 Notes:
 Arrays are defined using square brackets [].
 Each value inside the array is called an element.
 Elements can be:
o Numbers
o Strings
o Booleans
o Objects
o Even another array (nested arrays)
✅ Mixed types are allowed:
const myArray = [1, "Hitesh", true];

📚 Behind the Scenes


 Arrays in JavaScript are Objects.
 They're resizable – meaning you can add more elements
after creation.
 Arrays are not associative: you can't access elements using
custom string keys (like you do in objects).

🧾 Accessing Array Elements


console.log(myArray[0]); // Accessing the first element
 JavaScript uses zero-based indexing (starts from 0).
 Index must be a number, not a string.

🔁 Copying Arrays — Shallow vs Deep Copy

📌 Shallow Copy
 When you copy an array like this:
const copyArray = myArray;
Both arrays point to the same memory reference. Changes in one
affect the other.

📌 Deep Copy
 To create a separate copy:
const deepCopy = [...myArray]; // Using spread operator
Now changes in deepCopy won't affect myArray.

Some popular methods in JavaScript arrays:


 push() / pop()
 shift() / unshift()
 map() / filter() / reduce()
 slice() / splice()
 and many more…

🧱 Alternative Ways to Declare Arrays


Other than literal syntax, arrays can be declared using the
constructor:
const arr = new Array(10); // Creates array with 10 empty slots
But literal [] is preferred for clarity and readability.

🏁 Summary
 Arrays are crucial for organizing data in JavaScript.
 Know how to:
o Declare them
o Access elements
o Understand memory and copy behavior

🔹 JavaScript Array Basics


const marvel_heroes = ["Thor", "Iron Man", "Spider-Man"];
const dc_heroes = ["Superman", "Flash", "Batman"];

🔹 Mistake Example: Pushing One Array into Another

marvel_heroes.push(dc_heroes);
console.log(marvel_heroes);
❌ Output:
["Thor", "Iron Man", "Spider-Man", ["Superman", "Flash",
"Batman"]]
 Instead of merging both arrays, the entire dc_heroes array is
added as one element at index 3.
 Array.push() adds data as-is, meaning it can accept arrays,
objects, booleans, etc., as a single item.

🔍 Accessing values from this structure:


console.log(marvel_heroes[3][1]); // "Flash"
✨ Not a good approach if you want to merge two arrays cleanly.

🔹 ✅ Correct Method: Using .concat()


const all_heroes = marvel_heroes.concat(dc_heroes);
console.log(all_heroes);
✅ Output:
["Thor", "Iron Man", "Spider-Man", "Superman", "Flash",
"Batman"]
 .concat() merges two arrays properly.
 It creates a new array instead of modifying the original one.

🔹 Alternative: Spread Operator (ES6)

const all_heroes_spread = [...marvel_heroes, ...dc_heroes];


console.log(all_heroes_spread);
 Gives the same result as .concat(), but is more readable and
flexible.

🔹 Console Tip:

Use browser dev tools console to experiment with JavaScript


arrays.
const sampleArray = [1, 2, 3];
sampleArray.__proto__ // Shows all built-in array methods like
push, pop, shift, etc.

🔹 Summary
Metho Mutates
Purpose Returns
d Original?

.push( Adds element(s) at the New


✅ Yes
) end length

.conca New
Combines two arrays ❌ No
t() array

Spread elements into New


[...] ❌ No
new array array

Introduction to Objects
In JavaScript, objects are used to store collections of data in the
form of key-value pairs. They are very useful and a core concept
of the language.
Declaration of Objects
There are two ways to declare an object in JavaScript:
1. Object Literals
2. Using Constructors (Object.create)

Object Literal Declaration:


An object literal is the most common way to create an object in
JavaScript. It's also called an object literal because the object is
created directly using curly braces {}.
const user = {
name: 'Hitesh',
email: '[email protected]',
location: 'Jaipur'
};
 Here, the object user is declared with three properties:
name, email, and location. These are key-value pairs.
 In JavaScript, keys are usually strings, but you can omit the
quotes for standard identifiers (like name, email, etc.).

Constructor Method:
An object can also be created using the Object.create() method.
const user = Object.create({
name: 'Hitesh',
email: '[email protected]',
location: 'Jaipur'
});
 This method creates a new object with the specified
prototype object.
 When using the constructor method, a Singleton object is
created. That means only one object instance is created.
Accessing Object Properties
You can access object properties in two ways:
1. Dot Notation: This is the most common way of accessing
object properties.
console.log(user.name); // Output: Hitesh
2. Bracket Notation: Sometimes, you may need to access object
properties dynamically, or the key might not be a valid
identifier. In such cases, use bracket notation.
console.log(user['email']); // Output: [email protected]
Key-Value in Object:
JavaScript objects allow any type of value as a key, such as
strings, numbers, and even symbols.
const user = {
name: 'Hitesh',
age: 25,
location: 'Jaipur',
email: '[email protected]',
isLoggedIn: true,
lastLoginDays: ['Monday', 'Saturday']
};
You can store different data types like strings, numbers, arrays,
booleans, etc., as values in an object.

Important Concepts:
Symbol in JavaScript Objects
A symbol is a unique and immutable primitive value that can be
used as the key of an object. Symbols are useful for creating keys
that won't conflict with other keys.
const sym = Symbol('id');
const user = {
[sym]: '12345',
name: 'Hitesh'
};
 sym is a symbol that is used as a key for the user object.
Symbols are unique, so even if two symbols have the same
description, they will be different.

Object Key as a String:


By default, when you define a key in an object, it's treated as a
string. Even if you don’t add quotes around a key name,
JavaScript will convert it to a string.
const user = {
name: 'Hitesh',
age: 25
};

console.log(user.name); // Output: Hitesh


console.log(user['age']); // Output: 25

Advanced Object Handling


Adding New Properties to an Object:

You can easily add new properties to an existing object, either


using dot notation or bracket notation.
user.address = 'New Delhi'; // Adding a property using dot
notation
user['email'] = '[email protected]'; // Adding a property
using bracket notation
Nested Objects:
Objects can contain other objects. This allows you to create
complex data structures.

const user = {
name: 'Hitesh',
address: {
city: 'Jaipur',
state: 'Rajasthan'
}
};

console.log(user.address.city); // Output: Jaipur

Summary
1. Object Literals are a simple way to create objects in
JavaScript.
2. Constructor Method (Object.create()) is used for creating
objects with a prototype.
3. Access Object Properties using Dot Notation or Bracket
Notation.
4. Symbols are used for creating unique keys.
5. You can add new properties to an object, and objects can be
nested.

Today, we will continue talking about objects, specifically:


o Single-ton objects.
o Constructor-based object declarations.

Creating Objects
1. Single-Ton Object:
o Example:
let tinderUser = {
id: 'abc123',
name: 'Sam',
isLoggedIn: false
};
console.log(tinderUser);
o This is a simple object declaration where tinderUser is
an object with three properties: id, name, and
isLoggedIn.

2. Constructor-Based Object:
o Example:
const tinderUser = new Object();
tinderUser.id = 'abc123';
tinderUser.name = 'Sam';
tinderUser.isLoggedIn = false;
console.log(tinderUser);

o Both approaches give the same result.


o Key Difference: The first one is a single-turn object,
and the second one is a non-single-ton object.

Adding Properties to an Object


 You can add more properties to an object after its creation.
 Example:
tinderUser.id = 'xyz987';
tinderUser.name = 'Sammy';
tinderUser.isLoggedIn = true;
console.log(tinderUser);

Nested Objects
 You can have objects inside other objects.
 Example:
let regularUser = {
email: '[email protected]',
fullName: {
firstName: 'Hitesh',
lastName: 'Gupta'
}
};
console.log(regularUser);
console.log(regularUser.fullName.firstName); // Accessing nested
property
 Explanation:
o fullName is an object within regularUser.
o You can access nested properties using the dot
notation.

Accessing Nested Object Properties


 Dot Notation: You can access properties like this:
console.log(regularUser.fullName.firstName);
console.log(regularUser.fullName.lastName);
 Optional Chaining:
o Useful when accessing properties that may not exist.
o Example:
let fullName = regularUser.fullName?.firstName;
console.log(fullName); // Returns undefined if `fullName` doesn't
exist.
 Why Use Optional Chaining?
o It prevents errors when trying to access properties
that may not exist in the object.

Combining Objects
 You can combine objects in JavaScript using different
techniques:
1. Merging Objects:
o Example:
let objectOne = { a: 'value1' };
let objectTwo = { b: 'value2' };
let mergedObject = { ...objectOne, ...objectTwo };
console.log(mergedObject); // Output: { a: 'value1', b: 'value2' }
o Here, we use the spread operator (...) to merge two
objects.
2. Assigning Object Values:
o Example:
let objectThree = Object.assign({}, objectOne, objectTwo);
console.log(objectThree); // Output: { a: 'value1', b: 'value2' }

Important Takeaways
 Single-Ton: Objects: Directly declared with object literals.
 Constructor Objects: Created using new Object().
 Nested Objects: Objects can be nested inside each other.
 Accessing Properties: Use dot notation to access object
properties.
 Optional Chaining: Safely access properties that may not
exist.

🧠 What is Destructuring?
Destructuring is a syntax in JavaScript that allows you to extract values from
objects and assign them to variables easily.
💡 Very useful in React when you're dealing with props and state objects.

✅ Traditional Way of Accessing Object Values


const course = {
courseName: "JS in Hindi",
price: 999,
instructor: "Hitesh"
};
console.log(course.courseName);
console.log(course.price);
console.log(course.instructor);
 Works fine, but gets repetitive if you access the same values multiple
times.

✨ Object Destructuring Syntax


const { courseName, price, instructor } = course;

console.log(courseName); // "JS in Hindi"


console.log(price); // 999
console.log(instructor); // "Hitesh"
 Extracts values from the object directly into variables.
 Code becomes cleaner and more readable.

🎯 Rename While Destructuring


If you want to rename the variables while destructuring:
const { instructor: teacher } = course;

console.log(teacher); // "Hitesh"
 Here, instructor is assigned to a new variable teacher.

💬 Why Use Destructuring?


 Keeps your code DRY (Don’t Repeat Yourself).
 Especially helpful when the same value is accessed multiple times.
💻 Real Example in React
In React components, you often get props like:
function Navbar(props) {
console.log(props.company);
}
Instead of writing props.company again and again, you can destructure it:
function Navbar({ company }) {
console.log(company);
}
🔄 This helps avoid repeating props. prefix for each prop.

🔹 APIs (Application Programming Interfaces)


🍔 Simple Explanation
Think of an API like a restaurant menu:
 You tell the waiter what you want.
 You don’t care how it’s made in the kitchen.
 You just get the final dish.
Similarly, APIs let you request data or perform actions without worrying about
how it's done behind the scenes.

🌐 Example: Google Login


When you click "Login with Google":
 You're not checking how Google verifies you.
 You're using their API to get verified info securely.

📦 Data Format
Earlier APIs used XML, but now most APIs send data in JSON format, which is:
json
{
"name": "Hitesh",
"age": 30
}
 Easier to read and work with in JavaScript.

🔹 JavaScript Functions: Basic Concepts

✅ What is a Function?
 A function is like a reusable package of code.
 Instead of writing the same 10-20 lines of code again and again, we can
wrap them in a function and call it wherever needed.
 Think of it like:
📦 Package your logic once → Use it many times

🔹 How to Define a Function?


🧠 Syntax:
function functionName() {
// function definition (what the function will do)
}
🔸 Example:
function sayHello() {
console.log("Hello!");
}
Here:
 function → keyword
 sayHello → function name
 () → parenthesis (used to define parameters, if needed)
 {} → scope/block of the function

🔹 How to Call a Function?


You simply call the function using its name followed by ():
sayHello(); // Output: Hello!

🔹 Function with Parameters and Arguments


🧠 Parameters vs Arguments:
Term When used?
Parameters In function definition (a, b)
Arguments In function call (3, 4)
🔸 Example:
function addTwoNumbers(num1, num2) {
console.log(num1 + num2);
}

addTwoNumbers(3, 4); // Output: 7

🔹 Returning Values from Functions


If you want to return the result instead of just printing it:
function addTwoNumbers(num1, num2) {
return num1 + num2;
}
const result = addTwoNumbers(3, 4);
console.log(result); // Output: 7

⚠️Important Notes:
🚫 If you only write function name without ()
addTwoNumbers;
This gives only the reference to the function, it doesn't run the function.

⚠️Type Conversion (Common Error)


addTwoNumbers("3", "4"); // Output: 34 (because they're strings!)
 JavaScript automatically converts types if you’re not careful.
 It treats "3" and "4" as strings and concatenates them.
💡 Better Practice: Validate inputs using typeof
if (typeof num1 === "number" && typeof num2 === "number") {
return num1 + num2;
}

✅ Storing Function Output in Variables


const result = addTwoNumbers(3, 5);
console.log(result); // Output: 8
 You can store the returned value of a function into a variable.
 This is useful for further use or manipulation.

🧠 Summary Cheat Sheet:


Concept Example
Function Definition function greet() { console.log("Hi"); }
Function Call greet();
Concept Example

Function with Parameters function add(a, b) { return a + b; }


Calling with Arguments add(2, 3);
Storing Result const sum = add(2, 3);
Type Check (optional) typeof a === "number"

🛒 Real-World Use Case: Shopping Cart Example


In e-commerce apps, users may add an unknown number of items to a cart. We
need a function that can handle multiple values without knowing in advance
how many arguments will be passed.

✅ Basic Function Example


function calculatePrice(num1, num2) {
return num1 + num2;
}

console.log(calculatePrice(100, 200)); // Output: 300


But what if the user adds 3 or more values?
console.log(calculatePrice(200, 400, 500));
// Only the first two values are used, extra values are ignored.

📦 Handling Unknown Number of Arguments — Rest Operator


We use the rest operator (...) to collect all arguments into a single array.
function calculateTotal(...prices) {
console.log(prices); // [200, 400, 500]
}
calculateTotal(200, 400, 500);
📝 The ...prices packs all incoming arguments into a single array.

💡 Rest vs Spread
 ... is called a rest operator when used in function parameters.
 It is called a spread operator when used to expand arrays/objects.

🧮 You can sum all items using a loop (to be learned later)
function calculateTotal(...prices) {
let sum = 0;
for (let price of prices) {
sum += price;
}
return sum;
}

console.log(calculateTotal(100, 200, 300)); // Output: 600

📌 Mixing Regular & Rest Parameters


function calculate(val1, val2, ...others) {
console.log("val1:", val1); // 100
console.log("val2:", val2); // 200
console.log("others:", others); // [300, 400]
}

calculate(100, 200, 300, 400);


✅ val1 and val2 take first two values
✅ ...others collects the remaining into an array

🧑‍💻 Passing and Handling Objects in Functions


Let's say we want to pass a user object to a function:
const user = {
username: "Hitesh",
price: 199
};

function handleObject(anyObject) {
console.log(`Username is ${anyObject.username} and price is $
{anyObject.price}`);
}

handleObject(user);

⚠️Common Error Example


If you pass a wrong key or miswrite the object:
const user = {
username: "Hitesh",
price: 199
};

function handleObject(obj) {
console.log(`Username is ${obj.username} and price is ${obj.prices}`);
}
handleObject(user);
// Output: Username is Hitesh and price is undefined ❌

✅ Best Practices
1. Use meaningful function names (calculateTotal, not abc)
2. Always check if the keys exist in the object.
3. Use type checks when possible.

💬 Type Checking (For safety)


function handleObject(obj) {
if (typeof obj === "object" && obj.username && obj.price) {
console.log(`Username is ${obj.username} and price is ${obj.price}`);
} else {
console.log("Invalid object data");
}
}

📚 JavaScript Scope -
🔹 What is Scope?
 Scope refers to the accessibility of variables in different parts of your
code.
 It tells where a variable is available or accessible.

🔹 Types of Scope
1. Global Scope
o Variables declared outside any block or function.
o Accessible everywhere in the code.
let a = 10; // global
console.log(a); // Accessible
2. Block Scope { }
o Created using curly braces ({}) in if, for, function, etc.
o Variables declared with let and const inside are only accessible
within that block.
if (true) {
let x = 20;
console.log(x); // Accessible here
}
console.log(x); // ❌ Error: x is not defined
3. Function Scope
o Variables declared inside a function using let, const, or var are
accessible only inside that function.

🔹 var vs let/const in Scope


⚠️Problem with var:
 var is function scoped, not block scoped.
 Variables declared with var leak outside blocks, which may cause bugs
when multiple developers work together.
if (true) {
var z = 30;
}
console.log(z); // ✅ Outputs 30 (leaks outside block)
This is why developers avoid using var and prefer let or const.
🔹 Real-World Example of Problem
Suppose 10 programmers are working together and they all use var for a
variable c.
if (true) {
var c = 30;
}
console.log(c); // 30 — Even outside block!
Now someone else sets c = 300 globally, but it keeps getting overridden.
They'll be confused why the value is still 30 — this creates unpredictable bugs.

✅ Solution: Use let or const


if (true) {
let c = 30;
}
console.log(c); // ❌ Error: c is not defined
This prevents variable leakage and makes code safe & predictable.

🧠 Summary
 Use let and const for safer block-level scoping.
 Avoid var unless you're aware of its implications.
 Always remember:
o Global Scope: Accessible everywhere.
o Block Scope: Accessible only inside { }.
o Function Scope: Accessible only inside a function.

🔹 JavaScript Scope, Nested Functions & Closures


📌 What is Scope?
 Scope refers to the area where variables are accessible.
 In JavaScript, anything inside curly braces {} forms a new block scope.
 Types of scope:
o Global Scope – Accessible everywhere.
o Block Scope – Defined using {} in functions, loops, if-else etc.
o Function Scope – Variables declared inside a function.

🧠 Scope Review with Example


function one() {
let username = "Hitesh";

function two() {
let website = "YouTube";
console.log(username); // ✅ Accessible
}

two();
// console.log(website); ❌ Error: website is not defined
}
one();
✅ Explanation:
 username is declared inside function one, so it's available to function
two (child function).
 website is declared inside function two, so it is not accessible outside of
it.
 Child functions can access parent variables.
 Parent functions cannot access child variables.
🍦 Ice Cream Analogy for Scope
"Just like younger siblings can take ice cream from older ones,
but older siblings can’t snatch it from the younger ones."
 function two() is inside function one(), so it can access variables of one().
 But one() cannot access anything declared inside two().

⚠️Common Error Example


function one() {
let username = "Hitesh";

function two() {
let website = "YouTube";
}

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


two();
}
one();
 You will get an error because website was declared inside two(), and
you're trying to access it outside.

✅ Working Example
function one() {
let username = "Hitesh";

function two() {
let website = "YouTube";
console.log(username); // ✅ Works fine
}

two();
}
one();
 Output: Hitesh
 This is a valid case, as inner functions can access outer variables.

💡 Key Concept: Execution Stack & Scope Chain


 Each time a function is called, a new execution context or call stack
frame is created.
 These contexts are pushed into the call stack.
 Inner functions form a scope chain, which gives them access to outer
scopes.

🧠 A Glimpse of Closures (Teaser)


Closures are when inner functions "remember" the variables of outer
functions, even after the outer function has finished execution.
 Example (simplified idea):
function outer() {
let name = "Kratika";
return function inner() {
console.log(name); // Accessing name after outer() is done
};
}
const myFunc = outer();
myFunc(); // Output: Kratika
 We'll study closures in detail later with DOM examples.

🔄 Summary
Topic Key Point
Defined by {} — new scope is created for loops, functions, if-
Block Scope
else etc.
Variables declared inside functions are scoped to that function
Function Scope
only.
Nested
Inner functions can access outer function variables.
Functions
Trying to access inner function variables from outside causes
Scope Errors
errors.
Execution Stack New scope/context created for every function call.
Inner function accessing variables of outer function even after
Closures (Intro)
it's done.

💡 JavaScript Arrow Functions, this Keyword & Object


Methods

🔶 1. What is a Function in JavaScript?


A function is a block of code designed to perform a particular task. Functions
can be:
 Standalone (declared normally)
 Or used inside an object (called a method)
🔷 2. Object Method Using Normal Function
let person = {
name: "kratikA",
age: 20,
sayHello: function () {
console.log("hello");
},
};
🧾 Explanation:
 person is an object.
 sayHello is a function written inside the object — so it is called a
method.
 The function simply logs "hello" to the console.
✅ Output:
person.sayHello(); // Output: hello

🔷 3. Using this Keyword Inside Object Method


let person = {
name: "kratikA",
age: 20,
sayHello: function () {
console.log(`hello, my name is ${this.name}`);
},
};
🧾 What is this?
 this refers to the current object.
 Here, inside sayHello(), this.name means person.name.
✅ Output:
person.sayHello(); // Output: hello, my name is kratikA

🔴 4. Arrow Function in Object Method (Wrong Usage)


let person = {
name: "kratikA",
age: 20,
sayHello: () => {
console.log(`hello, my name is ${this.name}`);
},
};
🧾 Why this is WRONG:
 Arrow functions do not have their own this.
 They take this from the outer lexical scope (not the object).
 So this.name becomes undefined.
🚫 Output:
person.sayHello(); // Output: hello, my name is undefined

✅ 5. Correct Way to Use this in Object Method


let person = {
name: "kratikA",
age: 20,
sayHello() {
console.log(`hello, my name is ${this.name}`);
},
};
🧾 Why this works:
 This is method shorthand — same as using function() syntax.
 It correctly binds this to the person object.
✅ Output:
person.sayHello(); // Output: hello, my name is kratikA

🧠 Key Theory Points — Summarized


Concept Normal Function Arrow Function
Has its own this? ✅ Yes ❌ No (inherits from parent scope)
Use inside objects? ✅ Best choice ❌ Don't use it for object methods
Use in ✅ Best for short functions (like
✅ Okay
callbacks/short? map)
❌ Avoid if you need object
Use for this-based ops ✅ Yes (safe with this)
reference

✨ Real-Life Analogy:
Imagine this as "I" or "me".
 In a normal function, this knows who is calling — like “I am Kratika.”
 In an arrow function, it doesn’t know who’s calling — so it says “I don’t
know who I am 😵” and gives undefined.

📌 What is IIFE (Immediately Invoked Function Expression)?


➤ Definition:
An IIFE is a function that runs as soon as it is defined.
The full form is: Immediately Invoked Function Expression.

🧠 Why use IIFE?


1. ✅ To execute code immediately without calling the function separately.
2. ✅ To avoid polluting the global scope – variables declared inside an IIFE
don't interfere with global variables.
3. ✅ Used in modules like:
o Database connections (e.g., connect DB as soon as app starts)
o Configuration files
o Initial setup code

💻 Basic Function (Normal Execution)


function chai() {
console.log("DB CONNECTED");
}
chai(); // Explicit call
✅ This works fine — but it requires an extra call to run the function.

🔄 IIFE Syntax (Immediately Invoked)


✅ Correct Syntax:
(function chai() {
console.log("DB CONNECTED");
})();
🔍 Breakdown:
 function chai() { ... } → Function Definition
 Wrapping in () → Converts it into a Function Expression
 Final () → Immediately invokes it

❌ Incorrect Syntax (without wrapping)


function chai() {
console.log("DB CONNECTED");
}();
❌ Error: JS doesn't allow invoking a function declaration like this directly.

🧼 Main Use Case: Prevent Global Scope Pollution


Example:
var name = "Kratika";

(function () {
var name = "Inside IIFE";
console.log(name); // Output: Inside IIFE
})();

console.log(name); // Output: Kratika


✅ The variable name inside IIFE doesn’t interfere with the global name.

🔁 Arrow Function IIFE


You can also write IIFE using arrow functions:
(() => {
console.log("Arrow Function DB CONNECTED");
})();
⚠️If an error occurs while using arrow function as IIFE, it’s usually because of
missing semicolon before the IIFE — especially when used after another
function block.

⚠️Common Error: Missing Semicolon


function chai() {
console.log("Something");
}
// Missing semicolon below can cause error 👇
(() => {
console.log("IIFE Running");
})();
✅ Fix:
function chai() {
console.log("Something");
};
(() => {
console.log("IIFE Running");
})();
Always terminate the previous block with a semicolon if you're writing an IIFE
right after.

🧪 IIFE with Parameters


You can pass parameters to IIFE like this:
(function(name) {
console.log(`Hello, ${name}`);
})("Kratika");
🧾 Output: Hello, Kratika

🎤 Interview Tip
Don’t just say:
“IIFE is a function that executes immediately.”
✅ Instead say:
“IIFE is a function expression that executes immediately and is commonly used
to avoid polluting the global scope by creating its own scope.”
This shows deeper understanding, which impresses interviewers.

📘 JavaScript Execution Flow (Interview Concept)


💡 This topic is essential to understand how JavaScript executes code under the
hood, even before learning if-else, loops, or functions.

🔹 What We Are Learning


 How JavaScript executes code
 Understanding Execution Context
 Visualization using the browser
 No need for complex diagrams – you’ll see how it works directly

🔹 What is an Execution Context?


Execution Context is the environment in which JavaScript code is evaluated and
executed.
It has two main phases:
1. Memory Creation Phase
2. Code Execution Phase
And two main types:
1. Global Execution Context
2. Function Execution Context

✅ 1. Global Execution Context (GEC)


 Automatically created when any JS file runs.
 This is the default context.
 It gets assigned to the special keyword: this.
console.log(this); // In browser: outputs `window` object
 In Browser:
this === window // true
 In Node.js:
this === global // false (it's an empty object `{}` by default)
So, the value of this depends on the environment (browser or Node.js).

✅ 2. Function Execution Context (FEC)


Every time a function is called, a new Execution Context is created.
function greet() {
let name = "Kratika";
console.log("Hello " + name);
}

greet(); // Creates its own execution context

🟡 (Optional) Eval Execution Context


 Created by the use of eval() function.
 Not commonly used in modern code or interviews.
 Still good to know about.
eval("var a = 5;");
console.log(a); // 5
🔥 This creates variables dynamically. Avoid using eval() in production code.

🔍 How JavaScript Executes Code Internally


Two Phases:
🔸 Phase 1: Memory Creation Phase (a.k.a. Hoisting Phase)
 JavaScript scans the code.
 Allocates memory for:
o Variables → undefined
o Functions → Entire function body
console.log(a); // undefined
var a = 10;

function sayHi() {
console.log("Hi");
}
Variables are hoisted but only initialized with undefined, while functions are
hoisted with their entire definition.

🔸 Phase 2: Execution Phase


 Code is executed line-by-line.
 Actual values are assigned to the variables.
var a = 10;
console.log(a); // 10

🧠 Memory + Execution Example:


console.log(x); // undefined (hoisted)
var x = 5;

function test() {
console.log("Inside function");
}
test(); // Inside function
Memory Phase:
 x is allocated memory with value undefined
 test is allocated with full function
Execution Phase:
 x is assigned 5
 test() executes and prints the string

🔁 Summary
Concept Description
Global Execution Context Default context created for every JS file
Function Execution
Created for each function call
Context
Memory Phase Allocates memory before execution
Execution Phase Runs the code line-by-line
Refers to different objects depending on
this keyword
environment

🧠 JavaScript Execution Context & Call Stack - Simplified Notes

🧾 What is Execution Context?


Execution Context is like the environment where JavaScript code runs. Every
time a function is called, a new execution context is created.
There are two main phases inside it:
1. Memory Creation Phase (Creation Phase)
o Variables and functions are stored in memory.
o Variables are initialized with undefined.
2. Code Execution Phase (Execution Phase)
o Code runs line-by-line.
o Values are assigned and function calls are made.

🌐 Global Execution Context (GEC)


 This is created by default when your JavaScript file runs.
 Stored in the Call Stack.
 It stays till the program ends.

📦 Call Stack – The Core Concept


The Call Stack manages execution contexts. It follows the LIFO (Last In, First
Out) rule.
 The last function that is called, will be the first to complete and be
removed from the stack.

📌 Example:
function one() {
console.log("One");
two();
}

function two() {
console.log("Two");
three();
}
function three() {
console.log("Three");
}

one();
🔁 Step-by-step Call Stack Behavior:
1. Global Context is created → Added to Call Stack.
2. one() is called → New Execution Context for one() → Added to Call Stack.
3. Inside one(), two() is called → New EC for two() → Added to Call Stack.
4. Inside two(), three() is called → New EC for three() → Added to Call
Stack.
5. three() finishes → Removed from Call Stack.
6. two() finishes → Removed.
7. one() finishes → Removed.
8. Global Context finishes last.
🔁 Call Stack Snapshot:
At peak:
| three EC |
| two EC |
| one EC |
| Global EC |
🧠 Important Rule:
Last In, First Out (LIFO)
The last context added to the stack is the first to be popped off.

Bonus: DevTools Usage


You can open Google Chrome DevTools > Sources tab to:
 Write JavaScript code.
 Add breakpoints to see line-by-line execution.
 Watch how Execution Contexts get pushed & popped on the Call Stack.

🎯 Topic: JavaScript Control Flow (if statements &


comparison operators)

✅ What is Control Flow?


Control Flow is the direction in which the code executes. Normally, JavaScript
code runs top to bottom, but with control flow statements, you can decide:
 Which parts of code should run
 Which parts should be skipped

🧠 1. if Statement - Syntax & Concept


if (condition) {
// code block (only runs if condition is true)
}
 The condition must evaluate to a boolean (true or false)
 If it's true, the code inside the {} will execute
 If it's false, the code block will be skipped
📌 Example:
if (true) {
console.log("Executed"); // This will run
}

if (false) {
console.log("Not Executed"); // This won’t run
}

🔍 2. Comparisons in JavaScript (Comparison Operators)


🔸 Basic Comparison Operators:
Operator Meaning Example (a = 2, b = 3)
== Equal (loose check) a == "2" → true
=== Equal (strict check) a === "2" → false
!= Not Equal (loose) a != 3 → true
!== Not Equal (strict) a !== "2" → true
< Less Than a < b → true
> Greater Than a > b → false
<= Less Than or Equal To a <= 2 → true
>= Greater Than or Equal To a >= 3 → false

🚨 Important Notes:
🔹 = vs == vs ===
 = is for assignment (e.g., a = 5)
 == checks only value
 === checks value and data type (strict comparison)

📌 Example:
let num = 2;
let str = "2";

console.log(num == str); // true (only value)


console.log(num === str); // false (value + type)

🔹 Negation Example:
if (3 !== 2) {
console.log("True"); // Runs because 3 is not equal to 2
}

🤯 Common Use Cases for Conditions:


 Show different content for logged in vs logged out
 Show messages based on user input
 Handle validations

Folder Setup:
 A folder named 04-control-flow
 Create file like 01.js
 Start practicing different control flow conditions
✅ 1. if Condition Basics
if (userLoggedIn && hasDebitCard) {
console.log("Allow to buy courses");
}
 && means AND: Both conditions must be true.
 Example: User must be logged in and must have a debit card to buy the
course.

✅ 2. Multiple Conditions Using || (OR)


if (loggedInFromGoogle || loggedInFromEmail) {
console.log("Allow login");
}
 || means OR: At least one condition must be true.
 Used when user can login via either Google or Email.

✅ 3. More Complex Conditions


You can check multiple values together:
if ((emailFormatCorrect && isLoggedIn && hasDebitCard) || isGuestUser) {
console.log("Allow access");
}
 Nesting and combining && and || allows powerful logic control.

🔄 switch Statement
When you have one variable (like month number) and want to match multiple
fixed cases.
let month = 3;

switch(month) {
case 1:
console.log("January");
break;
case 2:
console.log("February");
break;
case 3:
console.log("March");
break;
case 4:
console.log("April");
break;
default:
console.log("Invalid month");
}
 switch() checks the value of month.
 Each case compares it to a value.
 break is used to stop the code from "falling through".
 default works like else.
📝 Important:
 If you don’t use break, all the following cases will also run after a match.

🧪 Example Use Case:


Suppose you want to give access only if:
if (userLoggedIn && tokenAvailable) {
showLogoutButton();
} else {
showLoginButton();
}

🔹 1. Nullish Coalescing Operator (??)


📌 Syntax:
let result = value1 ?? value2;
📌 मतलब:
अगर value1 null या undefined है, तो value2 को assign किया जाएगा। वरना
value1 को ही रखा जाएगा।
📌 Example:
let val = null ?? 10;
console.log(val); // Output: 10

let val2 = 5 ?? 10;


console.log(val2); // Output: 5
✅ Use-case:
जब आप किसी variable में कोई वैल्यू assign कर रहे हो और चाहते हो
कि अगर वो null या undefined है, तभी fallback value लो।
💡 Common real-world usage:
API response से जब डेटा आए और अगर वो null या undefined हो, तो default
value assign कर देते हैं।

🔸 2. Ternary Operator (? :)
📌 Syntax:
condition ? value_if_true : value_if_false;

📌 Example:
let price = 100;
let result = price <= 80 ? "Less than 80" : "More than 80";
console.log(result); // Output: "More than 80"
✅ Use-case:
जब आपको condition के आधार पर एक छोटा decision लेना हो – if-else की
जगह एक लाइन में।

❗ Difference between ?? and ? :


Feature Nullish Coalescing (??) Ternary (? :)
Default value देना अगर null या Condition check करके
Purpose
undefined हो decision लेना
Works
Null और Undefined only Any true/false condition
with
Syntax a ?? b condition ? trueVal : falseVal

🌀 Introduction to Loops (Iterations) in JavaScript


⚠️Common Mistake:
Most beginners get stuck in pattern printing (like stars, palindromes, etc.) and
forget that real programming is about solving practical problems and building
things.

✅ What are Loops?


 Loops = Iterations
 Loop means repeating a task multiple times.
 In programming, this allows us to run a block of code repeatedly based
on a condition.
🔄 Other Names:
 Loops are also called Iterations
 And in some languages (like Python), advanced forms are called Iterators

🧱 The Basic Structure of a for loop:


for (let index = 0; index < 10; index++) {
console.log(index);
}
🔍 Explanation:
 let index = 0 → initialization (starts the loop from 0)
 index < 10 → condition (loop will run until this becomes false)
 index++ → increment (increases index by 1 after every loop)
 { ... } → block of code that runs every time the loop iterates
🔁 How it works:
1. Initialize index with 0
2. Check if index < 10 (i.e., is 0 less than 10?)
3. If true, execute the block
4. After executing, increase index by 1 (index++)
5. Go back to step 2 and repeat until the condition is false
6. Once condition becomes false, the loop stops

🧠 Important Note:
Variables declared inside the loop block (like let index) cannot be accessed
outside the block — this is due to block scope in JavaScript.

🧪 Testing the Loop:


Here’s a practical test of printing numbers from 0 to 9:
for (let index = 0; index < 10; index++) {
console.log("Index value is:", index);
}
This loop will print:
Index value is: 0
Index value is: 1
...
Index value is: 9

🔧 Common Error:
If you try something like:
for (let index = 0; index < array.length; index++) {
console.log(array[index]);
}
But forget to define array, you'll get an error:
"ReferenceError: array is not defined"
So always ensure that the array exists before using its .length.

🧭 Loop Flow Summary:


1. Initialization happens only once.
2. Condition is checked before every loop.
3. If condition is true, the loop body runs.
4. After body execution, increment/decrement step runs.
5. Goes back to condition check.
6. When condition becomes false, loop exits.

Nested Loops Explanation (JavaScript)


1. Outer Loop Concept:
o The outer loop runs once, but the inner loop executes multiple
times for each iteration of the outer loop.
o Example: An outer loop will run 1 time, but the inner loop runs 10
times each time the outer loop runs.
2. Visualizing Nested Loops:
o The outer loop starts with a value (let’s say 0) and runs the inner
loop 10 times.
o After the inner loop completes 10 executions, the outer loop
continues with the next value.
o Example: If the outer loop value is 0, the inner loop will execute 10
times (0 to 9). Then, for the next outer loop value (1), the inner
loop will again execute 10 times.
3. Inner Loop Operations:
o Inside the inner loop, operations like multiplication can be
performed. For example, printing a multiplication table.
o Example: For each iteration, the outer loop sets a value (e.g., 0, 1,
2) and the inner loop prints the table for that number (e.g., 1*1,
1*2, ..., 1*10).
4. Printing a Multiplication Table:
o The inner loop prints the multiplication of each value of the outer
loop with numbers 1 to 10.
o Example:
for (let i = 1; i <= 10; i++) {
for (let j = 1; j <= 10; j++) {
console.log(i * j);
}
}
5. Modifying the Range of the Outer Loop:
o You can change the starting value of the outer loop to control from
where to start printing the multiplication tables.
o Example: If you want to start from 1 instead of 0, just change the
starting index in the outer loop.
6. Using String Interpolation:
o You can use string interpolation to format the output.
o Example:
console.log(`${i} * ${j} = ${i * j}`);
7. Arrays in Loops:
o Arrays can also be looped through in the same way.
o Example: Loop through an array and print its values.
let heroes = ["Batman", "Superman", "Flash"];
for (let i = 0; i < heroes.length; i++) {
console.log(heroes[i]);
}
8. Important Loop Keywords: break and continue:
o break: Exits the loop early, stopping further iterations.
o continue: Skips the current iteration and moves to the next one.
o These keywords are helpful when you need to terminate or skip
certain iterations based on a condition.
9. Using the break Keyword:
o Example: If you want to stop the loop when a certain number
(e.g., 5) is encountered.
for (let i = 1; i <= 20; i++) {
if (i === 5) {
break; // Exit the loop when 5 is encountered
}
console.log(i);
}
10.Using the continue Keyword:
o Example: If you want to skip printing a specific number (e.g., skip
5).
for (let i = 1; i <= 20; i++) {
if (i === 5) {
continue; // Skip this iteration
}
console.log(i);
}
11.Key Points to Remember:
o Ensure proper handling of loop boundaries to avoid errors.
o Avoid using an index that exceeds the array's length (out-of-bound
errors).
o Using break and continue can make your loops more efficient and
tailored to specific conditions.

1. Introduction to Loops:
 Loops are essential in programming as they allow repetitive execution of
code.
 There are multiple types of loops, such as:
o For Loop: One of the most common loops.
o While Loop: Another useful loop that checks a condition before
each iteration.
o Do-While Loop: A loop that checks the condition after each
iteration

2. Why Different Types of Loops?


 Multiple loop types exist because there are various ways to repeat
actions in programming.
 Each loop type has its specific use cases based on syntax and
requirements.

3. The For Loop:


 Syntax: The for loop is typically used when the number of iterations is
known beforehand.
 In programming, there is no single "right" loop. Different loops work for
different situations, but For Loop is the most commonly preferred.

4. The While Loop:


 Syntax: In a while loop, the condition is checked first, and then the loop
executes as long as the condition remains true.
 Steps to use while loop:
1. Initialize a variable.
2. Check the condition.
3. Print output if necessary.
4. Increment or decrement the variable.
 Example:
let index = 0;
while (index <= 10) {
console.log("Value of index is: " + index);
index += 2; // Increment the index
}
 When the condition index <= 10 becomes false, the loop terminates.
5. Using while Loop for Arrays:
 Array Example: You can use the while loop to iterate through arrays.
let myArray = ["Flash", "Batman", "Superman"];
let index = 0;
while (index < myArray.length) {
console.log("Value is: " + myArray[index]);
index++; // Increment the index
}
6. Important Considerations:
 The loop's condition and increments or decrements should be handled
properly to avoid infinite loops.
 For clarity and maintainability, code readability is essential, so always
ensure that your code is easy to understand, even if it takes a bit more
space or effort.

7. The Do-While Loop:


 Difference from While Loop: In a do-while loop, the code inside the loop
is executed at least once before the condition is checked.
 Syntax:
o The condition is checked after the code runs, so the loop always
executes at least once.
 Example:
let score = 1;
do {
console.log("Score is: " + score);
score++; // Increment the score
} while (score <= 10);

8. Conclusion:
 There's no "right" loop; the best loop to use depends on your situation
and preference.
 Programming Goal: Ensure the correct output is produced, whether
using a for, while, or do-while loop.

🔁 for...of Loop in JavaScript


✅ What is for...of?
for...of is a loop used to iterate over iterable objects like Arrays, Strings, Maps,
Sets, etc.
💡 It cannot be used on plain objects ({}) directly because they are not iterable.

🔹 Syntax:
for (const item of iterable) {
// block of code
}
 item: A variable that holds the current value in the loop.
 iterable: The data structure we are looping over (Array, String, etc.).

📌 Example 1: Looping over an Array


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

for (const num of numbers) {


console.log(num);
}
🧾 Output:
1
2
3
4
5

📌 Example 2: Looping over a String


const greeting = "Hello World!";
for (const char of greeting) {
console.log(char);
}
🧾 Output:
H
e
l
l
o

W
o
r
l
d
!
⚠️You can skip or break based on a character:
if (char === " ") continue; // skip spaces
if (char === "!") break; // exit loop

Looping Over Array of Objects


Suppose we have an array of objects:
const users = [
{ name: "Alice" },
{ name: "Bob" },
{ name: "Charlie" }
];

for (const user of users) {


console.log(user.name);
}
🧾 Output:
Alice
Bob
Charlie

Working with Map


✅ What is a Map?
A Map is a data structure that holds key-value pairs and remembers the
original insertion order.

📌 Creating a Map and Setting Values


const myMap = new Map();

myMap.set("name", "Kratika");
myMap.set("age", 20);
myMap.set("country", "India");

🔁 Looping over a Map


for (const [key, value] of myMap) {
console.log(`${key}: ${value}`);
}
🧾 Output:
name: Kratika
age: 20
country: India

🆚 for...of vs for...in
for...of for...in
Iterates over values Iterates over keys/indexes
Works with iterables Works with objects and arrays
Example: Arrays, Strings, Maps, Sets Example: Objects

📚 Summary
 Use for...of when you need to access values directly.
 Perfect for Arrays, Strings, and Maps.
 It's cleaner and less error-prone compared to for or forEach loops.
 Avoid using it on plain Objects—use for...in or Object.entries() for those

🔁 Difference between Arrays and Objects (with loops)


 Arrays in JavaScript are ordered collections with index-based keys (starts
from 0).
 Objects have key-value pairs, where keys can be strings, not just
numbers.
let arr = ["apple", "banana", "cherry"];
let obj = {
fruit1: "apple",
fruit2: "banana",
fruit3: "cherry"
};

🔍 Can we use for...in loop on Map?


let myMap = new Map();
myMap.set("name", "Kratika");
myMap.set("age", 20);

// Trying for...in
for (let key in myMap) {
console.log(key); // Nothing will be printed
}
 ❌ for...in doesn't work on Map because Map is not enumerable in the
same way as an object.
 ✅ To iterate over Map, you should use:
for (let [key, value] of myMap) {
console.log(key, value);
}

✅ When to use for...in and for...of


Loop Type Works On Gives
for...in Objects, Arrays Keys
for...of Arrays, Maps Values
let arr = ["a", "b", "c"];
for (let index in arr) {
console.log(index); // 0, 1, 2
}
for (let item of arr) {
console.log(item); // a, b, c
}

🔄 forEach loop - Best for arrays


 forEach is a higher-order function.
 It accepts a callback function, which is executed for each array element.
let coding = ["JavaScript", "Python", "C++", "Ruby"];

coding.forEach(function(item) {
console.log(item);
});
🔹 With Arrow Function:
coding.forEach((item) => {
console.log(item);
});
 item here represents each element of the array one by one.
 You don’t need to write a full for loop; forEach handles it internally.

🔧 Behind the scenes: Prototypes and Built-in Methods


 Arrays have methods like .length, .forEach, .map, etc. available because
they are part of the Array prototype.
 You can explore this in browser dev tools under the prototype chain
(__proto__).

✅ JavaScript forEach() vs filter()


🔁 forEach() Method:
 forEach() is used to iterate over array items.
 It does not return any value (i.e., it always returns undefined).

Syntax:
array.forEach((item) => {
// You can use or print the item here
console.log(item);
});
 Even if you try to assign it to a variable, the result will be undefined.
const result = array.forEach((item) => {
return item;
});

console.log(result); // undefined
🔍 filter() Method:
 filter() is used to return a new array based on a condition.
 It internally uses a callback function and returns only those items for
which the callback returns true.
 Syntax:
const newArray = array.filter((item) => {
return item > 5;
});
console.log(newArray); // [6, 7, 8, 9] — if array was [1,2,...9]

💡 Common Mistake:
If you use curly braces {} in arrow functions, you must use the return keyword.
If you skip return, it will return undefined and result in an empty array.
Wrong (No return):
const result = array.filter((item) => {
item > 5;
});
console.log(result); // []

Correct (With return):


const result = array.filter((item) => {
return item > 5;
});
Or, using implicit return (without {}):
const result = array.filter(item => item > 5);
🔁 Converting forEach() to act like filter():
Even though forEach() does not return a value, you can manually push filtered
values into a new array:
const newArray = [];
array.forEach((item) => {
if (item > 5) {
newArray.push(item);
}
});
console.log(newArray); // [6, 7, 8, 9]

✅ Key Takeaways:
 Use forEach() for side-effects (like logging, modifying outside variables).
 Use filter() when you need a new array of values based on some
condition.
 Arrow function return rules: No curly braces → auto return. Curly braces
→ need explicit return.

🔄 JavaScript Array Methods: map(), filter(), and reduce()

✅ map() Method
 The map() method returns a new array by applying a function to each
element of the original array.
 It does not modify the original array.

Example 1: Multiply each number by 10


let numbers = [1, 2, 3];
let result = numbers.map((num) => num * 10);
console.log(result); // [10, 20, 30]

🔁 Chaining Methods (map() + map() + filter())


You can chain multiple map() and filter() methods to process arrays step by
step.
Example 2: Multiply each number by 10, then add 1
let numbers = [1, 2, 3];
let result = numbers
.map((num) => num * 10) // [10, 20, 30]
.map((num) => num + 1); // [11, 21, 31]

console.log(result); // [11, 21, 31]


Example 3: Filter values greater than or equal to 40
let result = [11, 21, 31, 41, 51, 61, 71].filter((num) => num >= 40);
console.log(result); // [41, 51, 61, 71]
Note: filter() only includes values that return true for the condition provided.

🔽 reduce() Method
 reduce() is used to reduce all elements of an array into a single value
(e.g., sum, product).
 It takes a callback function and an optional initial value.
Syntax:
array.reduce((accumulator, currentValue) => {
return updatedAccumulator;
}, initialValue);

Example 4: Sum of numbers using reduce()


let numbers = [1, 2, 3, 4];
let sum = numbers.reduce((acc, curr) => acc + curr, 0);

console.log(sum); // 10
 On the first iteration, acc = 0 (initial value), curr = 1 → acc + curr = 1
 On the second iteration, acc = 1, curr = 2 → 3, and so on…

📌 Key Differences:
Method Returns Purpose
map() New array Transform each element
filter() Filtered array Keep only matching elements
Method Returns Purpose

reduce() Single value Accumulate result from array

🧱 What is the DOM?


 DOM stands for Document Object Model.
 It represents the structure of your HTML document as objects.
 For example, the head, body, and every div, h1, etc., are considered
objects in this model.
 If you understand DOM well, you can:
o Manipulate webpage elements (add, remove, or update them)
o Extract data (e.g., fetch numbers from cards)
o Create new elements dynamically (like a new card with updated
price)

Setting Up DOM Learning


1. Create a New HTML File — 1.html
2. Basic structure using shortcut (! + Tab in VS Code)
3. Add a simple layout:
<div class="bg-black">
<h1 class="heading" title="DOM Learning">DOM Learning on Chai aur
Code</h1>
<p>Lorem ipsum dolor sit amet.</p>
</div>

🧪 Exploring the DOM


 Open any web page (e.g., Wikipedia).
 Right-click → Inspect → Open Console
 Type window.document to explore the document structure
o You’ll find: doctype, html, head, body, children, etc.
 This entire structure is the DOM.
 You can also access it directly with just document (no need to write
window.document).

💡 Key Points
 Understanding DOM is essential before building real projects.
 Next, we’ll also explore Events, which handle user interactions.
 Projects will come after we deeply understand these fundamentals.

📘 JavaScript DOM Learning (With Tea & Code)


✅ Why DOM is Important
 Now you are moving from printing on a black screen (console) to
manipulating the actual webpage.
 This is the foundation of real frontend development — useful for React,
backend, and other frameworks.
 You’ve already covered JavaScript basics (like loops, functions) deeply, so
now DOM will make more sense.

🧱 Basic HTML Setup


Created an HTML file named 01_dom_basics.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>DOM Learning</title>
<style>
.bg-dark {
background-color: #121212;
color: white;
}
</style>
</head>
<body class="bg-dark">
<div>
<h1 id="title" class="heading">DOM Learning on Chai and Code</h1>
<p>Lorem ipsum text here for testing</p>
</div>
</body>
</html>

🖱 How to Select Elements


📍 1. getElementById()
Select an element using its ID:
document.getElementById('title')
 Case-sensitive: getElementById → G, E, B, I are all capitalized.
 Returns a single element (since ID is unique).
 You can access its properties:
const element = document.getElementById('title');
console.log(element.id); // "title"
console.log(element.className); // "heading"
📍 2. getAttribute()
To fetch any attribute from the selected element:
element.getAttribute('id'); // "title"
element.getAttribute('class'); // "heading"
🛠 3. setAttribute()
To update or set a new attribute value:
element.setAttribute('class', 'test');
⚠️This overwrites the existing class value. Original "heading" is replaced by
"test".

🔍 Why This is Useful


 React, Angular, Svelte all auto-manage DOM, but JavaScript basics are
essential to understand how things work behind the scenes.
 DOM methods help you capture and manipulate elements manually —
gives you full control.

🔦 Tips to Remember
 In React: class becomes className to avoid conflict with the class
keyword in JS.
 getAttribute() and setAttribute() are powerful for working with any HTML
attributes dynamically.

💡 JavaScript DOM Selection & Styling – Simplified Notes


✅ 1. Selecting a Single Element using querySelector()
const turnGreen = document.querySelector("li"); // selects the first <li>
element
turnGreen.style.backgroundColor = "green"; // changes background to green
turnGreen.style.padding = "10px"; // adds padding
turnGreen.innerText = "5"; // changes the text inside the element
 querySelector() selects the first matching element.
 Once selected, you can apply styles or change text/content using:
o .style.property = value
o .innerText = value

✅ 2. Selecting Multiple Elements using querySelectorAll()


const tempList = document.querySelectorAll("li"); // selects all <li> elements
 This returns a NodeList, which is not a real array, but array-like.
 You can't use .map() directly on a NodeList.
 But you can use .forEach():
tempList.forEach(function(item) {
item.style.backgroundColor = "green"; // changes background of each <li> to
green
});

❌ Common Mistake: Trying to Apply Style on Whole NodeList


tempList.style.backgroundColor = "green"; // ❌ This will throw an error
You need to loop over each item in the NodeList individually to apply styles.

✅ 3. Why .map() doesn’t work on NodeList


 Real Arrays have .map(), .push(), etc.
 NodeLists only have .forEach().
 You can convert NodeList to Array if needed:
const tempArray = Array.from(tempList);
tempArray.map(item => {
// Now map works
});
✅ 4. Selecting Elements using getElementsByClassName()
const listItems = document.getElementsByClassName("list");
 This returns an HTMLCollection (similar to NodeList).
 Again, it is not a true array, so .map() won’t work.
 Use for or forEach after converting to array:
const listArray = Array.from(listItems);
listArray.forEach(item => {
item.style.backgroundColor = "blue";
});

🎯 Key Takeaways
Can loop using
Method Returns Can use map?
forEach?
querySelector Single Element ❌ ❌
❌ (unless
querySelectorAll NodeList ✅
converted)
❌ (until ❌ (until
getElementsByClassName HTMLCollection
converted) converted)

🔁 Bonus: Looping using forEach


tempList.forEach((item, index) => {
item.innerText = index + 1;
item.style.color = "white";
});

🧠 DOM Terminologies:
 Window Object:
The global object representing the browser window. All other objects are
its children.
 Document Object:
A child of the window object that represents the entire HTML page. We
use it to access and modify elements on the webpage.

🔍 Accessing Elements in DOM


1. Using getElementById
let element = document.getElementById("myId");
console.log(element);
 Finds an element with the specific ID.
 Returns a single element (or null if not found).

2. Using getElementsByClassName
let items = document.getElementsByClassName("myClass");
console.log(items);
 Returns a collection of elements with the given class name.
 Output is an HTMLCollection (not a pure array, but can be looped
through).

3. Using getElementsByTagName
let allDivs = document.getElementsByTagName("div");
console.log(allDivs);
 Returns all elements of the specified tag (e.g., all <div> elements).

4. Using querySelector
let single = document.querySelector(".className");
console.log(single);
 Returns the first matching element based on CSS selector (#id, .class,
tag).
 Returns null if no match is found.

5. Using querySelectorAll
let allMatches = document.querySelectorAll("p");
console.log(allMatches);
 Returns all matching elements as a NodeList (like an array).
 Can be looped using forEach.

✍️Example Code (Complete Flow)


<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="main" class="container">Hello DOM</div>
<p class="text">Paragraph 1</p>
<p class="text">Paragraph 2</p>

<script>
let byId = document.getElementById("main");
console.log(byId);

let byClass = document.getElementsByClassName("text");


console.log(byClass);

let byTag = document.getElementsByTagName("p");


console.log(byTag);

let byQuery = document.querySelector(".text");


console.log(byQuery);

let byQueryAll = document.querySelectorAll("p");


console.log(byQueryAll);
</script>
</body>
</html>

💡 Notes Summary
 Use getElementById for unique elements.
 Use getElementsByClassName or querySelectorAll to get multiple
elements.
 Use querySelector for CSS-like selector access.
 querySelectorAll gives a NodeList, getElementsByClassName gives
HTMLCollection.

🔸 Initial HTML Setup:


1. A basic HTML file is created:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM 4</title>
</head>
<body>
</body>
</html>
2. Inside <body>, an unordered list is added:
<ul class="language">
<li>JavaScript</li>
</ul>
3. Style added inside <style> tag or externally:
body {
background-color: #212127;
color: white;
}

🔸 JavaScript Function to Add Language to List:


✅ Goal: Dynamically add new <li> items to the <ul class="language">.
1. A function is defined:
function addLanguage(langName) {
const li = document.createElement('li'); // create <li> element
li.innerHTML = langName; // set its inner content
document.querySelector('.language').appendChild(li); // append to UL
}
2. Function calls:
addLanguage("Python");
addLanguage("TypeScript");
✅ After running this script, the list becomes:
html
CopyEdit
<ul class="language">
<li>JavaScript</li>
<li>Python</li>
<li>TypeScript</li>
</ul>

🔸 Concepts Covered:
Concept Explanation
createElement() Used to create a new HTML element (like <li>).
innerHTML Sets or gets the HTML content inside an element.
Adds a new child element at the end of a parent
appendChild()
element.
querySelector('.class') Selects the first element with the given class.

📘 JavaScript Events –

🔹 1. What Are Events in JavaScript?


 Definition: Events are user interactions that the browser can respond to.
 Examples of Events:
o Click
o Key press
o Mouse hover
o Form submit
o Drag and drop

🔹 2. Why Are Events Important?


 Events allow us to make websites interactive.
 Without events, pages are static — events make them dynamic.

🔹 3. Types of Event Handling


Method Description Recommended?
Inline Events Added directly in HTML using onclick, etc. ❌ No
Event Properties Assigned using JS like element.onclick = … ⚠️Okay
Best practice method using separation of
addEventListener() ✅ Yes
JS

🔹 4. Inline Event Example (NOT Recommended)


<img src="river.jpg" onclick="alert('Hello')" />
 When you click the image, an alert pops up.
🙅‍♀️Problem: HTML and JavaScript are mixed. Not clean or scalable.

🔹 5. Preferred Way – Using addEventListener()


const image = document.querySelector("#river");

image.addEventListener("click", function () {
alert("Image Clicked!");
});
✅ Benefits:
 Keeps JS separate from HTML
 Clean and manageable code
 Can add multiple event listeners

🔹 6. Behind the Scenes (How Events Work)


 JS is usually sequential: runs line by line.
 But events break that sequence: they wait for user action.
 These actions are handled by the Browser Event Engine.

🔹 7. Common Events (You’ll Use Frequently)


Event Name Triggered When...
click User clicks an element
mouseover Mouse enters an element area
keydown A key is pressed
submit A form is submitted
load Page or image fully loads

📝 Mini Summary:
 Use addEventListener() → clean, flexible, modern.
 Inline onclick → works but not scalable.
 Events let websites react to users — clicks, typing, etc.
 Browsers listen for these actions and respond using our JS functions.

🧠 JavaScript Asynchronous Code –


📌 Basic Characteristics of JavaScript:
✅ JavaScript is Synchronous by default:
 This means that code executes line by line, in order.
 One line finishes execution before the next line starts.
✅ JavaScript is Single-Threaded:
 Only one thread handles the execution.
 Compared to multi-threaded languages, it's slower.
 But JavaScript hides this limitation well using delegation and runtime
environments (like browser or Node.js).
🚫 Misconception:
 Many think asynchronous code is complicated – it's not.
 The goal of this video is to simplify asynchronous concepts.

JavaScript Execution Model:


📌 Execution Context:
 When a JavaScript program runs, an Execution Context is created.
 If unfamiliar, watch the separate video explaining:
o Memory creation phase
o Call stack
o Global Execution Context
📌 Single Thread Execution:
 JavaScript follows a single call stack (like a line).
 Every task waits for the previous one to complete.
 No statement is executed unless the one before it finishes.
console.log(1);
console.log(2);
console.log(3);
This prints:
1
2
3
Because of synchronous, single-threaded nature.

📚 Core Idea:
“Each operation waits for the previous one to complete before executing.”
This explains why JavaScript is synchronous and blocking by default – unless
we explicitly use asynchronous techniques.

📦 Memory + Call Stack Recap:


 JavaScript creates:
o Memory Heap: where variables are stored.
o Call Stack: tracks function calls and execution context.
 When a function is called, a new execution context is pushed onto the
stack, and popped when done.

🧠 What is an API? (Application Programming Interface)


An API is a bridge between two systems, allowing them to communicate and
exchange data.
 APIs are like waiters in a restaurant:
You (client) place an order → Waiter (API) → Kitchen (server) → Waiter
brings food (response) → You receive data.

🌐 APIs in Web Development


In JavaScript, we use APIs to get data from a server or send data to a server.
 This is done using:
o fetch() API
o XMLHttpRequest (older method)
o Axios (external library)

📦 What is JSON? (JavaScript Object Notation)


 JSON is a lightweight format for storing and transporting data.
 It is language-independent but follows JavaScript object syntax.
✅ JSON Format:
{
"name": "Kratika",
"age": 20,
"isStudent": true
}
Rules:
 Keys & string values must be in double quotes " "
 No comments allowed in JSON
 It is purely data, not executable code

🔁 Difference: JavaScript Object vs JSON


Feature JavaScript Object JSON
Quotes on Keys Optional Mandatory ("key")
Supports Functions ✅ Yes ❌ No
Syntax JS syntax Text format, used for data exchange
Example:
JavaScript Object:
const obj = {
name: "Kratika",
greet: function() {
console.log("Hello");
}
};
JSON:
{
"name": "Kratika"
}

🔧 Converting Between Object & JSON


👉 Convert JavaScript object to JSON:
const jsonString = JSON.stringify(obj);
👉 Convert JSON to JavaScript object:
const jsObject = JSON.parse(jsonString);

📬 Calling APIs using fetch()


fetch("https://api.example.com/data")
.then(response => response.json()) // Convert JSON string to JS object
.then(data => {
console.log(data); // Use the data
})
.catch(error => {
console.error("Error:", error);
});
Note: fetch() returns a Promise, so we handle it using .then() or async/await.

🔐 Types of API Requests


Method Purpose
GET Fetch data
POST Send data
PUT Update existing data
DELETE Delete data

📥 POST Request Example using fetch()


fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Kratika",
age: 20
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

🧩 Conclusion:
 APIs allow communication between client and server.
 JSON is the data format used in most APIs.
 JavaScript provides tools like fetch(), JSON.stringify(), and JSON.parse() to
work with APIs.

🔹 Introduction to Promises (with real-world logic)
📌 What is a Promise?
A Promise in JavaScript represents the eventual completion (or failure) of an
asynchronous operation and its resulting value.
Think of it like this:
 You ordered something online (the task).
 The delivery will happen later (asynchronous).
 You’ll either receive the item (fulfilled) or get a failure message
(rejected).
 Until then, your order is pending.
This is exactly what a Promise does. It manages tasks that take time, like
fetching data from the internet, reading a file, etc.

🧠 Why Promises?
Before fetch() and async/await, developers used raw XHR (XMLHttpRequest). It
was complex and hard to manage when chaining multiple tasks. Promises
simplified this.
📥 Use Case Example:
Let’s say you request data from a server:
fetch("https://something.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error))
.finally(() => console.log("Operation Complete"));
 .then() → Runs when the Promise is fulfilled.
 .catch() → Runs if there is an error.
 .finally() → Runs no matter what.
This is a simple Promise consumption example using fetch().
⚙️How to Create a Promise
Although in most real-world cases you consume Promises (like with fetch), you
can create your own too:
const myPromise = new Promise((resolve, reject) => {
let success = true; // simulate logic

if (success) {
resolve("Task completed!");
} else {
reject("Something went wrong.");
}
});

myPromise
.then(result => console.log(result))
.catch(error => console.log(error));

🔄 Three States of a Promise:


1. Pending – Initial state, neither fulfilled nor rejected.
2. Fulfilled – The operation completed successfully.
3. Rejected – The operation failed.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Done!");
}, 2000);
});
While it’s waiting for 2 seconds, the state is pending. Once done, it becomes
fulfilled.

📌 Common Async Tasks That Use Promises:


 Network requests (fetch, axios)
 File system access (in Node.js)
 Databases
 Timers (with custom Promises)
 Device access (e.g. camera, microphone in mobile apps)
 Cryptography or heavy calculations

🧠 Think Conceptually:
Don’t carry over your understanding of objects/classes from Java or C++.
In JavaScript, everything is a reference type – even Promises.
It may look like an object, but it’s different in behavior and usage.

🔹 Understanding .then, .catch, and .finally in Promises


✅ Handling Errors with .catch
If an error occurs inside a promise chain, you can catch it using .catch().
myPromise
.then((res) => {
// Handle resolved value
})
.catch((err) => {
// Handle error
});
🔹 It's just a syntactical approach — whether you write it inline or spread it out
is up to you.
✅ Using .finally() with Promises
.finally() is always executed regardless of whether the promise is resolved or
rejected.
myPromise
.then((res) => {
console.log("Resolved:", res);
})
.catch((err) => {
console.log("Error:", err);
})
.finally(() => {
console.log("This runs no matter what!");
});
 ✅ Used to signal that "everything that had to be done is now done."
 ✅ Useful in scenarios where you want to clean up or conclude a process
regardless of success or failure.
⚠️Note: A promise isn’t meant to run indefinitely — it has a definite execution
time. For more on this, you can refer to the documentation.

🔹 Creating a New Promise (promise5) with setTimeout


const promise5 = new Promise((resolve, reject) => {
setTimeout(() => {
let username = "JavaScript";
let password = "123";
if (username === "JavaScript" && password === "123") {
resolve("Login Success");
} else {
reject("Login Failed");
}
}, 1000);
});
 ✅ Uses arrow function for better readability.
 ✅ Delay added via setTimeout to simulate asynchronous behavior.

🔹 Misconception: Only .then() and .catch() Can Handle Promises


❌ It's a myth that Promises must only be handled with .then() and .catch().
You can also use async/await, which provides a more readable and
synchronous-like flow in handling asynchronous code.

🔹 Consuming promise5 with async/await


const consumePromise5 = async () => {
try {
const response = await promise5;
console.log("Response:", response);
} catch (error) {
console.log("Error:", error);
} finally {
console.log("Cleanup or final message");
}
};

consumePromise5();
✅ What is async/await?
 await waits for the promise to settle (resolve or reject).
 try...catch is used to handle success and error cases.
 finally runs at the end, just like in .finally() method chain.
🔹 In real-world apps (like database connections), async/await is preferred for
readability and structured error handling.

✅ Summary
Feature Use Case
.then() Handle resolved value
.catch() Handle rejected value
.finally() Runs regardless of resolution or error
async/await Cleaner syntax with try/catch/finally

Introduction to Fetch API in JavaScript (with Node.js


Context)
What is Fetch API?
 Fetch API is a modern way to make web requests.
 Earlier, making HTTP requests required complex handling using
XMLHttpRequest (XHR), which was clunky and hard to manage.
 The Fetch API is easier and returns promises, making asynchronous code
cleaner and more readable.
 It’s available in browsers but was not initially available in Node.js.
 Recently, Fetch API support has been added to Node.js, which was a big
breakthrough for developers.

Historical Background
 Before Fetch API, developers used XMLHttpRequest (XHR) for HTTP
requests.
 XHR had a complicated syntax and required manually handling states and
responses.
 Then, some libraries like jQuery provided easier methods, but these
were still limited.
 Fetch API was introduced as a successor to XMLHttpRequest.
 Fetch API provides a simpler syntax, returning promises that you can
handle using .then() and .catch().
 The problem was that Fetch API was browser-dependent and not
available in Node.js runtime because Node.js doesn’t have browser APIs
like window or document.

Why was Fetch API not in Node.js initially?


 Node.js does not have a browser environment, so APIs that depend on
browser internals were not available.
 It was tricky to implement Fetch API in Node.js because of this.
 Recently, Node.js team merged Fetch API into Node.js core, allowing
developers to use Fetch natively in Node.js (since Node.js 18+).

How Fetch API works (Basic flow)


1. When you call fetch(url), it sends a network request.
2. The promise resolves with a Response object once the request is
complete.
3. You can then extract data (like JSON) from the Response using .json()
or .text() methods.
4. If the request fails, the promise rejects, and you can catch the error.

Key Points
 Fetch API is promise-based, so you can use async/await or .then() syntax.
 You should always handle both success and failure cases (.then()
and .catch()).
 Fetch API simplifies making HTTP requests compared to older
XMLHttpRequest.
 Now, with Node.js supporting Fetch API natively, the same code can run
both in browser and server environments.

Summary
 Fetch API modernized how we make HTTP requests.
 It replaced the older XMLHttpRequest with a cleaner, promise-based
syntax.
 Native Fetch support in Node.js makes life easier for backend JavaScript
developers.
 Next, we will look into the internal working of Fetch API with diagrams
and deeper explanations.

🧩 Core Discussion: Are There Classes in JavaScript?


❓ Does JavaScript Really Have Classes?
 It's a common debate.
 Technically, JavaScript did not have classes earlier.
 Even now, under the hood, it still doesn’t function like traditional class-
based languages (like Java or C++).

⚠️Important Clarification
JavaScript is not class-based like Java or C++.
Instead, JavaScript is a prototype-based language.
✅ Then Why Do We See Classes in JS?
 ES6 (ECMAScript 2015) introduced the class keyword.
 But it's syntactic sugar over existing prototype-based behavior.
 It is made to make developers from other languages feel familiar.

🧁 Syntactic Sugar Explained:


 “Syntactic sugar” means it looks like a class, but internally it still works
with prototypes.
 The class keyword makes code cleaner and more familiar, but JavaScript
is still prototype-based.

💬 ChatGPT’s Response Used in the Video


"JavaScript has classes" — this is true only in syntax, not in its core
mechanism.
Behind the scenes, it’s still prototype chaining, not class inheritance.

🧭 Understanding Programming Paradigms


🧱 What is a Programming Paradigm?
A programming paradigm is simply a style or structure in which code is
written.
Examples:
 Procedural Programming
 Object-Oriented Programming (OOP)
 Functional Programming
JavaScript allows using all of these, but its core is prototype-based.

🔍 So, What is an Object?


An object in JS is just a collection of properties and methods.
🧱 Object = Properties + Methods
 Properties = Variables/constants inside the object
 Methods = Functions defined within the object
Example:
const person = {
name: "Kratika",
age: 20,
greet: function () {
console.log("Hello!");
}
};

📝 Summary for This Part:


Concept Key Point
JavaScript Classes Introduced in ES6, syntactic sugar
True Nature Prototype-based inheritance
Programming
OOP is a coding style, not a rule
Paradigm
Collection of properties (variables) and methods
Object in JS
(functions)

📘 JavaScript Classes and OOP -


🔸 Introduction to Object-Oriented Programming (OOP)
 OOP is a programming paradigm that structures code using objects and
classes.
 Helps organize large codebases.
 JavaScript supports OOP using prototypes and ES6 classes.

🔹 Why Use OOP?


 Makes code modular, reusable, and maintainable.
 Common terms:
o Class: A blueprint.
o Object: Instance of a class.
o Constructor: Special method for initializing new objects.
o Method: Function inside a class.
o this keyword: Refers to current object instance.

🔹 Creating a Class in JavaScript


class CreateUser {
constructor(name, age) {
this.name = name;
this.age = age;
}

talk() {
console.log(`Hello, my name is ${this.name}`);
}
}
 class CreateUser: Class name is in PascalCase by convention.
 constructor(): Auto-called when object is created.
 this.name = name: Assigns value to object property.
 talk(): Custom method accessible to all objects.

🔹 Creating Objects from a Class


const user1 = new CreateUser("Kratika", 20);
const user2 = new CreateUser("Ravi", 25);
user1.talk(); // Hello, my name is Kratika
user2.talk(); // Hello, my name is Ravi
 new keyword: Creates a new object from class.
 user1, user2: Instances of CreateUser.

🔹 Understanding the this Keyword


Inside a class:
class Test {
constructor(name) {
this.name = name;
}

show() {
console.log(this.name);
}
}

const obj = new Test("Kratika");


obj.show(); // Kratika
 this.name: Refers to the object's property.

🔹 Prototype Behind the Scenes


console.log(Object.getPrototypeOf(user1));
 Even though we use class, it’s syntactic sugar over prototypes.

🔹 Inheritance Using Classes (Extends & Super)


🔸 Example 1:
class Animal {
constructor(name) {
this.name = name;
}

speak() {
console.log(`${this.name} makes a sound.`);
}
}

class Dog extends Animal {


constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}

bark() {
console.log(`${this.name} barks!`);
}
}

const d = new Dog("Tommy", "Labrador");


d.speak(); // Tommy makes a sound.
d.bark(); // Tommy barks!
 extends: Inherit from another class.
 super(): Call parent class’s constructor.
🔹 Method Overriding in Classes
class Animal {
speak() {
console.log("Animal speaks");
}
}

class Cat extends Animal {


speak() {
console.log("Meow");
}
}

const kitty = new Cat();


kitty.speak(); // Meow
 Child class method overrides parent method.

🔹 Static Methods in Classes


class MathUtil {
static add(a, b) {
return a + b;
}
}

console.log(MathUtil.add(2, 3)); // 5
 static: Called on the class itself, not on instances.
🔹 Private Fields (ES2022)
class BankAccount {
#balance = 1000;

showBalance() {
console.log(`Balance is: ${this.#balance}`);
}
}

const acc = new BankAccount();


acc.showBalance(); // Balance is: 1000
// acc.#balance; // ❌ Error: Private field
 Fields starting with # are private.

🔹 Getters and Setters


class Person {
constructor(name) {
this._name = name;
}

get name() {
return this._name;
}

set name(newName) {
if (newName.length > 0) {
this._name = newName;
}
}
}

const p = new Person("Kratika");


console.log(p.name); // Kratika
p.name = "Riya";
console.log(p.name); // Riya
 get: Access a property like a variable.
 set: Assign value with validation logic.

🧠 Summary
Concept Description
class Blueprint for creating objects
constructor() Special method called on object creation
this Refers to current object instance
extends Inherit from parent class
super() Call parent class’s constructor
static Method called on class, not instance
# Private field syntax
get/set Define property accessors

🧠 3. What is Prototype Behavior?


 JavaScript is prototype-based.
 Every object in JS has an internal link to another object called its
prototype.
 This chain forms the prototype chain.
let newHero = ['Hulk', 'Spiderman'];
 Here, newHero is an array.
 When we inspect it in the browser console, we can see:
o Elements at index 0 and 1.
o A length property.
o A hidden property: __proto__ (or [[Prototype]]).

🔗 4. Prototype Chain Working


 If JavaScript doesn't find a property directly on the object, it goes up the
prototype chain.
 It checks:
o Parent → Grandparent → Great-grandparent...
 It stops only when it finds null.
This behavior is what enables inheritance in JavaScript.

5. How Prototype Enables Features


 The new keyword, classes, and inheritance all rely on the prototype
chain.
 Built-in methods like map(), flat(), join(), etc., are not part of the array
itself.
o They're available via Array.prototype.
If not found in the immediate object, JS "doesn’t give up" — it goes up to find
it.

🧬 6. Why This Matters


 It’s not enough to just say “JavaScript is prototype-based.”
 You should understand why and how this behavior is implemented.
 Especially important when moving from beginner to intermediate (like L2
→ L3 developer).

🔁 7. Prototype vs Prototypal Inheritance


 Prototypal inheritance means:
o Objects inherit directly from other objects.
o Not through classes like in classical OOP.
 It’s dynamic, flexible, and based on real object chains.

💬 8. Common Misconceptions
 People think:
o prototype is just a term.
o It’s only used in inheritance.
 But it’s much deeper — it affects:
o Method lookup.
o this binding.
o Object behavior during runtime.

🧪 9. Inspecting in Browser Console


1. Declare:
const newHero = ['Hulk', 'Spiderman'];
2. Expand in console.
o You’ll see elements, length, and [[Prototype]].
3. Under prototype, you’ll see inherited methods like:
o map(), indexOf(), flat(), etc.
You don’t see them directly on the array — they’re inherited through the
prototype.

⚙️10. Summary
 Prototype is not just theory, it’s JavaScript’s core mechanism.
 It helps JS objects to inherit features and methods from their prototypes.

🔁 New Keyword in JavaScript


 When we use the new keyword with a constructor function:
 An empty object is created.
 That object is linked to the constructor’s prototype.
 Inside the constructor, properties/methods are added to the object
using this.
 The constructor is called with specified arguments.
 If the constructor doesn't return a value, this (the newly created object)
is returned by default.
 function User(username, score) {
 this.username = username;
 this.score = score;
 }
 const user1 = new User("Kratika", 100);

 🧠 Important Concept:
 The new keyword may be just 3 letters, but behind the scenes, it sets up
object linkage, injects methods and properties, and handles the return
logic. It automates a lot, which is why it's considered powerful.

 🧪 Time for a More Intuitive Prototype Example
 The goal is to understand how custom functionality (like trueLength)
can be added to all string objects via the prototype system.

 🧩 Problem Statement:
 Suppose we have a string:
 let myName = "Hitesh ";
 Now this string includes extra spaces. When we do:
 console.log(myName.length); // outputs: 9
 But the actual visible characters (after trimming) are fewer.
 🔍 What we want:
 We want to create a method called trueLength() that will:
 Remove extra spaces.
 Return the length of the trimmed string.
 console.log(myName.trueLength()); // should output: 6

 🧪 Challenge:
 We want to be able to call trueLength() like this on any string, not just
one instance.
 let str1 = "Hitesh ";
 let str2 = "Kratika ";

 console.log(str1.trueLength()); // 6
 console.log(str2.trueLength()); // 7
 This is not possible by default because JavaScript strings don’t have a
trueLength method.

 ✅ Solution: Use the Prototype Chain


 To solve this, we extend the String.prototype and add our custom
method.
 ✨ Add trueLength Method to All Strings:
 String.prototype.trueLength = function () {
 return this.trim().length;
 };
 Now:
 let str = "OpenAI ";
 console.log(str.trueLength()); // 6
 🔍 Explanation:
 this refers to the current string instance.
 trim() removes whitespaces from both ends.
 length gives the actual number of characters post-trimming.

 💡 Why Use Prototype?


 Efficiency: The method is shared across all string instances, not
duplicated.
 Custom Extension: Helps mimic or add framework-like behavior.
 Foundation of JS Libraries: Libraries like Lodash or jQuery rely heavily on
such techniques.

 🦸‍♂️Bonus Fun Analogy (from the video):


 Imagine two heroes:
 js
 CopyEdit
 let hero1 = "Thor";
 let hero2 = "Spiderman";
 We can create an object to hold their powers:
 js
 CopyEdit
 let heroPowers = {
 Thor: "Thunder",
 Spiderman: "Web-slinging"
 };
 This sets the scene for applying custom behaviors to multiple
characters/objects — just like we did for strings above.

 📌 Summary:
 Concept  Meaning
 Creates an object, links it to
 new keyword
prototype, binds this, returns object
 A shared object where you can add
 prototype
common methods
 A custom method added to all
 String.prototype.trueLength()
strings to get trimmed length
 Memory-efficient, reusable,
 Why prototype?
foundation of frameworks

🔹 Why call()?
 Earlier, due to less syntactic sugar and minimal abstraction in JS
frameworks/libraries, developers had to manually use .call() and .bind()
frequently.
 Modern JS frameworks evolved, but these methods are still very relevant
to understand execution context and this behavior.

🔹 Call Stack & Execution Context Recap


 Call Stack: Stack where function calls are managed (LIFO - Last In, First
Out).
 Execution Context: Each function when called, gets its own context:
o Starts with Global Execution Context (GEC).
o Each new function call adds a new context on top of the stack.
o When a function completes, its context is popped off the stack.
Example flow:
[ Function C Execution Context ]
[ Function B Execution Context ]
[ Function A Execution Context ]
[ Global Execution Context ]

🔹 What is this?
 Refers to the current execution context's object.
 In the global scope, this refers to the global object (window in
browsers).
 Inside a function, this may vary depending on how the function is called.

🔹 Problem with Nested Functions


 Consider this scenario:
function outerFunc() {
console.log(this); // What is `this` here?

function innerFunc() {
console.log(this); // What is `this` here?
}

innerFunc();
}
outerFunc();
 this inside innerFunc() no longer refers to the same context as in
outerFunc().
 This confusion in nested functions led to the need for .call(), which lets
you explicitly set the this context.

🔹 Visual Diagram (Explained in Video)


1. Global Execution Context (GEC)
2. A function is called → New Execution Context is created.
3. Inside this function, another function is called → Another Execution
Context is created.
4. Problem: How does the innermost function know what this refers to?
o This is where .call() becomes useful.

✅ Summary
 JavaScript uses execution contexts stacked in a call stack to manage
function calls.
 The keyword this refers to the current execution context’s object.
 In nested functions, this can lose its intended reference.
 .call() helps us manually set what this should refer to, solving a long-
standing issue in JS.

🔷 JavaScript Classes (ES6)


✅ What are Classes?
 Classes in JavaScript were introduced in ES6.
 They are syntactic sugar over the existing prototype-based inheritance
system.
 Behind the scenes, prototypes and functions are still used.

🔸 Creating a Class
class User {
constructor(username, email, password) {
this.username = username;
this.email = email;
this.password = password;
}

encryptPassword() {
return `${this.password}abc`; // Just adding 'abc' to show encryption
}
}
🧠 Explanation:
 class User {}: Defines a class named User.
 constructor():
o It gets automatically called when an object is created using the
new keyword.
o It initializes class properties like username, email, and password.
 encryptPassword():
o This is a method inside the class.
o It returns a modified/encrypted version of the password.
🔹 Creating an Object (Instance) of the Class
const chai = new User("chai", "[email protected]", "123");
✅ Explanation:
 new User(...):
o This creates a new instance of the User class.
o It automatically calls the constructor() method.
 chai now has all the properties and methods of the User class.

🔍 Accessing Method
console.log(chai.encryptPassword()); // Output: "123abc"

📝 Notes:
 Use class keyword to define a class.
 Always define a constructor if you want to initialize values.
 Methods can be defined directly without the function keyword.
 Use the new keyword to create an instance.
 It’s easier and cleaner than the traditional function-based OOP structure.

🧠 React Early Days –


🔸 1. The Time Before React v1
 React was in its early beta phase, not even version 1.
 Facebook had launched React with a revolutionary idea:
o Virtual DOM
o No full page reload
o Fast, lightweight, and modern frontend technology
 Documentation was not very good at that time.
 Developers were experimenting, making errors, asking the dev team, and
learning by solving issues.

🔸 2. Real-Time Developer Struggles


 People who understood the internal working of the web (under the
hood, “under the D hood”) learned faster.
 Others struggled because:
o Resources were expensive (courses were $300+)
o No structured material or YouTube tutorials like now.
 Now we are lucky to have quality free resources available on YouTube.

🔸 3. React Without JSX – The Old HTML Setup


To understand the core working of React in early days, the video demonstrates
a setup using only HTML + JS.
📁 File Creation
 Create a file: react.html
 Add basic HTML code with a <button>
<button>Click Me</button>
 Open it in browser → You'll see the button.

🔸 4. Adding JavaScript (No React Tools Yet)


In early React versions, there were:
 No React CLI tools like create-react-app
 No JSX
 Direct DOM manipulation was used
🧪 Now Write a <script> inside HTML
You had to manually write JavaScript inside the HTML page:
<script>
// Example class structure (similar to how early React worked)
class React {
constructor() {
// Declare important variables
this.username = "";
// Other server-side setup if needed
}
}
</script>

🔸 5. What Happened Behind the Scenes


 React team had already defined some methods.
 But developers had to write their own logic for:
o Handling button clicks
o Collecting form data
o Sending data to server
Example flow:
1. User clicks a button
2. A method is triggered
3. It collects form data
4. Sends it to server (e.g., using fetch or XHR)

🔸 6. Manual Server Setup (Basic Example)


 When the server is started (e.g., via Node or backend), React used to
auto-run on:
http://localhost:3000/
 This was a typical development environment.

🔸 7. The Importance of Learning Core JavaScript


The instructor emphasizes that:
 Without knowing JavaScript basics (e.g., constructor, methods, classes),
you’ll always struggle with React.
 That’s why understanding what happens behind the scenes is
important.

📌 Key Takeaways:
Concept Meaning
A lightweight copy of real DOM used by React to optimize
Virtual DOM
rendering
React pre-v1 No JSX, minimal tools, developers used plain JS & HTML
Constructor in
Used to initialize values and set up the component
class
Server setup Used localhost:3000 during dev phase
Developer
No clear documentation, expensive courses
struggle

🧠 Interview Scenario:
 The speaker was conducting interviews for SD1–SD3 level roles where
JavaScript knowledge was important.
 A tricky question was asked:
"In JavaScript, can you change the value of Math.PI from 3.14... to something
else like 4 or 3? If yes, how? If no, why not?"
🧪 Expected Answer (Most Give):
 Most candidates say "No, it cannot be changed" because it's a constant
value.
✅ Actual Deeper Explanation:
 Math.PI cannot be changed because:
o The Math object in JavaScript is non-writable, non-enumerable,
and non-configurable for properties like PI.
o These properties are defined using property descriptors.
🧾 JavaScript Code Example:
Object.getOwnPropertyDescriptor(Math, 'PI');
Output:
{
value: 3.141592653589793,
writable: false,
enumerable: false,
configurable: false
}
➡️So even though Math is an object, and objects are mutable, this specific
property PI is locked down using these flags — hence you cannot change its
value.

🧱 Deep Dive into JavaScript Objects


🔧 Creating a Simple Object:
let myObj = {
username: "kratikadev"
};
 This is a regular object with one own property: username.
🔍 Hidden/Inherited Properties:
Even though only one property is visible (username), the object inherits many
hidden properties and methods from its prototype, like:
 constructor
 toString()
 valueOf()
 hasOwnProperty()
 isPrototypeOf()
 etc.
📦 How to Discover Hidden Properties:
Object.getOwnPropertyNames(Object.prototype)
Or,
javascript
CopyEdit
Object.getOwnPropertyDescriptors(myObj)

‍♀️Why Some Objects Can’t Be Iterated?


💡 Real-Life Case Shared in Video:
 In a project, a response looked like a regular object, but it couldn’t be
iterated with for...in or Object.keys().
🔍 Root Cause:
 That object’s properties had:
o enumerable: false — so they didn’t show up in iteration.
Solution:
Use:
Object.getOwnPropertyDescriptors(yourObj)
To check whether properties are enumerable or not.
📌 Conclusion & Key Takeaways:
Concept Explanation
Because its descriptors are non-writable and non-
Math.PI is immutable
configurable
Objects can hide
Due to prototype chain or non-enumerable flags
properties
Debug deeper with Use Object.getOwnPropertyDescriptor() and
descriptors Object.getOwnPropertyNames()
Interviews test "why", Understand internal behavior, not just surface-level
not just "what" features

Getters and Setters in JavaScript Classes


1. Introduction
 Purpose: Classes ke andar properties ko control karne ke liye.
 Aksar humein direct properties ko access ya modify nahi karna hota,
especially sensitive data jaise passwords.
2. What are Getters and Setters?
 Getter: Ek special method jo property ki value ko safely access karne ke
liye use hota hai.
 Setter: Ek special method jo property ki value ko set (update) karne ke
liye use hota hai.
3. Syntax in Classes
class User {
constructor(email, password) {
this.email = email;
this._password = password; // Convention: '_' prefix se private property
dikhate hain
}
// Getter for password
get password() {
return this._password;
}

// Setter for password


set password(newPassword) {
this._password = newPassword;
}
}
4. Important Points
 Getter ko call karte waqt parentheses nahi lagate — property ki tarah
access karte hain:
user.password; // Calls getter
 Setter ko value assign karte waqt bhi parentheses nahi lagate:
user.password = 'newpass123'; // Calls setter
 Agar setter define nahi kiya, to property set karne par error aayega ya
value change nahi hogi.
 Getter aur Setter methods encapsulation provide karte hain — yani data
ko secure karte hain aur control dete hain.
5. Why use Getters and Setters?
 Direct access se bachane ke liye, especially sensitive info jaise passwords.
 Property ke access par additional logic lagana (jaise validation,
encryption).
 Code ko modular aur maintainable banana.
6. Example Usage
const user = new User('[email protected]', 'mypassword');

console.log(user.password); // Access via getter

user.password = 'newpassword'; // Update via setter

console.log(user.password); // Output updated password


7. Notes
 JavaScript mein truly private properties ke liye ab # syntax bhi available
hai (ES2020+).
 Getter aur Setter class ke bahar bhi property jaisa lagta hai, isliye ye
convenient hain.

JavaScript Closures and Lexical Scoping —

1. Introduction
 This topic is essential and interesting in JavaScript.
 Closures solve many problems related to scope and variable access.
 Lexical scoping is the foundation to understand closures.
 We'll go through both concepts, read official documentation, and
implement a small example to see real-world usage.

2. What is Lexical Scoping?


 Lexical scoping means functions have access to variables defined in
their outer (parent) scopes.
 Scope is determined at creation time based on the position of the
function in the source code.
 Inner functions can access variables and functions declared in their outer
functions.
 This behavior enables closures.

3. What is a Closure?
 A closure is a combination of a function bundled together with
references to its surrounding state (the lexical environment).
 In other words, a closure gives you access to the outer function’s scope
from an inner function.
 Closures allow functions to remember and access variables from their
outer scope even after the outer function has finished executing.

4. Why Use Closures?


 Useful for data encapsulation and privacy.
 Allows creation of function factories.
 Helps in implementing modules and maintaining state in asynchronous
code.

5. Understanding with Code Example


File: closure.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>JavaScript Closure Example</title>
</head>
<body>
<script>
// Outer function
function outer() {
let name = "Mozilla";

// Inner function (Closure)


function display() {
console.log(name); // Accesses 'name' from outer function's scope
}

display(); // Calling inner function inside outer function


}

outer();
</script>
</body>
</html>

Explanation:
 outer() declares a variable name.
 Inside outer(), an inner function display() accesses name — this is lexical
scoping.
 display() has closure over name.
 When outer() is called, display() is executed and logs "Mozilla".

6. More Practical Closure Example


File: counter.js
function createCounter() {
let count = 0; // Private variable

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

const counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

Explanation:
 createCounter returns an inner function that has closure over count.
 count persists between calls, acting as a private variable.
 Every call to counter() increments and returns count.
 Demonstrates how closures help maintain state.

7. Reading Documentation and Practice


 It’s important to get comfortable reading official JavaScript
documentation (MDN).
 Always try to implement small examples yourself.
 Understanding closures will deepen your grasp on how JavaScript
functions and scopes work.
Summary

Concept Meaning Example Use Case


Lexical Functions access variables in the Inner functions access outer
Scope scope they were created in. variables.
Maintain state, data privacy,
Function + lexical environment module pattern.
Closure
bundled together.

✅ JavaScript Arrays – Beyond Basics


🔸 Introduction
 This video is not about just basics of arrays (like looping, push/pop,
access values etc.), but goes beyond the MDN-level knowledge.
 Just like we first learn "Sun rises in the east", then later understand
Earth’s rotation – similarly, this video will shift you from basic array
knowledge to the engineering-level understanding.

🔸 Recap – What You Already Know (MDN Level)


 Arrays in JS are objects (unlike static arrays in other languages).
 They can resize dynamically, hold multiple data types, and are zero-
indexed.
 You’ve used methods like:
o push(), pop(), shift(), unshift(), map(), filter(), reduce() etc.
 You’ve defined arrays like:
const myArr = [];
const anotherArr = new Array();

🔸 Now, Going Beyond the Basics


The goal is to explore how JavaScript arrays actually work internally using:
 JavaScript engines like V8
 Debugging tools
 Compiler hooks
 The idea is to explore memory behavior, optimizations, and engine-
level mechanics.

🔸 V8 Engine Overview
 V8 is the JavaScript engine used in:
o Google Chrome
o Node.js
 You can view V8’s source code on GitHub.
o Not easy to read, but you can explore it for deep learning.
 V8 has components like:
o AST (Abstract Syntax Tree): created from your JS code
o Compilers, Optimizers, Garbage Collectors
o You may also encounter tools like d8, debugging hooks, etc.

🔸 Code Demo: Going Beyond Terminal Debugging


const myArray = [];
%DebugPrint(myArray);
 This will throw an error in Node.js or VS Code terminal, because
%DebugPrint() is a V8 internal debug function, not standard JavaScript.
 These % functions only run in special debugging modes within the V8
engine or using tools like d8 (V8 shell).

🔸 Tools Used for Deep Engine Debugging:


 V8’s d8 shell: a tool used by V8 developers to test and debug low-level
features.
 %DebugPrint(): used to print internal structure of objects (like arrays).
 These functions are not part of ECMAScript but part of V8-specific
internals.
 Cannot be used in normal JS environments (browser console, Node.js,
etc.).

🔸 Real-World Relevance
 Most devs only scratch the surface by learning map, filter, etc.
 But real engineers go deeper: how does JS optimize memory, handle
hidden classes, and manage performance?
 Exploring these internals helps in:
o Writing performance-optimized code
o Understanding memory leaks
o Contributing to engines like V8

✅ Conclusion
"This isn’t just about DSA or interviews – it’s about becoming a true engineer
who knows how JS runs under the hood."

📦 Understanding Array Optimization in JavaScript (V8 Engine


Internals)
JavaScript arrays in modern engines like V8 (used in Chrome and Node.js) are
internally optimized in various ways. These optimizations help in better
performance and memory management. But small changes in how we use
arrays can downgrade that optimization. Let's understand it step by step.

🧩 1. Packed SMI Elements (Packed Small Integer)


let arr = [1, 2, 3, 4, 5];
 This array contains only integers (SMIs - Small Integers).
 It's packed, meaning no empty slots (no holes).
 This is the most optimized form of an array.
 JS engine will treat this efficiently in memory and performance.

🧪 2. Adding a Float: Packed Doubles


arr.push(6.0);
 Still packed.
 But now elements are doubles (floating point), not just small integers.
 So, it's now a Packed Double array.
 Slightly less optimized than Packed SMI, but still good.

🧬 3. Adding a String: Packed Elements (Mixed Types)


arr.push("7");
 Now the array has mixed data types (int, float, string).
 Still packed, as there are no holes.
 But type becomes Packed Elements (mixed).
 JS engine now has to treat each element more generically.

4. Introducing Holes: Holey Arrays


arr[10] = 11;
 You directly assigned a value to index 10.
 But no values were set for index 6 to 9 → holes created.
 Now it's a Holey Array.
 Optimization level drops because the memory layout is no longer
continuous.
🚫 Important Concept: No Going Back!
Once an array becomes downgraded (like from Packed SMI → Packed Doubles
or Holey), you cannot upgrade it back.
delete arr[10];
// Still holey! It won’t go back to Packed.
Even after removing the last pushed value or a hole, JS engines do not re-
optimize it back.

📚 Optimization Hierarchy
Here’s the internal optimization type order in V8:
1. Packed SMI Elements → Best
2. Packed Doubles
3. Packed Elements (Mixed)
4. Holey SMI
5. Holey Doubles
6. Holey Elements → Worst
Once you go down the ladder, you can't go back up.

💡 Why It Matters?
If you're writing performance-sensitive code (e.g., in large applications, or
during interviews for system roles), being aware of array types and structure
can help you write faster and memory-efficient JavaScript.

✅ Console Example (Optional Debugging)


console.log(arr); // See the actual values and structure
This log is only for verification. The main purpose of this lesson is
understanding the concept, not just printing everything with console.log.
📌 Summary Points
 Arrays with only integers and no holes are most optimized.
 Adding different types or skipping indices downgrades performance.
 Once downgraded, an array remains in its lower form.

You might also like