0% found this document useful (0 votes)
4 views

JavaScript_Conc

The document outlines various JavaScript interview topics including Promises, polyfills, event handling, and object manipulation techniques. It discusses concepts like closures, the event loop, and the use of 'this' in different contexts, as well as providing examples of flattening objects and arrays. Additionally, it includes code snippets to illustrate these concepts and their applications in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

JavaScript_Conc

The document outlines various JavaScript interview topics including Promises, polyfills, event handling, and object manipulation techniques. It discusses concepts like closures, the event loop, and the use of 'this' in different contexts, as well as providing examples of flattening objects and arrays. Additionally, it includes code snippets to illustrate these concepts and their applications in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 34

Promises Interview Questions:

-----------------------------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-promises-and-its-
polyfills?source=more_articles_bottom_blogs

B: Polyfills: (Done)
-------------------------
1. Polyfills - Promise AllSettled, bind(), apply() and call()
2. Execution Context, How JS is executed & CallStack, JS engine Exposed.
Google V8 architecure.

G: Debounce and throttling: (Done)


---------------------------
1. Debounce
2. Throttling
3. currying / infinite currying.

Call, Bind, apply:


-------------------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-call-bind-and-
apply-polyfills-output-based-explicit-binding

Objects:
----------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-objects-output-
based-destructuring-object

DOM Manipulation:
-----------------
https://www.freecodecamp.org/news/the-javascript-dom-manipulation-handbook/
https://www.freecodecamp.org/news/dom-manipulation-in-javascript/

Arindom Paul Notes:


--------------------

Arindam Paul
attached screenshot in Document folder

JS VM INTERNALS:
---------------
Eventloop, Async and Scopechains

what programming make interesting ?


-> programming is an abstract concept

science part -
art part - understand implications of code.

Agenda:
--------
1. JS interpretation and Memory Model
2. Compilation and Variable Hosting
3. Function Execution
4. Scope Chains and closure.
5. JavaScript Runtime and Event loop
6. Async Examples
7. Single Threaded parallel Execution.

Example Code:
--------------

1.compilation will start from line 29


2.then goes to b = 1 ; this is not variable declration then move to next
3.then its aa function which is declared in scope
and whatever is inside of y , the compilation is actually
just check for syntax and then skip over everything
and it directly move to line 54.
4. Compilation of the main code block done.
5. then move to line 56
6. after the compilation is done, immeditaiely the execuation begins.

Global Scope(Window)
a
f ------> lamba exp
---> pointing to some text object in memory
which I am calling
at this point lambda f

In second phase:
----------------
1.value 2 wil be assinged
2. now line go to b = 1;
3. JS runtime does is will ask from memory do have refrence of be
it will say no , but JS will forgive, if you are in Global scope
if you dont have reference of vairable, it will create variable for u
this happens at runtime, execuation phase not compliation time
it will directly reach to f(1) //18

once we enter f -->


local execution scopefor f()
z
d (because var dec is there)
g
e

local execution scopefor f()


again start from line 1

z becomes 1
now what about b = 3 ? , it will check in lexical or global space.

then b will be replace from 1 to 3 in above glboal scope window


and it will check in above chain of lexical env /global space

d (because var dec is there)


g
e

again in Global Scope(Window)


a 2
f
b 1 will be replace by 3
c 4

then we go inide compliationphase of g

z 1
d 6
g -------> lamba d ----> Local execuation for g() will be created ---->
e 0

e 1

//Variable Shadowing: NOte that , from g() there is not way


to access "e" of outer space f().

inside g() --> then we have d = 3* d;


then compilation will ask do u have reference of d
it will say no , then it ask f(), it say yes I have reference
of d.

d = 3 * 6 -> d becomes 18 inside local execuation scop of f()


z 1
d 18

e 1

Now everything is done

f(1) // 18

a 2
var a = 2;
b = 1; //this is not variable dec

function f(z) {
b = 3;
c = 4;
var d = 6;
e = 1;

function g() {
var e = 0;
d = 3*d;
return d;
}
return g();
var e; // variable hosting
}
f(1); //18

//2nd Scenerio
----------------
var a = 2;
b = 1; //this is not variable dec

function f(z) {
b = 3;
c = 4;
var d = 6;
e = 1;

function g() {
var e = 0;
d = 3*d;
return d;
}

return g();
var e; // variable hosting
}

//we are returning the function itself here


//we also have a global reference to function
//Object
var myG = f(1);
f(1); //18

memory allocation will be different now:


----------------------------------------

now global spope (window)


--------------------------
a
f ------------> lambda f
b
myG

//The execution for this code will be similar so lets go


//to the interesting part of it.

-------------------------------

Local execution scope for f()


-----------------------------

z 1
d 6
g ------> lambda g
e 1

Local Execution scope for g()


-----------------------------
e 0

// note: lambda g is not garbage collected


this time.

myG has reference of lambda g as well

Closure:
--------
What happens when a function is executed outside of its
orginial scope chains ?
Moreover, what happens when a variable hold on to a function
reference which get defined in such deep function
execution chain.

It is an implicit, permanent link between function


and its scope chain.

A function definition(lamba) hidden[[scope]] referecnce.


scope chain is allocated as heap memory.
It is used and copied as the "outer environment reference"
anytime the fucntion is run.

Implicit closure:
------------------
var data = "My Data!";
setTimeout(function() {
console.log(data);// print "My Data!"
},3000);

Explicit Closures:
------------------
function makeAdder(n) {
var inc = n;
var sum = 0;
return function add() {
sum = sum + inc;
return sum;
};
}
var adder3 = makeAdder(3);
console.dir(adder3);

JS runtime itself is just a callstack and heap


as it only one callstack , single threaded
and it can only execute one thing at a time
setTimeout is actually an web api,
it doesnt live inside js runtime.
it creates timer outside of JS run time.
Event loop also implemented outside of JS runtime.

function process(num) {
//function to delay

delay();
console.log(num);
//Synchronous
[1,2,3,4,5,6].forEach(function(i) {
process(i);
}};

//Asynchornous

function process(num) {
//function to delay

delay();
console.log(num);

//Synchronous
[1,2,3,4,5,6].forEach(function(i) {
setTimeout(function() {

process(i);
},o)
process(i);
});

this keyword in Event handling :


-------------------------------
2. Using an Arrow Function as an Event Handler
Arrow functions do not have their own this. Instead, they inherit this from their
lexical scope (the scope in which the function was defined).

Example:
const button = document.querySelector("#btn");

button.addEventListener("click", () => {
console.log(this); // Logs the outer scope (e.g., Window object)
});
Here, this does not refer to the button element. Instead, it refers to the this
value of the scope in which the arrow function was defined.
Why?
Arrow functions capture this from their surrounding context and do not redefine it.

3. Mixing Regular Functions and Arrow Functions


Sometimes, you might need both the this context of the event target and a specific
outer scope. You can mix regular and arrow functions.

Example:

javascript
Copy code
const obj = {
value: 42,
init() {
const button = document.querySelector("#btn");
button.addEventListener("click", function () {
console.log(this); // Logs the button element
console.log(obj.value); // Error: obj is not defined
});

button.addEventListener("click", (event) => {


console.log(this.value); // Logs 42
});
},
};

obj.init();
In the first handler (regular function), this refers to the button, and obj is not
accessible.
In the second handler (arrow function), this refers to the obj because the arrow
function captures this from the surrounding init() method.
4. Using bind to Control this
You can use bind to explicitly set the value of this.

Example:

javascript
Copy code
const obj = {
value: 42,
};

function handleClick() {
console.log(this.value);
}

const button = document.querySelector("#btn");


button.addEventListener("click", handleClick.bind(obj)); // Logs 42
Here, this inside handleClick is explicitly bound to obj.
5. Event Delegation and this
In event delegation, where one parent element listens for events on multiple child
elements, this still refers to the element that triggered the event.

Example:
--------
javascript
Copy code
const parent = document.querySelector("#parent");

parent.addEventListener("click", function (event) {


console.log(this); // Logs the parent element
console.log(event.target); // Logs the actual element clicked (child element)
});
this refers to the parent element (the element the handler is bound to).
event.target refers to the element that actually triggered the event.
6. Special Cases in Event Handlers
a. Inline Event Handlers
When using inline event handlers in HTML, this refers to the element that triggered
the event.

Example:

html
Copy code
<button onclick="console.log(this)">Click Me</button>
Logs the button element.
b. addEventListener with Class Methods
When using class methods, be cautious of this losing its context.

Example Without Binding:


javascript
Copy code
class Example {
constructor() {
this.value = 42;
this.button = document.querySelector("#btn");
}

handleClick() {
console.log(this.value);
}

attachHandler() {
this.button.addEventListener("click", this.handleClick);
}
}

const ex = new Example();


ex.attachHandler(); // Logs undefined (or throws an error)
Solution: Bind this:

javascript
Copy code
class Example {
constructor() {
this.value = 42;
this.button = document.querySelector("#btn");
this.handleClick = this.handleClick.bind(this); // Bind in the constructor
}

handleClick() {
console.log(this.value);
}

attachHandler() {
this.button.addEventListener("click", this.handleClick);
}
}

const ex = new Example();


ex.attachHandler(); // Logs 42
Summary of this in Event Handlers
Regular Functions: this refers to the element that triggered the event.
Arrow Functions: this is inherited from the surrounding lexical scope.
Explicit Binding: Use bind, call, or apply to control this.
Event Delegation: this refers to the element the event listener is attached to, but
event.target refers to the actual element clicked.

//this inside DOM elements => reference to HTMLelement


//this inside class, constructor.

2. Flattening Objects:
----------------------

Approach
We make a function called flatten object which takes input of an object and returns
an object.
Loop through the object and check the type of the current property:
If it is of type Object and it is not an Array, recursively call the function
again.
Otherwise, store the value in the result.
Return the object.
Example: In this example, we are following the above approach.

// Declare an object
let ob = {
Company: "GeeksforGeeks",
Address: "Noida",
contact: +91-999999999,
mentor: {
HTML: "GFG",
CSS: "GFG",
JavaScript: "GFG"
}
};

// Declare a flatten function that takes


// object as parameter and returns the
// flatten object
const flattenObj = (ob) => {

// The object which contains the


// final result
let result = {};

// loop through the object "ob"


for (const i in ob) {

// We check the type of the i using


// typeof() function and recursively
// call the function again
if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
const temp = flattenObj(ob[i]);
for (const j in temp) {

// Store temp in result


result[i + '.' + j] = temp[j];
}
}

// Else store ob[i] in result directly


else {
result[i] = ob[i];
}
}
return result;
};

console.log(flattenObj(ob));

Example 2:
-------------
const nestedObject = {
user: {
name: "John Doe",
address: {
city: "New York",
zip: "10001",
geo: {
lat: "40.7128",
lng: "-74.0060"
}
},
hobbies: ["reading", "traveling"]
}
};

function flattenObject(obj, prefix = "", result = {}) {


for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === "object" && obj[key] !== null && !
Array.isArray(obj[key])) {
flattenObject(obj[key], newKey, result);
} else if (Array.isArray(obj[key])) {
obj[key].forEach((value, index) => {
flattenObject(value, `${newKey}[${index}]`, result);
});
} else {
result[newKey] = obj[key];
}
}
}
return result;
}

const flattened = flattenObject(nestedObject);


console.log(flattened);

Flattened objects are often used in:

Form Handling: Flattening nested data for easier mapping to form inputs.
API Integration: Converting nested JSON for endpoints that don't accept nested
structures.
Database Storage: Storing flattened keys in NoSQL databases like MongoDB.

Example 3: Deeply Nested Array | Leetcode


------------------------------------------
CASE: 1
Input:
arr = [1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
n =0

output:
[1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]

CASE: 2
Input:
arr = [1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
n =1

output:
[1,2,3,4,5,6,7,8,[9,10,11],12,13,14,15]

CASE: 3
Input:
arr = [1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
n =2

output:
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

WE will solve this without built in method, and use recursion instead.

var flat = function (arr, n) {


//base case
if(n === 0)
{
return arr.slice();// why slice-> return shallow copy of arr
}
let flatarr = [];

for (let i = 0; i< arr.length; i++)


{
if(Array.isArray(arr[i]))
{
const nested = flat(arr[i], n-1);
flatarr.push(...nested);
}
else {
flatarr.push(arr[i]);
}
}
return flatarr;
};
console.log(flat([1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]],2));

Deep nested Object:


---------------------
https://learnersbucket.com/examples/interview/deep-flatten-object-in-javascript-1/

Example - Search any value in a deeply nested Object:


------------------------------------------------------

var nestedObject = {
data: {
info: {
stuff: {
thing: {
moreStuff: {
magicNumber: 44,
something: "foo2",
random: [4.5],
},
},
stuffValue: 36,
},
},
},
anotherData: [{ arrayval: { testval: [60] } }, 8],
};

function contains(obj, value) {


// Deep equality check for arrays
function arraysEqual(arr, value) {
if (arr.length !== value.length) return false;

for (let i = 0; i < arr.length; i++) {


if (!isEqual(arr[i], value[i])) return false;
}
return true;
}

// Deep equality check for objects


function isEqual(obj, value) {
if (typeof obj !== typeof value) return false;
if (typeof obj !== "object") return obj === value;

if (Array.isArray(obj) && Array.isArray(value)) {


return arraysEqual(obj, value);
}

const keys1 = Object.keys(obj);


const keys2 = Object.keys(value);

if (keys1.length !== keys2.length) return false;

for (const key of keys1) {


if (!keys2.includes(key) || !isEqual(obj[key], value[key])) return false;
}

return true;
}

// Base case: if obj is equal to target value (including arrays), return true
if (isEqual(obj, value)) {
return true;
}

// Base case: if obj is not an object or array, return false


if (typeof obj !== "object" || obj === null) {
return false;
}

// Recursive case for objects


for (const key in obj) {
if (contains(obj[key], value)) {
return true;
}
}

// If the value is not found in the object, return false


return false;
}

console.log(contains(nestedObject, 36)); // true


console.log(contains(nestedObject, 4)); // false
console.log(
contains(nestedObject, {
magicNumber: 44,
something: "foo2",
random: [4.5],
})
); // true
console.log(contains(nestedObject, [4.5])); // true
console.log(contains(nestedObject, [99, 42])); // false
console.log(
contains(nestedObject, { magicNumber: 73, something: "foo3" })
); // false

-----------------------------------------------------------------------------------
----------------------------------------------

G: Debounce and throttling:


--------------------------

Debouncing:
-----------
Developing search bar , auto suggestion- for ex , I searched latop
api will fetch related to it.

and if we search laptops lenevo


then more apis will be called.

Imp thing : It doesnt make api call for each key stroke events

I want to search school bags--> 2 api calls.

How can we implement this ?


debouncing come into picture

//Debouncing in html

<html>
<body>
<input type="text" onkeyup="getData()"/>
<script src="./js/index.js"></script>
</body>
</html>

//index.ts
let counter = 0;

const getData = () => {


//calls an api and get data

console.log("Fetching data..", counter++);


}
//fetching data ... 1
//fetching data ... 2
//fetching data ... 3
//fetching data ... 4

so it calls 4 time, we want once I pause typing, then api should call.

better function
---------------
//doSomeMagic is debounce only...
//replaces doSomeMagic with Debounce now
const debounce = function (fn, delay) {
return function ()
{
let timer;
let context = this,
args = arguments;

//now how to stop function calling create variable of setTimeout


//clearing setimeout as well
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
}
}
const betterFunction = debounce(getData, 3000)

//difference of time between 2 key preses is greater than 3000 then


//only fetch api
//this betterFunction will be called in html file onkeyup
<input type="text" onkeyup="betterFunction()"/>

it will make less network calls


as we start typing it will not make , as we stop it calls

scrolling

THROTTLING:
---------------
is generally used for performance optimization and rate limiting
the functional call or execution.

suppose we have button, on click of this button


onCLick function is called --> APi is called
let suppose if we click again again
| | | | | | || | || | || |

we can use throttling on onClick method

So, lets say user click first time on onclick button


function call -------> give limit of 500ms
---> so , next function call will be after 500 ms
const expensive = () => {
console.log("fdsd")
}

//window.addEventListener("resize",expensive);
now, window.addEventListener("resize",betterExpensive);
//on every scrolling function expensive will call which
is not correct.

const betterExpensive =
throttle (expensive,limit);

throttle method() implementation:


-----------------------------

const throttle = (func, limit) => {


let flag = true;
return function () {
let context = this;
args= arguments,
if(flag){
func.apply(context, args) --> i will call this function only my flag is trur
flag = false;
setTimeout(() => {
flag = true;
}, limit);
}
}
}

//Roadside discussion:
-----------------------
Question 1:
-----------
Create a button UI and add debounce as follows =>
show Button pressed <X> Times very time button is pressed.
Increase "Triggered <Y> times" count after 800ms of
debounce.

index.html:
----------
<html>
<body>
<button class="increment_btn">Increment</button>
<span class="increment_pressed">0</span>
<span class="increment_count">0</span>
<script src="script.js"></script>
</body>
</html>
script.ts
-----------

const btn = document.querySelector(".increment_btn")'


const btnPress = document.querySelector(".increment_pressed")'
const count = document.querySelector(".increment_count")'

var pressedCount = 0;
var triggerCount = 0;

const debounceCount = _.debounce(() => {


count.innerHTML = ++triggerCount;
},800);

btn.addEventListener('click',()=> {
btnPress.innerHTML = ++pressedCount;
debounceCount();
})

//you can use lodash or debouncing or custom implemnentation


loadash cdn

//Throttle:
----------
-----------
const btn = document.querySelector(".increment_btn")'
const btnPress = document.querySelector(".increment_pressed")'
const count = document.querySelector(".increment_count")'

var pressedCount = 0;
var triggerCount = 0;

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

const throttleCount = _.throttle(() => {


const now = new Date().getTime();
count.innerHTML = ++triggerCount;
},800);

btn.addEventListener('click',()=> {
btnPress.innerHTML = ++pressedCount;
throttleCount();
})

it limit the execution on clicking any function call


search throttle function from lodash

3.currying / infinite currying.


https://roadsidecoder.hashnode.dev/javascript-interview-questions-currying-output-
based-questions-partial-application-and-more

example 1:
----------
function testFunction(...args) {
const numbers = []; // Array to store all intermediate numbers

// Inner function to handle currying


function curryFunc(value) {
if (typeof value === 'string') {
// If the value is a string, perform the specified operation
if (value === 'add') {
return numbers.reduce((sum, num) => sum + num, 0); // Sum all
numbers
} else if (value === 'multiply') {
return numbers.reduce((product, num) => product * num, 1); //
Multiply all numbers
} else {
throw new Error(`Unsupported operation: ${value}`);
}
} else {
// Otherwise, collect the number and return the same function for
chaining
numbers.push(value);
return curryFunc;
}
}

// Start by processing any initial arguments


numbers.push(...args);

return curryFunc;
}

// Usage examples:
console.log(testFunction(1)(2)(3)(4)(5)(6)(7)('add')); // Outputs: 28
console.log(testFunction(1)(2)(3)('multiply')); // Outputs: 6

Currying:
------------
Currying is a technique where a function that takes multiple arguments is
transformed into a series of functions that take one argument each. It enables
partial function application and enhances code flexibility.

Example: Let's create a curried function to calculate the total price of items with
tax.

const calculateTotalPrice = (taxRate) => (price) => price + price * (taxRate /


100);

const calculateSalesTax = calculateTotalPrice(8); // 8% sales tax


const totalPrice = calculateSalesTax(100); // Price with tax
console.log(totalPrice); // 108

C: Event Loop: (Done)


------------------------
//synchronous
[1,2,3,4].forEach(function(i) {
console.log('processing sync');
delay();
});

//Asynchronous
---------------
function asyncForEach(array, cb){
array.forEach(function () {
setTimeout(cb, 0);
})
}

asyncForEach([1,2,3,4], function(i) {
console.log("processing");
delay();
})
JavaScript Runtime itself like V8 which is Runtime inside Chrome.
I have attached one screeshot in document folder.

one thread == one call stack == one thing at a time.

function foo()
{
return foo();
}
foo();

//call stack with overflow


//RangeError: Maximum call stack size exceeded

1.Refer Namaste JS notes.


2. Let’s explore how the event loop works in JavaScript using an example. The event
loop is a mechanism that manages the execution of multiple pieces of code
(callbacks, promises, etc.), ensuring a non-blocking environment in JavaScript.

Here’s a code snippet with detailed explanations:

javascript
Copy code
console.log("Start");

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

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

console.log("End");
Dry Run and Event Loop Explanation
Execution Context and Call Stack: JavaScript runs code in a single-threaded
environment. It has a call stack that processes function execution one at a time.

Web APIs and Event Queue: When asynchronous tasks like setTimeout or Promise are
encountered, they interact with Web APIs or the Promise microtask queue. These
tasks are processed based on their respective priorities.
Microtask Queue vs. Macrotask Queue:

Microtasks (e.g., Promise.then) have higher priority than macrotasks (e.g.,


setTimeout).
Microtasks are cleared before processing macrotasks.
Step-by-Step Execution
Step 1: console.log("Start");

Output: Start
console.log is synchronous, so it executes immediately.
Step 2: setTimeout(() => { console.log("Timeout callback"); }, 0);

A macrotask is scheduled in the Event Queue with a delay of 0ms. This means it will
execute as soon as the main thread is free and all microtasks are cleared.
Step 3: Promise.resolve().then(() => { console.log("Promise resolved"); });

A microtask is scheduled in the Microtask Queue. It will execute after the current
synchronous code finishes but before macrotasks.
Step 4: console.log("End");

Output: End
This is synchronous and executes immediately.
Step 5: The event loop now processes the Microtask Queue before the Event Queue.

Promise resolved is printed as the microtask in Promise.then runs before the


macrotask.
Step 6: Finally, the Event Queue is processed.

Timeout callback is printed as the macrotask from setTimeout executes.


Final Output
text
Copy code
Start
End
Promise resolved
Timeout callback
Key Points to Remember
Synchronous code runs first.
Microtasks (e.g., Promises) execute before macrotasks (e.g., setTimeout).
The Event Loop ensures the execution of tasks in the correct order based on their
priority and type.
Let me know if you’d like further examples or clarifications!

question 2:
-----------
The JavaScript event loop manages task execution in a systematic way to prioritize
and ensure smooth, non-blocking operation. The priority is resolved based on the
type of task and the queue it belongs to. JavaScript categorizes tasks into
synchronous and asynchronous, and asynchronous tasks are further divided into
microtasks and macrotasks.

1. Call Stack
The call stack is where all synchronous code is executed. The event loop starts by
processing all the synchronous code until the call stack is empty. Only then does
it move to handle asynchronous tasks.

2. Task Queues
Microtasks Queue
Microtasks are the highest-priority asynchronous tasks.
Examples: Promise.then, MutationObserver, queueMicrotask
These tasks are executed immediately after the current execution context finishes
but before any macrotasks.
Microtasks can generate additional microtasks, which are processed until the queue
is empty.
Macrotasks Queue
Macrotasks (or tasks) have a lower priority compared to microtasks.
Examples: setTimeout, setInterval, setImmediate (Node.js), I/O tasks, UI rendering
tasks
These tasks are executed after the call stack is cleared and the microtasks queue
is empty.
3. How Priority is Resolved
When the event loop decides what to execute next, it follows these steps:

Execute Synchronous Code:

Start with the main execution context (e.g., global code or function calls).
Process Microtasks:

After clearing the call stack, the event loop processes all microtasks in the
microtasks queue before checking for macrotasks.
Process Macrotasks:

Once the microtasks queue is empty, the event loop picks a task from the macrotasks
queue to execute.
Only one macrotask is picked and executed, after which the event loop returns to
process the microtasks queue again.
Repeat:

The event loop alternates between processing microtasks and macrotasks, ensuring
the microtasks queue is cleared before moving on to the next macrotask.
Example: Task Priority in Action
javascript
Copy code
console.log('Start');

setTimeout(() => {
console.log('Macrotask 1');
}, 0);

Promise.resolve().then(() => {
console.log('Microtask 1');
}).then(() => {
console.log('Microtask 2');
});

setTimeout(() => {
console.log('Macrotask 2');
}, 0);

console.log('End');
Execution Breakdown:
Synchronous Code:

console.log('Start') → Output: Start


console.log('End') → Output: End
Microtasks:
Promise.resolve().then schedules Microtask 1 and Microtask 2 in the microtasks
queue.
Macrotasks:

Two setTimeout callbacks are scheduled in the macrotasks queue (Macrotask 1 and
Macrotask 2).
Event Loop Steps:

Execute synchronous code: Start, End.


Process microtasks:
Execute Microtask 1 → Output: Microtask 1
Execute Microtask 2 → Output: Microtask 2
Process the first macrotask:
Execute Macrotask 1 → Output: Macrotask 1
Process the second macrotask:
Execute Macrotask 2 → Output: Macrotask 2
Final Output
text
Copy code
Start
End
Microtask 1
Microtask 2
Macrotask 1
Macrotask 2
Visual Representation
plaintext
Copy code
1. Call Stack 2. Microtasks Queue 3. Macrotasks Queue
[Start] [] [setTimeout 1]
[End] [Promise 1] [setTimeout 2]
[Promise 2]
The event loop continuously monitors these queues:

It ensures all microtasks are cleared before moving to the macrotasks.


If new microtasks are added during execution, they take precedence over the next
macrotask.
Key Insights
Microtasks > Macrotasks: Microtasks are always executed before macrotasks.
Nested Tasks: If a microtask enqueues another microtask, it runs immediately after
the current one.
Macrotasks in Sequence: Only one macrotask is executed before checking the
microtasks queue again.
Browser Rendering: UI rendering happens between macrotask executions, ensuring
smooth visuals.

Question : 3:
---------------
how the event loop manages asynchronous tasks and the execution order of different
tasks such as Promises and setTimeout. This concept is crucial for understanding
how JavaScript handles concurrency.

Ans:
JavaScript uses the event loop to manage asynchronous tasks and maintain
concurrency. It achieves this by handling tasks in distinct phases and prioritizing
them based on their type (e.g., Promises, setTimeout) and the queue they belong to.
Below is a detailed explanation of how the event loop manages asynchronous tasks
and determines the execution order.
Core Concepts
Call Stack:

Stores and executes function calls in a Last-In-First-Out (LIFO) manner.


Synchronous tasks are directly pushed to the call stack and executed immediately.
Asynchronous tasks (e.g., setTimeout, Promises) are offloaded to Web APIs and
handled in the background.
Task Queues:

Microtasks Queue:
Holds high-priority asynchronous tasks.
Includes Promise.then, MutationObserver, and queueMicrotask.
Microtasks are executed after the current execution context finishes and before
moving to macrotasks.
Macrotasks Queue:
Holds lower-priority asynchronous tasks.
Includes setTimeout, setInterval, setImmediate (Node.js), I/O tasks, and rendering
tasks.
Only one macrotask is executed per iteration of the event loop, after all
microtasks are processed.
Event Loop:

Monitors the call stack, microtasks queue, and macrotasks queue.


Operates in cycles:
Executes all synchronous code.
Processes all microtasks in the queue.
Executes one macrotask, then repeats the cycle.
Phases of the Event Loop
Execute Synchronous Code:

Synchronous code runs first and is placed in the call stack.


The stack is cleared before moving to asynchronous tasks.
Process Microtasks:

Microtasks (Promises, queueMicrotask) are handled next.


All microtasks are processed before moving to the macrotasks queue.
If a microtask adds another microtask, it is processed immediately.
Process Macrotasks:

The first macrotask in the queue is executed after the microtasks queue is cleared.
After the macrotask executes, the event loop checks the microtasks queue again
before processing the next macrotask.
Example: Promises vs. setTimeout
javascript
Copy code
console.log("Start");

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

Promise.resolve().then(() => {
console.log("Microtask: Promise 1");
}).then(() => {
console.log("Microtask: Promise 2");
});

console.log("End");
Execution Steps:
Synchronous Code:

console.log("Start") → Output: Start


setTimeout schedules a macrotask (not executed yet).
Promise.resolve().then schedules a microtask (Promise 1).
console.log("End") → Output: End
Microtasks Queue:

The event loop processes Promise 1 → Output: Microtask: Promise 1.


The second .then adds Promise 2 to the microtasks queue.
Promise 2 executes → Output: Microtask: Promise 2.
Macrotasks Queue:

The event loop processes the macrotask from setTimeout → Output: Macrotask:
setTimeout.
Output
text
Copy code
Start
End
Microtask: Promise 1
Microtask: Promise 2
Macrotask: setTimeout
Key Points to Understand Execution Order
Synchronous Tasks Always Execute First:

They block the main thread until completed.


Microtasks Have Higher Priority:

Microtasks like Promises are executed immediately after synchronous code, before
any macrotask.
Macrotasks Are Processed Sequentially:

Only one macrotask is executed per event loop iteration, and then the loop checks
for new microtasks.
Nested Microtasks:

A microtask can queue more microtasks, and these are executed before moving to the
next macrotask.
Visualizing the Event Loop Workflow
Call Stack:

Handles synchronous code and functions.


Microtasks Queue:

Processes tasks from Promises, queueMicrotask, etc.


Macrotasks Queue:

Processes tasks from setTimeout, setInterval, I/O, etc.


plaintext
Copy code
Execution Order:
1. Synchronous code (Call Stack)
2. Microtasks Queue (Promises, MutationObserver)
3. Macrotasks Queue (setTimeout, setInterval)
Practical Example with Nested Tasks
javascript
Copy code
console.log("Start");

setTimeout(() => {
console.log("Macrotask 1");
Promise.resolve().then(() => {
console.log("Microtask inside Macrotask 1");
});
}, 0);

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

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

console.log("End");
Execution Breakdown:
Synchronous:

Start → Output: Start


End → Output: End
Microtasks:

Microtask 1 → Output: Microtask 1


First Macrotask (Macrotask 1):

Macrotask 1 → Output: Macrotask 1


Microtask inside Macrotask 1 → Output: Microtask inside Macrotask 1
Second Macrotask (Macrotask 2):

Macrotask 2 → Output: Macrotask 2


Final Output:
text
Copy code
Start
End
Microtask 1
Macrotask 1
Microtask inside Macrotask 1
Macrotask 2
Conclusion
Concurrency in JavaScript is managed by the event loop, ensuring smooth execution
of both synchronous and asynchronous tasks.
Execution Priority:
Synchronous > Microtasks > Macrotasks.
Microtasks like Promises are always executed before macrotasks like setTimeout.
Understanding the event loop, microtasks, and macrotasks is crucial for debugging
and optimizing JavaScript code.

D: Closures & scopes: (Done)


----------------------------
Closures - Episode 10 , 11, 12 from Namaste JS.
https://roadsidecoder.hashnode.dev/closures-javascript-interview-questions
Episode 7 from Namaste JS: The Scope Chain, Scope & Lexical Environment.
Episode 9 : Block Scope & Shadowing in JS.

1. Function Scope and Lexical Environment


When a function is created in JavaScript, it is associated with a lexical
environment. This environment contains references to the variables that were in
scope when the function was defined.

A lexical environment is a data structure that stores variable bindings for a


specific execution context.
It consists of:
Environment Record: Stores the actual bindings of variables and functions.
Reference to the Outer Lexical Environment: A pointer to the parent environment,
enabling access to variables declared outside the function.
For example:

javascript
Copy code
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}

const increment = outer();


console.log(increment()); // 1
console.log(increment()); // 2
2. Retaining Variables After the Outer Function Returns
When outer is called, it creates its own lexical environment where the variable
count is defined. When inner is returned, it retains a reference to this lexical
environment instead of letting it be garbage collected.

The inner function forms a closure over the lexical environment of outer.
Even though outer has finished execution, the memory allocated for its variables
remains because the inner function still has access to it.
This behavior is enabled by the JavaScript engine's execution context model, which
ensures closures retain their parent scope's environment.

3. Under-the-Hood Mechanism
Heap Allocation for Lexical Environment:

Variables declared in a function are stored in memory (heap) as part of the


function's lexical environment.
When the outer function completes, the JavaScript engine keeps the lexical
environment alive as long as it's referenced by a closure.
Scope Chain:

Each function has access to its scope chain, which consists of its own environment
and references to outer environments.
When a variable is accessed, the engine looks up the scope chain to find it.
Garbage Collection:

If no closures reference the outer function’s variables, the lexical environment is


marked for garbage collection.
If closures exist, the environment remains until no closures reference it.
4. Practical Example
Let's dive deeper with another example:

javascript
Copy code
function createCounter() {
let count = 0;

return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
}
};
}

const counter = createCounter();


console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
Here’s what happens:

createCounter is executed:

A new lexical environment is created with count = 0.


The object with increment and decrement functions is returned.
Closures retain access to count:

Both increment and decrement share the same lexical environment of createCounter.
Memory management:

The lexical environment of createCounter isn't garbage collected because


counter.increment and counter.decrement still reference it.
5. Visual Representation
A simplified diagram of how closures work:

plaintext
Copy code
Global Scope
↳ createCounter() Lexical Environment
↳ count: 0
↳ Functions: increment, decrement
Key Points to Remember
Closures enable encapsulation: Variables in closures are private to the function,
providing a mechanism for data hiding.
Performance considerations: Closures can lead to memory leaks if not managed
carefully, as they prevent garbage collection of unused variables.
Practical usage: Closures are used in callbacks, event handlers, currying, and
factory functions.
By understanding the lexical environment and the scope chain, you can write more
effective and efficient JavaScript code.

-----------------------------------------------------------------------------------
----------------------------------------------------

E: Hoisting & Prototype: (Sunday)


---------------------------------

var, let and const:


-------------------

https://roadsidecoder.hashnode.dev/javascript-interview-questions-on-var-let-and-
const

Resume + JS coding questions


-----------------------------------------------------------------------------------
-------------------------------------------------

CRP:
------
When a web page loads in a browser, it involves multiple steps in rendering the UI.
The placement and behavior of <script> tags with async and defer attributes
influence the sequence of these steps.

Lifecycle of Web Page Rendering


HTML Parsing Begins

The browser starts parsing the HTML document sequentially, constructing the
Document Object Model (DOM).
If a <script> tag is encountered without async or defer, the browser halts HTML
parsing to download and execute the script.
External Resource Loading

Resources like images, stylesheets, and JavaScript are fetched based on their
placement in the document.
CSSOM Construction

Stylesheets are parsed, and the CSS Object Model (CSSOM) is built alongside the
DOM.
Rendering Tree Construction

The DOM and CSSOM are combined to create the rendering tree, which represents the
visual elements on the page.
Layout and Painting

The browser calculates the layout of elements and paints them on the screen.
Behavior of <script> Tags
Without async or defer

The browser halts HTML parsing to download and execute the script immediately.
This can delay the rendering of the page because parsing and rendering are paused.
html
Copy code
<script src="script.js"></script>
With async

The script is fetched asynchronously while HTML parsing continues.


Once the script is downloaded, HTML parsing is paused to execute the script.
Suitable for scripts that don’t depend on or modify the DOM immediately.
html
Copy code
<script src="script.js" async></script>
Order of execution: Scripts are executed as soon as they are available, but the
order is not guaranteed.

With defer

The script is fetched asynchronously while HTML parsing continues.


Execution is deferred until after the HTML is fully parsed.
Suitable for scripts that rely on the complete DOM.
html
Copy code
<script src="script.js" defer></script>
Order of execution: Scripts with defer are executed in the order they appear in the
document.

Key Differences Between async and defer


Feature async defer
Download Asynchronous Asynchronous
Execution Immediate (on download completion) Deferred (after HTML parsing)
Order Not guaranteed Guaranteed
Use Case Independent scripts (e.g., analytics) Scripts needing DOM (e.g.,
interactive UI)
Real-World Rendering Impact
Scripts Without async or defer: Block rendering and slow down the page.
async: Faster page rendering but potential race conditions with DOM-dependent
scripts.
defer: Balanced approach, ensuring the DOM is ready before script execution.
By carefully selecting async and defer, developers can optimize page loading and
ensure a smooth user experience.

You said:
please explain in detail with example
ChatGPT said:
ChatGPT
Let’s dive deeper into how async and defer impact the rendering lifecycle of a web
page with detailed explanations and examples.

Lifecycle Recap
When a browser processes an HTML document:

It parses HTML from top to bottom, building the DOM.


If it encounters a <script> tag:
Without async or defer: Parsing halts, the script is downloaded and executed, and
then parsing resumes.
With async: Parsing continues while the script downloads, but execution happens
immediately when the script is ready, pausing parsing.
With defer: Parsing continues, the script downloads in parallel, and execution
happens only after the DOM is fully parsed.
Example Scenarios
1. No async or defer (Default Behavior)
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example: No async or defer</title>
<script src="script1.js"></script>
<script src="script2.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Behavior:

The browser parses the HTML until it encounters script1.js.


Parsing halts. The browser downloads and executes script1.js.
After script1.js is executed, parsing resumes until script2.js.
The process repeats for script2.js.
Problems:

Blocking behavior: The browser pauses rendering while fetching and executing each
script.
Slow page load time.
2. Using async
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example: async</title>
<script src="script1.js" async></script>
<script src="script2.js" async></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Behavior:

Both script1.js and script2.js are downloaded asynchronously in parallel while the
HTML is parsed.
As soon as a script is ready, it is executed. The order of execution is not
guaranteed.
Scenario:

If script2.js finishes downloading first, it executes before script1.js.


Suitable for scripts that don’t depend on the DOM or each other (e.g., analytics).
Pros:

Non-blocking: HTML parsing continues while scripts are fetched.


Faster load times for independent scripts.
Cons:

Execution order is unpredictable, which can break interdependent scripts.


3. Using defer
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example: defer</title>
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Behavior:

Both script1.js and script2.js are downloaded asynchronously in parallel while the
HTML is parsed.
Scripts are executed in the order they appear in the document after the DOM is
fully parsed.
Scenario:

script1.js executes before script2.js regardless of download completion time.


Suitable for scripts that rely on the DOM being fully constructed or depend on each
other.
Pros:

Non-blocking: HTML parsing and script downloading occur in parallel.


Predictable execution order.
Ideal for DOM-dependent scripts.
Cons:

Scripts execute later, so they can’t run before the DOM is ready.
Visual Timeline of Events
No async or defer:
css
Copy code
[ Parse HTML ] ----> [ Download script1.js ] ----> [ Execute script1.js ] ---->
[ Parse HTML ] ...
With async:
css
Copy code
[ Parse HTML ] -- [ Download script1.js ] --> [ Execute script1.js ] --- [ Parse
HTML ] -- ...
[ Download script2.js ] --> [ Execute script2.js ]
With defer:
css
Copy code
[ Parse HTML ] -- [ Download script1.js ] [ Download script2.js ] --> [ Execute
script1.js ] --> [ Execute script2.js ]
Real-World Example
Imagine you have these files:

analytics.js (does not depend on the DOM, e.g., Google Analytics).


main.js (relies on the DOM to attach event listeners).
HTML with Optimal Script Placement:

html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Real-World Example</title>
<!-- Analytics script with async -->
<script src="analytics.js" async></script>
<!-- Main script with defer -->
<script src="main.js" defer></script>
</head>
<body>
<h1>Hello World</h1>
<button id="myButton">Click Me</button>
</body>
</html>
Explanation:

analytics.js:
Downloads asynchronously and executes as soon as it’s ready, without waiting for
the DOM.
Doesn’t depend on DOM structure, so async is appropriate.
main.js:
Downloads asynchronously but executes after the DOM is fully parsed.
Ensures DOM is ready before attaching event listeners.
Best Practices
Use async for scripts like analytics or third-party libraries that don’t interact
with the DOM.
Use defer for scripts that manipulate or rely on the DOM.
Avoid inline scripts or scripts without attributes unless necessary, as they block
rendering.
By understanding and using async and defer appropriately, you can improve the
performance and user experience of your web pages.

You said:
critical rendering path
ChatGPT said:
ChatGPT
The Critical Rendering Path (CRP) refers to the sequence of steps a browser takes
to convert HTML, CSS, and JavaScript into pixels on the user's screen. Optimizing
the CRP is crucial for improving page load performance and reducing time to the
first meaningful paint.

Steps in the Critical Rendering Path


HTML Parsing (Building the DOM)

The browser starts parsing the HTML document from top to bottom.
It constructs the Document Object Model (DOM), a tree-like representation of the
HTML structure.
CSS Parsing (Building the CSSOM)

When the browser encounters external or inline CSS, it downloads and parses these
styles to build the CSS Object Model (CSSOM).
Until the CSSOM is complete, rendering is blocked.
JavaScript Execution

If JavaScript is encountered, it is either executed immediately (blocking


rendering) or deferred/asynchronous depending on the async or defer attributes.
JavaScript may modify the DOM and CSSOM, so rendering waits until all dependent
scripts are executed.
Render Tree Construction
The browser combines the DOM and CSSOM to create the Render Tree.
The Render Tree includes only visible elements (e.g., display: none elements are
excluded).
Layout (Reflow)

The browser calculates the position and size of each element in the Render Tree
based on the viewport size and CSS rules.
This process is called layout or reflow.
Painting

The browser paints pixels to the screen based on the layout and styling
information.
Detailed Flow of the CRP
Here’s how the CRP works in a real-world scenario:

html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
<title>Critical Rendering Path Example</title>
</head>
<body>
<h1>Hello World</h1>
<p>This is a demo of the Critical Rendering Path.</p>
</body>
</html>
Step-by-Step Breakdown:
HTML Parsing Starts:

The browser starts building the DOM from the HTML.


CSS Blocks Rendering:

When the <link> tag for styles.css is encountered, the browser pauses rendering to
download and parse the stylesheet.
CSSOM is constructed.
JavaScript Deferred:

The script.js with the defer attribute is downloaded in parallel but executed only
after the DOM is fully built.
Render Tree Built:

Once the DOM and CSSOM are ready, the browser constructs the Render Tree.
Layout and Paint:

The browser calculates the positions of visible elements and paints them to the
screen.
Optimization Techniques
1. Minimize Render-Blocking Resources
CSS and JavaScript block rendering. Minimize or defer them:
Use defer or async for JavaScript.
Inline critical CSS to avoid blocking.
Minify and compress resources.
2. Critical CSS
Extract and inline only the CSS required for above-the-fold content.
Defer non-critical CSS.
3. Reduce HTML Complexity
Minimize the size and complexity of your HTML to speed up DOM construction.
4. Preload Key Resources
Use <link rel="preload"> to prioritize loading critical resources.
5. Lazy Load Non-Critical Resources
Defer loading of images, videos, and other heavy assets until they are needed.
6. Use Efficient Fonts
Optimize font loading with font-display: swap to avoid rendering delays caused by
font loading.
Visualizing the CRP
Timeline:
HTML is parsed to build the DOM.
CSS is fetched, parsed, and combined with the DOM to create the Render Tree.
JavaScript modifies the DOM/CSSOM (if necessary).
Layout and paint are completed.
Blockers:
CSS is a render-blocking resource. Rendering cannot proceed until the CSSOM is
complete.
JavaScript can block DOM construction if not deferred or asynchronous.
Critical Rendering Path Example with Optimization
Optimized HTML:

html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Inline critical CSS -->
<style>
body { font-family: Arial, sans-serif; margin: 0; }
h1 { color: darkblue; }
</style>
<!-- Preload key assets -->
<link rel="preload" href="styles.css" as="style">
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

<!-- Async JS for analytics -->


<script src="analytics.js" async></script>
<!-- Defer main JS -->
<script src="main.js" defer></script>
</head>
<body>
<h1>Hello Optimized World</h1>
<p>This is an optimized example of the Critical Rendering Path.</p>
</body>
</html>
Key Optimizations:
Inline Critical CSS: Ensures the page is styled as quickly as possible.
Preload and Async: Speeds up loading of critical resources like CSS and JavaScript.
Defer Non-Critical JavaScript: Reduces blocking of the DOM construction.
Performance Metrics Related to CRP
First Contentful Paint (FCP):

Measures when the first visible element is painted.


Largest Contentful Paint (LCP):

Measures when the largest visible content is rendered.


Time to Interactive (TTI):
Measures when the page becomes fully interactive.
Optimizing the CRP is key to improving these metrics and providing a faster and
smoother user experience.

CRP:
----
https://www.youtube.com/watch?v=Tnp3yX9Z93Q&t=12s

Let us look at the above theory with an example.

async function withAsyncAwait() {

console.log('Start')

let promise = new Promise((resolve, reject) => {


setTimeout(() => {
resolve("Hello World, after 3 seconds");
}, 10000);
});

const data = await promise;


console.log(data)

console.log("Some Synchrnous Code, also after 3 seconds");


}

withAsyncAwait();
Start is logged in the console.
when the code execution comes at the await keyword. The withAsyncAwait function is
pushed out of the call stack.
At this point, if there is anything else to execute other than the withAsyncAwait
function, that will be executed. If nothing else is present to execute, then the
Call Stack would be EMPTY.
The main JS execution thread is never blocked.
Once the promise is resolved after 10 seconds, the withAsyncAwait function is
pushed back inside the call stack and starts to execute where it left off.
So first, the value of the resolved promise is stored in variable data, and it is
logged in the console.
After that, the next console statement is run.
Without the above knowledge, it looks like the JS code is blocked. but, after
seeing how it actually works in the Call Stack will make it clear that JS code is
never blocked. The call stack is never held back.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy