JavaScript Final V1
JavaScript Final V1
Table of Content
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
2
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
3
Chapter 10: New Trends & Technologies in JavaScript (2024) ........................................................... 536
• Serverless Architecture:
o AWS Lambda, Google Cloud Functions
• Edge Computing with JavaScript:
o Deno, Cloudflare Workers
• JavaScript in IoT Development
• Artificial Intelligence and Machine Learning with JavaScript:
o TensorFlow.js and Brain.js
• WebAssembly and JavaScript Interoperability
• Progressive Web Apps (PWAs)
• Modern Frontend Tools:
o Vite, TurboRepo, Next.js for React, SvelteKit
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
4
Chapter 13: Interview Preparation: Practice Problems and Coding Questions ........................ 722
• Algorithms and Data Structures with JavaScript:
o Array and String Manipulation
o Recursion Problems
o Sorting Algorithms (Bubble, Merge, Quick Sort)
o Search Algorithms (Binary Search, Linear Search)
o Data Structures (Stacks, Queues, Trees, Graphs)
• System Design Interview Questions:
o Designing Frontend-heavy Applications
o API Design using Express and Node.js
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
5
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
6
THEORETICAL QUESTIONS
Answer:
var, let, and const are used to declare variables, but they have different behaviors in terms
of scope and mutability.
● var: Has function scope or global scope. Variables declared with var can be re-
declared and updated.
● let: Has block scope and cannot be re-declared in the same scope. It can, however, be
updated.
● const: Like let, it has block scope, but it cannot be re-assigned after initialization.
For Example:
function example() {
var x = 10;
if (true) {
var x = 20; // re-declares x
console.log(x); // Output: 20
}
console.log(x); // Output: 20
}
let y = 10;
if (true) {
let y = 20; // Creates a new y within this block
console.log(y); // Output: 20
}
console.log(y); // Output: 10
const z = 30;
// z = 40; // Error: Assignment to constant variable
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
7
Answer:
JavaScript supports seven primitive data types and one non-primitive type (object). These
types include:
For Example:
Answer:
Arithmetic operators are used to perform basic mathematical operations on numbers.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
8
For Example:
let x = 10;
let y = 3;
console.log(x + y); // Output: 13
console.log(x - y); // Output: 7
console.log(x * y); // Output: 30
console.log(x / y); // Output: 3.333...
console.log(x % y); // Output: 1
console.log(x ** y); // Output: 1000
x++;
console.log(x); // Output: 11
Answer:
The == operator compares two values for equality, with type coercion (it converts the types if
they are different). On the other hand, the === operator checks both the value and the type
for strict equality.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
9
Answer:
Both if-else and switch are used for conditional branching, but they differ in syntax and
use case.
For Example:
// Using switch
switch (day) {
case 'Monday':
console.log("Start of the week!");
break;
case 'Friday':
console.log("Almost weekend!");
break;
default:
console.log("It's a regular day.");
}
Answer:
Loops are used to execute a block of code repeatedly until a specified condition is met.
JavaScript provides several looping constructs:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
10
For Example:
// for loop
for (let i = 0; i < 3; i++) {
console.log(i); // Output: 0, 1, 2
}
// while loop
let j = 0;
while (j < 3) {
console.log(j); // Output: 0, 1, 2
j++;
}
Answer:
A function declaration is hoisted to the top of its scope, meaning it can be called before its
definition. A function expression is not hoisted and must be declared before use.
For Example:
// Function Declaration
function greet() {
console.log("Hello!");
}
greet(); // Output: Hello!
// Function Expression
const greetExpression = function() {
console.log("Hello from expression!");
};
greetExpression(); // Output: Hello from expression!
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
11
Answer:
Arrow functions provide a more concise syntax for writing functions. They have implicit this
binding, meaning they do not have their own this context and instead inherit from their
parent scope.
For Example:
Answer:
A closure is a function that retains access to its lexical scope even when the function is
executed outside of its original context.
For Example:
function outerFunction(outerVariable) {
return function innerFunction(innerVariable) {
console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
};
}
const closure = outerFunction('outside');
closure('inside'); // Output: Outer: outside, Inner: inside
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
12
Answer:
For Example:
const obj = { a: 1, b: 2 };
for (let key in obj) {
console.log(key); // Output: a, b
}
Answer:
JavaScript offers various types of functions to fit different programming scenarios:
● Named Functions: These functions have a specific name and are useful when
reusability is required.
● Anonymous Functions: Functions without names. They are often used as arguments
to other functions (e.g., callbacks).
● Arrow Functions: Introduced in ES6, they provide a shorter syntax and have no this
binding of their own, making them useful for callbacks and closures.
● IIFE (Immediately Invoked Function Expression): Executes immediately after its
definition, often used to create private scopes and avoid variable conflicts.
● Higher-Order Functions: Functions that take other functions as parameters or return
functions, enabling advanced patterns like composition.
For Example:
// Named Function
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
13
function add(a, b) {
return a + b;
}
Answer:
Both undefined and null represent an absence of value, but they differ in how they are used
and behave.
For Example:
let x;
console.log(x); // Output: undefined (x is declared but not assigned)
let y = null;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
14
function test() {}
console.log(test()); // Output: undefined (function has no return value)
Answer:
A callback function is a function passed as an argument to another function. It is commonly
used in asynchronous operations such as reading files, API requests, or handling events.
JavaScript is single-threaded, and callbacks help prevent the blocking of code execution by
executing the function after the asynchronous task is completed.
For Example:
function afterGreet() {
console.log("This message runs after the greeting.");
}
greet("Alice", afterGreet);
// Output:
// Hello, Alice
// This message runs after the greeting.
Answer:
An IIFE is a function that executes as soon as it is defined. It provides an isolated scope to
avoid polluting the global environment. This is useful when you need to create variables or
functions that should not interfere with other parts of the program.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
15
For Example:
(function() {
let counter = 0;
console.log(`Counter initialized to: ${counter}`);
})();
Here, the variable counter is not accessible outside the IIFE, preserving its privacy.
15. What is the difference between block scope, function scope, and global
scope?
Answer:
● Global Scope: Variables defined outside any function or block can be accessed from
anywhere in the program.
● Function Scope: Variables declared inside a function are accessible only within that
function.
● Block Scope: Variables declared with let or const inside a block (e.g., within {}) are
restricted to that block.
For Example:
function demoScope() {
let funcVar = "Function Scope"; // Function scope
console.log(globalVar); // Output: Global
}
// console.log(funcVar); // Error: funcVar is not defined
{
let blockVar = "Block Scope"; // Block scope
console.log(blockVar); // Output: Block Scope
}
// console.log(blockVar); // Error: blockVar is not defined
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
16
Answer:
Hoisting is a behavior where JavaScript moves declarations (but not initializations) to the top
of their scope. This applies to both variable and function declarations. However, variables
declared with let and const are not accessible before they are declared, due to the
temporal dead zone.
For Example:
Answer:
The this keyword refers to the object it belongs to. In a method, this refers to the object
calling the method. In the global context, this refers to the global object (in browsers, it is
the window object). Arrow functions inherit this from their surrounding scope.
For Example:
const person = {
name: "Alice",
greet: function() {
console.log(`Hello, ${this.name}`);
}
};
person.greet(); // Output: Hello, Alice
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
17
Answer:
Default parameters allow you to initialize function parameters with default values if no
argument is passed or if the argument is undefined. This makes the code more concise and
eliminates the need for extra checks.
For Example:
Answer:
Template literals are string literals enclosed by backticks (`) instead of quotes. They allow
embedding variables and expressions using ${}. They also support multi-line strings without
needing escape sequences.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
18
Answer:
A higher-order function is a function that takes other functions as arguments or returns
functions. They allow more modular code and are essential in functional programming,
enabling operations like map, filter, and reduce.
For Example:
Here, applyFunction takes a function as an argument and applies it to the input value.
21. What is the event loop in JavaScript, and how does it work?
Answer:
The event loop manages the execution of asynchronous code in JavaScript. Since JavaScript
is single-threaded, it can only do one thing at a time. The event loop ensures non-blocking
operations by coordinating tasks between:
The event loop constantly checks if the call stack is empty, and if so, it moves the next task
from the microtask queue (or task queue) to the call stack.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
19
For Example:
console.log('Start');
console.log('End');
// Output: Start, End, Promise, Timeout
Here, the promise runs before setTimeout because microtasks have higher priority than
tasks.
Answer:
A promise represents an asynchronous operation's eventual success or failure. It provides a
cleaner alternative to callbacks and avoids callback hell. A promise has three possible states:
For Example:
promise
.then((result) => console.log(result)) // Output: Success!
.catch((error) => console.log(error));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
20
Promises improve readability by chaining .then() and .catch() to handle success and
failure.
23. What is async/await, and how does it simplify working with Promises?
Answer:
async/await makes asynchronous code easier to read and manage. The async keyword
marks a function as asynchronous, and await pauses the execution of the function until the
promise resolves.
For Example:
fetchData();
This code avoids deeply nested .then() calls and makes the logic clearer.
Answer:
The try-catch block handles runtime errors and prevents them from crashing the program.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
21
Code in the try block is executed, and if an error occurs, it is caught by the catch block. The
finally block runs regardless of whether an error occurs, allowing you to clean up resources.
For Example:
try {
let result = 10 / 0;
console.log(result); // Output: Infinity
let obj = undefined;
console.log(obj.name); // This will throw an error
} catch (error) {
console.error('Caught an error:', error.message);
} finally {
console.log('This runs regardless of errors.');
}
// Output:
// Caught an error: Cannot read properties of undefined (reading 'name')
// This runs regardless of errors.
Answer:
Modules help organize JavaScript code by splitting it into reusable pieces. They prevent
name collisions and improve maintainability. JavaScript uses the import and export
keywords to manage modules.
For Example:
// module.js
export const greet = (name) => console.log(`Hello, ${name}!`);
// main.js
import { greet } from './module.js';
greet('Alice'); // Output: Hello, Alice!
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
22
26. What is the difference between shallow copy and deep copy in
JavaScript?
Answer:
● Shallow Copy: Copies only the top-level properties of an object, while nested objects
are copied as references.
● Deep Copy: Recursively copies all levels, ensuring no shared references between the
original and the copied object.
For Example:
const obj1 = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj1 }; // Shallow copy
const deepCopy = JSON.parse(JSON.stringify(obj1)); // Deep copy
shallowCopy.b.c = 42;
console.log(obj1.b.c); // Output: 42 (shallow copy affected original)
deepCopy.b.c = 100;
console.log(obj1.b.c); // Output: 42 (deep copy is independent)
Answer:
A closure allows a function to access variables from its outer scope, even after the outer
function has finished execution. Closures are used to create private variables or maintain
state.
For Example:
function counter() {
let count = 0;
return function () {
count++;
console.log(count);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
23
};
}
Here, the inner function retains access to count even after counter() has executed.
28. What are setTimeout and setInterval? How are they different?
Answer:
For Example:
// setTimeout example
setTimeout(() => console.log('Executed after 2 seconds'), 2000);
// setInterval example
let count = 0;
const interval = setInterval(() => {
count++;
console.log(`Count: ${count}`);
if (count === 3) clearInterval(interval); // Stops after 3 counts
}, 1000);
Answer:
● Synchronous Code: Blocks the execution until the current task completes.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
24
● Asynchronous Code: Allows other tasks to run while waiting for the current task to
complete.
For Example:
// Synchronous code
console.log('Start');
for (let i = 0; i < 1000000000; i++) {} // Blocking loop
console.log('End');
// Asynchronous code
console.log('Start');
setTimeout(() => console.log('Timeout'), 0); // Non-blocking
console.log('End');
// Output: Start, End, Timeout
30. How does the debounce function work, and when would you use it?
Answer:
Debouncing ensures a function is called only after a specified delay since its last invocation. It
is used to limit the rate of function calls, especially during frequent events (like resizing or key
presses).
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
25
}, 500);
window.addEventListener('resize', resizeHandler);
This example limits the resizeHandler call to only once every 500ms, improving
performance.
Answer:
call(), apply(), and bind() are methods used to control the value of this in JavaScript
functions.
● call(): Invokes a function with a given this value and arguments passed
individually.
● apply(): Similar to call(), but arguments are passed as an array.
● bind(): Returns a new function with this bound to the specified object, without
invoking it immediately.
For Example:
const person = {
name: "Alice",
greet: function (age) {
console.log(`Hello, my name is ${this.name} and I am ${age} years old.`);
}
};
// Using call
person.greet.call(anotherPerson, 25); // Output: Hello, my name is Bob and I am 25
years old.
// Using apply
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
26
// Using bind
const boundGreet = person.greet.bind(anotherPerson, 35);
boundGreet(); // Output: Hello, my name is Bob and I am 35 years old.
Answer:
These are higher-order array methods:
● map(): Creates a new array by transforming every element in the original array.
● filter(): Creates a new array with elements that pass a test function.
● reduce(): Reduces the array to a single value by executing a reducer function on
each element.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
27
Answer:
In JavaScript, every object has a prototype, which is another object it inherits properties from.
When accessing a property, JavaScript first looks at the object itself; if the property is not
found, it checks the prototype. This series of lookups is called the prototype chain.
For Example:
const person = {
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};
Here, alice inherits the greet method from person through the prototype chain.
Answer:
For Example:
const obj = { a: 1, b: 2 };
// Object.freeze
Object.freeze(obj);
obj.a = 10; // This will be ignored
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
28
console.log(obj.a); // Output: 1
// Object.seal
const sealedObj = { x: 1, y: 2 };
Object.seal(sealedObj);
sealedObj.x = 10; // Allowed
delete sealedObj.y; // Ignored
console.log(sealedObj); // Output: { x: 10, y: 2 }
Answer:
Generators are special functions in JavaScript that can be paused and resumed, allowing
them to produce a series of values over time. They are defined using the function* syntax
and use yield to return values.
For Example:
function* generator() {
yield 1;
yield 2;
yield 3;
}
Answer:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
29
For Example:
Answer:
Symbol is a primitive data type introduced in ES6. It creates unique and immutable values,
which are often used as object property keys to prevent name collisions.
For Example:
const obj = {
[sym1]: 'Value associated with sym1'
};
console.log(obj[sym1]); // Output: Value associated with sym1
38. What is the typeof operator, and what are its quirks?
Answer:
The typeof operator returns the data type of a value. However, it has some quirks, especially
with null.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
30
39. What is the difference between event delegation and event bubbling?
Answer:
● Event bubbling: An event starts at the target element and propagates up through its
ancestors (from child to parent).
● Event delegation: A technique where a parent element handles events for its children
by leveraging event bubbling. It improves performance by attaching fewer event
listeners.
For Example:
Answer:
● WeakMap: A collection of key-value pairs where keys must be objects. It holds "weak"
references to objects, allowing garbage collection if there are no other references.
● WeakSet: A collection of unique objects, also holding "weak" references.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
31
obj = null; // Now, obj can be garbage collected since WeakMap/WeakSet do not
prevent it.
SCENARIO QUESTIONS
41. Scenario: You need to store the configuration settings for an application
that won't change during runtime.
Question: Which variable declaration should you use: var, let, or const? Explain the
behavior with an example.
Answer:
You should use const for variables whose values should not change during runtime. This
ensures the variable is read-only and prevents reassignment. Using let or var for such cases
is not recommended since they allow reassignment, which can lead to bugs. However, note
that objects or arrays declared with const can have their contents modified, though the
reference itself cannot change.
For Example:
const CONFIG = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
32
Resulting Table:
42. Scenario: You want to create a counter that increments every time a
button is clicked and display the count.
Question: How would you implement a counter using closures to preserve the state between
button clicks?
Answer:
Using closures, we can create a function that retains access to its lexical scope, even after the
outer function has finished execution. This ensures that the state of the count variable
persists between button clicks.
For Example:
function createCounter() {
let count = 0; // Private variable inside the closure
return function () {
count++;
console.log(`Button clicked ${count} times.`);
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
33
Here, count is a private variable maintained by the closure returned from createCounter.
Each time the button is clicked, the incrementCounter function increments and logs the
count value.
43. Scenario: You are building a search input field that suggests results.
However, to avoid sending too many API requests, you need to optimize it.
Question: How would you use debounce to limit API calls during user input?
Answer:
Debouncing ensures that a function is executed only after the user has stopped triggering
the event for a specified time, reducing unnecessary operations. This is especially useful in
search inputs, where every keystroke can otherwise lead to API requests.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
34
In this example, the function only triggers 500ms after the user stops typing, reducing the
number of API calls.
Question: Which loop would you use to iterate over the selected filters: for...in or
for...of?
Answer:
Use for...of when iterating over arrays, as it allows easy access to each element. for...in
is more suitable for objects where you need to access keys. In this scenario, since the filters
are an array, for...of is the better choice.
For Example:
Resulting Table:
for...in Iterate over object keys Keys Iterating over object props
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
35
Answer:
In this scenario, if-else statements can be used to check the role of the user and display the
corresponding dashboard. This provides clear decision-making based on the value of
userRole.
For Example:
Here, the if-else structure ensures that the correct message is displayed based on the
user’s role.
46. Scenario: You want to initialize certain values at the start of your
program.
Answer:
An Immediately Invoked Function Expression (IIFE) runs immediately when defined,
making it useful for setting up initial values or configurations.
For Example:
(function () {
const initialValue = 42;
console.log(`Initialized with value: ${initialValue}`);
})();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
36
This ensures that initialValue is only available within the IIFE, preventing it from polluting
the global scope.
Answer:
You can create a function that uses arithmetic operators like addition, subtraction,
multiplication, and division to perform operations on two numbers.
For Example:
function arithmeticOperations(a, b) {
console.log(`Addition: ${a + b}`);
console.log(`Subtraction: ${a - b}`);
console.log(`Multiplication: ${a * b}`);
console.log(`Division: ${a / b}`);
}
arithmeticOperations(10, 5);
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
37
48. Scenario: You need to calculate the sum of numbers from 1 to 10 using a
loop.
Answer:
You can use a for loop to iterate through numbers from 1 to 10 and accumulate the sum.
For Example:
let sum = 0;
for (let i = 1; i <= 10; i++) {
sum += i;
}
console.log(`Sum of numbers from 1 to 10: ${sum}`);
This example demonstrates how the loop adds each number to sum until it reaches 10.
49. Scenario: You need to manage multiple user states like "active",
"inactive", and "banned".
Answer:
A switch statement is effective for handling multiple specific cases.
For Example:
switch (userState) {
case 'active':
console.log('User is active.');
break;
case 'inactive':
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
38
console.log('User is inactive.');
break;
case 'banned':
console.log('User is banned.');
break;
default:
console.log('Unknown state.');
}
50. Scenario: You want to create a function that takes another function as
an argument.
Answer:
A higher-order function is a function that takes another function as an argument or returns
a function.
For Example:
function greetUser(name) {
console.log(`Hello, ${name}!`);
}
executeFunction(greetUser, 'Alice');
This example shows how executeFunction takes greetUser as an argument and executes it.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
39
51. Scenario: You need to create a variable that may change within a loop
but should not affect the global scope.
Question: Should you use var or let inside a loop, and why?
Answer:
Use let inside a loop because it ensures the variable is scoped only to that loop block,
preventing it from affecting variables outside the block. Using var would cause the variable
to leak into the function or global scope, potentially causing bugs if reused unintentionally
across iterations.
For Example:
If var were used instead of let, the i variable would be accessible outside the loop, possibly
causing conflicts.
Resulting Table:
52. Scenario: You want to store a large integer value that exceeds the safe
range of JavaScript numbers.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
40
Question: How would you use the BigInt data type to store this value?
Answer:
JavaScript’s regular numbers can accurately represent integers only up to 2^53 - 1. To handle
larger integers, you can use BigInt, which allows you to safely store and manipulate
numbers beyond this limit.
For Example:
Resulting Table:
53. Scenario: You need to find out if a given value is null or undefined.
Question: How would you distinguish between null and undefined in JavaScript?
Answer:
In JavaScript, null is an assigned value representing the absence of any object, while
undefined indicates that a variable has been declared but not initialized. They are distinct,
though both imply the absence of value.
For Example:
let a;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
41
let b = null;
Resulting Table:
54. Scenario: You want to compare two values but need to avoid
unexpected type coercion.
Answer:
Always use === for comparisons to avoid type coercion. The === operator ensures both the
value and the type are the same, reducing the risk of unexpected behavior.
For Example:
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
42
Answer:
The for...in loop is ideal for iterating over the properties (keys) of an object. It allows you to
access both the key and the corresponding value.
For Example:
Resulting Table:
for...in Iterates over object properties Keys & Values Loop through object keys
56. Scenario: You need to create a function that doesn’t bind its own this
context.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
43
Answer:
Arrow functions inherit the this context from their parent scope. This makes them useful
when you want to maintain the same this context, avoiding unintended behavior inside
callbacks.
For Example:
const person = {
name: 'Alice',
greet: () => console.log(`Hello, ${this.name || 'Guest'}`),
};
Here, this.name does not refer to the person object because arrow functions do not bind
their own this.
Answer:
An if-else statement is useful when you need to handle multiple conditions and execute
different blocks of code based on the results.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
44
}
// Output: You are an adult.
58. Scenario: You need to execute code multiple times until a condition is
met.
Answer:
Use a while loop when the number of iterations is unknown, and you want to keep looping
until a condition is satisfied.
For Example:
let count = 0;
while (count < 3) {
console.log(`Count: ${count}`);
count++;
}
// Output:
// Count: 0
// Count: 1
// Count: 2
59. Scenario: You need to break out of a loop when a specific condition is
met.
Answer:
The break statement exits the loop immediately when the specified condition is met.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
45
Answer:
The continue statement skips the current iteration and moves to the next one. This is useful
when you want to bypass specific cases without breaking the entire loop.
For Example:
Resulting Table:
continue Skips the current iteration and i === 2 Skips iteration for
proceeds to the next i = 2
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
46
Question: How would you use Promise.all() to handle multiple asynchronous operations?
Answer:
Promise.all() runs multiple promises in parallel and returns a new promise that resolves
when all input promises are fulfilled or rejects if any one promise fails. This method is useful
when you need to wait for several asynchronous operations to complete before proceeding.
For Example:
Resulting Table:
Promise.all() Waits for all promises to resolve or any Multiple API calls in
to reject parallel
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
47
Promise.allSettled() Waits for all promises to settle Useful for collecting all
(resolve/reject) results
Answer:
finally() is a method on promises that executes code after the promise has settled,
regardless of whether it was fulfilled or rejected. This is useful for cleaning up resources or
logging completion of an operation.
For Example:
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Error:', error))
.finally(() => console.log('Operation Complete'));
// Output:
// Data: { ...post data... }
// Operation Complete
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
48
Answer:
With async/await, you can write asynchronous code that looks synchronous, ensuring one
operation completes before the next starts. This approach avoids deeply nested .then()
callbacks and improves code readability.
For Example:
fetchData();
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
49
await Pauses execution until promise resolves const data = await fetch(url)
Question: How does async/await improve the readability of asynchronous code compared
to callbacks?
Answer:
Using async/await makes asynchronous code look like synchronous code, improving
readability. It also helps avoid "callback hell," where callbacks are deeply nested, making code
difficult to read and maintain.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
50
fetchData();
65. Scenario: You need to create an event listener for multiple buttons
using event delegation.
Answer:
Event delegation leverages event bubbling, where events on child elements propagate to
their parent. You can attach a single event listener on a parent element and handle events
for multiple child elements efficiently.
For Example:
// HTML:
// <div id="parent">
// <button>Button 1</button>
// <button>Button 2</button>
// </div>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
51
Answer:
Closures allow you to create private variables by keeping them within an inner function's
scope, which is not accessible from the outside.
For Example:
function createCounter() {
let count = 0; // Private variable
return function () {
count++;
console.log(`Count: ${count}`);
};
}
Resulting Table:
67. Scenario: You need to ensure only one instance of an object exists
across the application.
Answer:
A singleton pattern ensures that only one instance of an object is created and provides a
global point of access to it.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
52
For Example:
function createInstance() {
return { name: 'Singleton Instance' };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
68. Scenario: You want to compare objects but JavaScript compares them
by reference, not value.
Answer:
Since objects are compared by reference, use JSON.stringify() to compare their values.
For Example:
const obj1 = { a: 1, b: 2 };
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
53
const obj2 = { a: 1, b: 2 };
69. Scenario: You need to delay the execution of code by a few seconds.
Answer:
setTimeout executes a function after a specified delay.
For Example:
console.log('Start');
setTimeout(() => console.log('Executed after 2 seconds'), 2000);
console.log('End');
// Output:
// Start
// End
// Executed after 2 seconds
70. Scenario: You need to run a task periodically, such as updating the time
on a webpage.
Question: How would you use setInterval to execute a task at regular intervals?
Answer:
setInterval repeatedly executes a function at the specified interval.
For Example:
setInterval(() => {
const now = new Date();
console.log(`Current Time: ${now.toLocaleTimeString()}`);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
54
}, 1000);
Answer:
Throttling ensures that a function is called at most once every specified interval, even if it is
triggered multiple times during that interval. This technique is useful for events that fire
continuously, such as scrolling or button clicks, where running the function too frequently
can degrade performance.
For Example:
document.getElementById('button').addEventListener('click', handleClick);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
55
In this example, the handleClick function will execute at most once every 2 seconds, no
matter how frequently the button is clicked.
Resulting Table:
Debouncing Delays function execution until the event Search input, form
stops validation
Answer:
Memoization is a technique that stores the results of expensive function calls to reuse them
when the same inputs occur again, improving performance.
For Example:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) {
console.log('Fetching from cache:', key);
return cache[key];
}
console.log('Calculating result for:', key);
const result = fn(...args);
cache[key] = result;
return result;
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
56
In this example, the second call to add(1, 2) fetches the result from the cache, avoiding
unnecessary computation.
Resulting Table:
Caching Stores data for faster future retrieval API response caching
Answer:
The iterator protocol allows objects to be iterable by defining a Symbol.iterator method
that returns an iterator. This iterator provides a next() method that returns objects
containing value and done properties.
For Example:
const myIterable = {
data: [1, 2, 3, 4],
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
57
This example shows how to make the custom object myIterable behave like an iterable,
allowing it to work with for...of loops.
Resulting Table:
Answer:
An IIFE (Immediately Invoked Function Expression) allows you to create a private scope and
expose only selected parts of the code, simulating a module.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
58
function privateMethod() {
console.log('Private method called');
}
return {
increment: function () {
privateVar++;
console.log(`Current value: ${privateVar}`);
},
};
})();
In this example, only the increment method is accessible outside the module, while
privateMethod and privateVar remain private.
Answer:
A recursive function can flatten arrays with multiple levels of nesting.
For Example:
function flattenArray(arr) {
return arr.reduce((acc, value) =>
Array.isArray(value) ? acc.concat(flattenArray(value)) : acc.concat(value),
[]);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
59
This recursive approach ensures that all nested arrays are flattened into a single-level array.
Question: How would you safely serialize an object with circular references?
Answer:
Circular references occur when an object references itself. A custom serializer can detect
such references to avoid errors.
For Example:
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
});
}
Answer:
You can use structuredClone() or JSON.parse(JSON.stringify()) to deep-clone an
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
60
object. However, structuredClone() is preferred for handling special cases like circular
references and Date objects.
For Example:
const obj = { a: 1, b: { c: 2 } };
const deepClone = JSON.parse(JSON.stringify(obj));
deepClone.b.c = 42;
console.log(obj.b.c); // Output: 2
Answer:
You can chain promises to execute asynchronous tasks in sequence.
For Example:
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((post) => {
console.log(post);
return fetch('https://jsonplaceholder.typicode.com/users/1');
})
.then((response) => response.json())
.then((user) => console.log(user))
.catch((error) => console.error('Error:', error));
This example ensures that the second fetch request runs only after the first completes.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
61
Answer:
Use the Intersection Observer API to detect when images enter the viewport and load them
dynamically.
For Example:
Answer:
Debouncing delays the function execution until the user stops triggering the event,
reducing unnecessary calls.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
62
Resulting Table:
Debouncing Executes after events stop triggering Search input, API requests
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
63
Answer:
The DOM (Document Object Model) is an API that allows JavaScript to interact with the
structure of a webpage. When a browser loads a web page, it parses the HTML and creates a
tree-like representation called the DOM. This tree consists of nodes: elements, attributes, and
text. Using the DOM, JavaScript can manipulate the structure and style of a webpage
dynamically, without needing to reload it. This manipulation includes tasks like adding,
removing, or modifying elements or text.
For Example:
In this example, the getElementById method selects the element with the ID title. The
innerHTML property updates the content of this element, reflecting the change on the page.
Answer:
JavaScript offers several ways to select elements from the DOM. Selection methods depend
on the type of element you're targeting. Some commonly used methods are:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
64
Here, the code selects all elements with the class list-item and sets their text color to blue
using the style property. This is particularly useful when applying changes to multiple
elements at once.
Answer:
The innerHTML property provides a way to read or change the content inside an element,
including HTML markup. It is used to inject text, images, or entire HTML elements
dynamically.
For Example:
In this example, the innerHTML property replaces the current content of the div with a new
paragraph. However, be cautious when using innerHTML because it can expose your site to
Cross-Site Scripting (XSS) attacks if untrusted user input is injected.
Answer:
The classList property allows you to manipulate the CSS classes of an element efficiently.
You can add, remove, toggle, or check if a specific class is present without overwriting
existing classes.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
65
In this example, each click on the button toggles the active class. If the class is present, it
gets removed; if it’s not, it gets added.
Answer:
The addEventListener method allows you to attach an event handler to an element. It can
listen for various events like click, keyup, mouseover, etc. Unlike the older onclick attribute,
addEventListener supports multiple handlers for the same event.
For Example:
In this example, clicking the button triggers an alert. Using addEventListener keeps your
JavaScript code modular and helps separate behavior from structure.
Answer:
Event Delegation leverages event bubbling, where events propagate from child to parent
elements. Instead of attaching listeners to each child, you can place a single listener on a
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
66
parent to manage events on all children. This is especially useful for dynamically generated
elements.
For Example:
In this example, the parent <ul> element listens for clicks on any of its <li> children, even if
they are added later.
Answer:
The Browser Object Model (BOM) allows interaction with the browser itself. It provides
objects like window, navigator, and history to control the browser environment. Using
BOM, you can perform tasks like opening new tabs, navigating between pages, or storing
data in localStorage and sessionStorage.
For Example:
Here, the window object provides information about the browser window, including the
current page URL using location.href.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
67
Answer:
Both localStorage and sessionStorage store data in the browser, but they differ in how
long the data persists:
For Example:
In this example, the localStorage retains data across browser sessions. Use localStorage
for longer-term data and sessionStorage for temporary data.
Answer:
The Fetch API simplifies making asynchronous HTTP requests. It returns a Promise, which
allows for easier handling of success and error responses compared to older AJAX methods.
For Example:
In this example, the fetch method requests data from an API. If successful, the response is
converted to JSON and logged.
Answer:
The setTimeout function executes a task after a specified delay, while setInterval repeats a
task at specified intervals. Both functions allow JavaScript to manage time-based events.
For Example:
In this example, setTimeout delays the message by 2 seconds, while setInterval repeatedly
prints a message every second. The interval is cleared after 5 seconds using clearInterval.
Answer:
Event Bubbling is a process in which an event starts from the target element (where it is
triggered) and propagates up through its parent elements in the DOM tree until it reaches
the root element (like <html> or document). This happens by default for most events, such as
click and keyup. It enables parent elements to react to events triggered by their child
elements. However, in cases where this behavior is not desired, the event propagation can be
stopped using event.stopPropagation().
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
69
Here, the click event on the child <div> triggers both the child and the parent’s click event
handlers because the event bubbles up from the child to the parent.
Answer:
In Event Capturing, the event starts from the topmost parent element and travels down
through the DOM tree until it reaches the target element. By default, JavaScript uses event
bubbling, but you can enable capturing by setting the third parameter of
addEventListener() to true. Event capturing is less commonly used, but it can be helpful
when you want a parent element to handle events before they reach the target element.
For Example:
In this example, the click event triggers the parent’s event handler first because capturing is
enabled.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
70
Answer:
To stop an event from propagating through the DOM tree (either during bubbling or
capturing), you can use event.stopPropagation(). This method is helpful when you want to
restrict an event to the target element only, without letting it affect parent elements.
For Example:
In this example, clicking the child element logs "Child clicked," but the parent’s event listener
is not triggered due to the use of stopPropagation().
Answer:
In JavaScript, certain events come with a default action—for example, submitting a form on
button click or navigating to another page when a link is clicked. To prevent this behavior,
you can use event.preventDefault(). This method is commonly used to control form
submissions or navigation links.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
71
event.preventDefault();
console.log('Form submission prevented');
});
In this example, the default form submission is stopped, and the message "Form submission
prevented" is logged instead.
Answer:
The window object represents the entire browser window, serving as the global context in a
web page. It includes properties and methods to control the browser window, such as
alert(), location, and innerWidth.
The document object, on the other hand, represents the content of the web page currently
loaded in the browser. It allows you to manipulate HTML elements within the page.
For Example:
Here, the window object provides the width of the browser window, and the document object
gives the title of the current web page.
Answer:
Both localStorage and cookies allow data storage on the client side, but they have different
characteristics:
● localStorage: Stores larger data (5-10 MB) that persists until manually cleared by the
user or code. It is accessible only from JavaScript.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
72
● Cookies: Limited to 4 KB in size, can be accessed by both client and server, and often
have expiration dates for tracking sessions.
For Example:
Cookies are useful for session management, while localStorage is better for larger and
long-term data.
Answer:
The navigator object provides information about the browser, device, and operating
environment. It helps developers detect the browser version, user’s preferred language, and
whether cookies are enabled. This can be useful for browser-specific optimizations or
tracking user settings.
For Example:
In this example, the navigator object provides details about the browser type and the
language set by the user.
Answer:
The history object allows navigation through the user's browser history. It is part of the
BOM and provides methods such as:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
73
For Example:
Using the history object, you can programmatically control browser navigation.
Answer:
Since the Fetch API returns a Promise, you need to handle potential errors using .catch()
or try-catch blocks when using async/await. If the request fails, the error can be caught
and logged or handled appropriately.
For Example:
In this example, if the request fails or the response is not OK, an error is thrown and handled
in the .catch() block.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
74
Answer:
The setInterval() function repeats a given task at regular intervals until it is stopped using
clearInterval(). You need to store the interval ID returned by setInterval() to clear it
later.
For Example:
In this example, the interval prints a message every second, but it is stopped after 3 seconds
using clearInterval(). This demonstrates how you can manage repeating tasks efficiently.
Answer:
JavaScript handles asynchronous operations using Promises to prevent blocking the main
thread. A Promise is an object representing an operation that may be completed in the
future or may fail. This avoids "callback hell" by enabling structured chaining through
.then() and .catch(). A pending Promise can either resolve (complete successfully) or
reject (fail). Promises are useful in network requests, timers, or file reading operations.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
75
fetchData()
.then(data => console.log(data)) // Output: Data fetched successfully
.catch(error => console.error(error));
Here, after 1 second, the Promise resolves, logging "Data fetched successfully". If an error
occurred, it would be handled in .catch().
Answer:
async and await are syntactic sugar for handling Promises, making asynchronous code look
synchronous. Declaring a function as async ensures it returns a Promise. Within an async
function, the await keyword pauses the function until the Promise is resolved, improving
code readability.
For Example:
fetchUser();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
76
Here, await waits for the API response and JSON conversion before logging the result.
Answer:
A closure is when a function retains access to variables from its parent scope, even after the
outer function finishes executing. Closures are used to encapsulate data and create private
state.
For Example:
function counter() {
let count = 0;
return function () {
count++;
console.log(`Count: ${count}`);
};
}
Here, the inner function retains access to the count variable, even though the outer
counter() function has already finished executing.
Answer:
JavaScript is single-threaded but uses the event loop to manage concurrency. When an
asynchronous task is initiated (like a timer or API request), it’s handled outside the main
thread. Once completed, the event loop places it back in the call stack to execute.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
77
console.log('Start');
setTimeout(() => console.log('Inside Timeout'), 0);
console.log('End');
// Output:
// Start
// End
// Inside Timeout
Here, the synchronous code runs first. Even though setTimeout has no delay, its callback is
added to the task queue and executed after the synchronous code finishes.
Answer:
Debouncing ensures a function executes only after a specified delay has passed since the
last time it was called. It is often used in search boxes or resize events to prevent excessive
function calls.
For Example:
Here, the logInput function executes 500ms after the user stops typing.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
78
Answer:
Throttling ensures a function runs at regular intervals, even if it’s triggered continuously. This
is useful for scrolling or button click events to limit the frequency of execution.
For Example:
window.addEventListener('scroll', logScroll);
Here, the logScroll function executes at most once every second, even if the scroll event
fires continuously.
Answer:
JavaScript has automatic garbage collection, but memory leaks can still occur if references
to objects are not released. Common causes include:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
79
Not removing the event listener keeps the reference in memory, causing a memory leak.
28. What are JavaScript modules, and how do you use them?
Answer:
JavaScript modules allow code to be split into reusable files, improving code maintainability.
You can export functions, variables, or objects from one file and import them into another.
For Example:
// math.js (module)
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './math.js';
console.log(add(2, 3)); // Output: 5
Here, the add function is exported from math.js and imported into main.js.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
80
Answer:
Promise.all() executes multiple Promises in parallel and resolves when all Promises are
fulfilled. If any Promise fails, Promise.all() rejects with the error.
For Example:
Promise.all([fetchPosts, fetchUsers])
.then(responses => Promise.all(responses.map(res => res.json())))
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Here, both API calls are executed simultaneously. If both resolve, the results are logged;
otherwise, the error is caught.
Answer:
JavaScript is single-threaded, but Web Workers allow you to run code in background
threads. This prevents the UI from freezing during long computations.
For Example:
// main.js
const worker = new Worker('worker.js');
worker.postMessage(5);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
81
In this example, the Web Worker performs a calculation in the background and sends the
result back to the main thread. This ensures smooth UI performance.
Answer:
Both Map and Object store key-value pairs, but they have different use cases and behaviors:
For Example:
Here, the Map stores a non-string key, which is not possible with Object.
Answer:
The value of this depends on the context in which a function is called:
● In global scope, this refers to the global window object (in browsers).
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
82
For Example:
const obj = {
name: 'Shardul',
greet: function () {
console.log(this.name); // 'Shardul'
},
};
obj.greet();
Here, the regular function uses this as the object (obj), while the arrow function keeps this
from the outer scope.
Answer:
A generator function is declared with function* and can pause its execution using the
yield keyword. Generators are useful for creating iterators.
For Example:
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
83
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
Answer:
These methods allow you to change the context of this in a function:
● call(): Invokes the function immediately with a specified this value and arguments.
● apply(): Similar to call(), but arguments are passed as an array.
● bind(): Returns a new function with a bound this value.
For Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
Answer:
JavaScript uses prototypes for inheritance. Every object has a hidden property called
__proto__, which points to its prototype. Methods and properties are inherited through the
prototype chain.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
84
For Example:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, ${this.name}`);
};
Here, the user object inherits the greet method from Person.prototype.
Answer:
Deep cloning creates a complete copy of an object, including nested objects, unlike shallow
cloning.
For Example:
deepClone.address.city = 'Mumbai';
console.log(obj.address.city); // Output: Pune
Here, the original object remains unchanged because we used deep cloning.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
85
Answer:
The module pattern allows you to create private variables and public methods by using
closures. It helps in organizing code and encapsulating logic.
For Example:
return {
increment() {
count++;
console.log(count);
},
reset() {
count = 0;
console.log('Counter reset');
},
};
})();
Counter.increment(); // 1
Counter.increment(); // 2
Counter.reset(); // Counter reset
Here, the count variable is private, accessible only through public methods.
Answer:
Hoisting allows function and variable declarations to be moved to the top of their scope
before code execution. However, only the declaration is hoisted, not the initialization.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
86
var a = 5;
Variables declared with let and const are hoisted but cannot be accessed before declaration
(temporal dead zone).
Answer:
WeakMap and WeakSet hold weak references to objects, meaning the garbage collector can
reclaim the memory if there are no other references. They do not prevent memory leaks and
are useful in managing memory.
For Example:
In this example, once obj is set to null, the garbage collector can reclaim it because WeakMap
holds a weak reference.
Answer:
A custom iterator allows an object to be iterated using the for...of loop by implementing
the [Symbol.iterator] method.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
87
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.data[index++],
done: index > this.data.length,
}),
};
},
};
// Output:
// 1
// 2
// 3
Here, we created an object with a custom iterator that allows it to be traversed using
for...of.
SCENARIO QUESTIONS
41. Scenario: Changing the content of an HTML element dynamically.
Question: How can you use JavaScript to modify the content of an HTML element using
innerHTML?
Answer:
To modify the content of an HTML element, innerHTML is used. It updates the HTML
structure or text within the selected element. This is helpful when you need to change the
display based on user input, an event, or a data update.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
88
For Example:
// JavaScript
const greetingDiv = document.getElementById('greeting');
greetingDiv.innerHTML = '<h1>Hello, Welcome to the Page!</h1>';
If this example were displayed on a webpage, the resulting HTML structure would be:
Tag Content
This structure shows the div element containing a header (h1), which is inserted dynamically
using JavaScript.
Question: How can you use JavaScript to apply styles to multiple elements with the same
class name?
Answer:
Using querySelectorAll(), you can select all elements with a specific class name. It returns
a NodeList, which behaves like an array. You can use forEach() to iterate over the elements
and apply styles.
For Example:
// JavaScript
const highlights = document.querySelectorAll('.highlight');
highlights.forEach(paragraph => {
paragraph.style.color = 'red';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
89
});
Resulting Table:
Question: How can you use addEventListener to trigger an alert when a button is clicked?
Answer:
The addEventListener() method lets you attach multiple event handlers to an element
without overriding existing ones. It also separates JavaScript behavior from HTML structure,
following best practices.
For Example:
// JavaScript
const button = document.getElementById('alertButton');
button.addEventListener('click', () => {
alert('Button was clicked!');
});
After clicking the button, the user will see an alert dialog with the message "Button was
clicked!".
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
90
Question: How can you use sessionStorage to store and retrieve data?
Answer:
The sessionStorage object stores data temporarily for the duration of the session. It is
cleared when the tab or browser is closed, making it useful for temporary data, such as form
progress.
For Example:
Resulting Table:
Question: How can you use setTimeout to show a message after 3 seconds?
Answer:
The setTimeout() function executes a specified function after a given delay. This can be
used for delayed notifications, animations, or loading indicators.
For Example:
setTimeout(() => {
console.log('Hello! This message appears after 3 seconds.');
}, 3000);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
91
Question: How can you use event delegation to handle clicks on dynamically added list
items?
Answer:
With event delegation, you add an event listener to a parent element, allowing it to handle
events for dynamically added child elements. This improves performance by avoiding
multiple listeners.
For Example:
// JavaScript
const itemList = document.getElementById('itemList');
itemList.addEventListener('click', event => {
if (event.target.tagName === 'LI') {
alert(`You clicked on ${event.target.textContent}`);
}
});
After clicking on the new <li> item, an alert will show the clicked item's text.
Question: How can you store and retrieve a value using localStorage?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
92
Answer:
The localStorage object stores key-value pairs with no expiration date, making it ideal for
saving user preferences.
For Example:
Resulting Table:
Question: How can you prevent a form from submitting using event.preventDefault()?
Answer:
The event.preventDefault() method prevents the default behavior of an event, like form
submission, to allow custom validation or actions.
For Example:
// JavaScript
const form = document.getElementById('myForm');
form.addEventListener('submit', event => {
event.preventDefault();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
93
Now, clicking the submit button will not submit the form but log the message "Form
submission prevented".
Question: How can you use the Fetch API to retrieve data from a server?
Answer:
The Fetch API provides a modern way to make HTTP requests and returns a Promise. It’s
used for GET, POST, PUT, or DELETE requests.
For Example:
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
{
"userId": 1,
"id": 1,
"title": "Sample Post",
"body": "This is a sample post content."
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
94
Question: How can you demonstrate event bubbling and capturing in JavaScript?
Answer:
Event bubbling propagates the event from the target element to its ancestors, while
capturing flows from ancestors to the target. Both can be demonstrated with
addEventListener().
For Example:
// JavaScript
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// Event capturing
parent.addEventListener(
'click',
() => console.log('Parent capturing'),
true
);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
95
This table shows how both bubbling and capturing occur when the button is clicked.
Answer:
The getElementById() method selects an HTML element using its unique ID. This is the
most efficient way to access a single element because IDs are unique within the DOM. After
selecting the element, you can read or modify its properties and content.
For Example:
// JavaScript
const titleElement = document.getElementById('title');
console.log(titleElement.innerHTML); // Output: Welcome
Resulting Table:
Property Value
Tag <h1>
ID title
Content Welcome
Question: How can you use JavaScript to modify an element’s CSS styles?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
96
Answer:
The style property allows you to dynamically modify an element’s inline CSS styles. This is
helpful when you need to change an element's appearance in response to user actions, such
as hovering, clicking, or form submissions.
For Example:
// JavaScript
const box = document.getElementById('box');
box.style.backgroundColor = 'blue';
box.style.border = '2px solid black';
Resulting Table:
Property Value
backgroundColor blue
The <div> element now has a blue background and a black border.
Question: How do you add or remove CSS classes using the classList property?
Answer:
The classList property provides methods to add, remove, toggle, or check CSS classes on
an element. This is useful when changing the visual state of an element dynamically without
directly modifying the styles.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
97
// JavaScript
const box = document.getElementById('box');
box.classList.add('highlight'); // Adds 'highlight' class
box.classList.remove('highlight'); // Removes 'highlight' class
box.classList.toggle('active'); // Toggles 'active' class
Resulting Table:
Question: How can you create and add new elements to the DOM using JavaScript?
Answer:
You can use document.createElement() to create a new HTML element and appendChild()
or append() to add it to the DOM. This method is useful when content needs to be generated
dynamically based on user actions or API responses.
For Example:
// JavaScript
const newElement = document.createElement('p');
newElement.textContent = 'This is a new paragraph.';
document.body.appendChild(newElement);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
98
Resulting Table:
Tag Content
The <p> element is added to the end of the <body> with the given content.
Question: How do you detect when a user presses a key using JavaScript?
Answer:
The keydown and keyup events detect when a key is pressed or released. These events are
commonly used to build features like input validation or keyboard shortcuts.
For Example:
When a key is pressed, the console will log the corresponding key's name.
Answer:
The setInterval() function executes a specified function at fixed intervals (in milliseconds).
This is useful for real-time updates, such as clocks or dashboards.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
99
setInterval(() => {
console.log('This message logs every 2 seconds.');
}, 2000);
Question: How can you use clearTimeout or clearInterval to cancel a scheduled task?
Answer:
The clearTimeout() and clearInterval() methods cancel tasks that were scheduled with
setTimeout() or setInterval(). This is useful to prevent unnecessary operations when they
are no longer needed.
For Example:
setTimeout(() => {
clearInterval(intervalId);
console.log('Interval cleared');
}, 5000);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
100
Resulting Table:
1 Running...
2 Running...
3 Running...
4 Running...
5 Interval cleared
Question: How can you create, read, and delete cookies in JavaScript?
Answer:
Cookies store small pieces of data in the browser. You can set, read, or delete cookies using
the document.cookie API.
For Example:
// Setting a cookie
document.cookie = "user=Shardul; expires=Fri, 31 Dec 2024 12:00:00 UTC";
// Reading cookies
console.log(document.cookie); // Output: user=Shardul
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
101
Answer:
The resize event detects when the browser window is resized. You can use it to make your
application responsive by adjusting layout or components dynamically.
For Example:
window.addEventListener('resize', () => {
console.log(`Window size: ${window.innerWidth}x${window.innerHeight}`);
});
Resulting Table:
Answer:
Event bubbling propagates events from the target element to its parent elements. You can
stop this propagation using event.stopPropagation().
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
102
For Example:
// JavaScript
const parent = document.getElementById('parent');
const child = document.getElementById('child');
Resulting Table:
This ensures only the child’s click event is logged, as the propagation is stopped.
Question: How can you use Promises to handle asynchronous API calls?
Answer:
Promises simplify handling asynchronous operations. With fetch(), you can make an API
call that returns a promise. This promise resolves with the response if the request is
successful or rejects if there is an error.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
103
getPost(1)
.then((data) => console.log(data))
.catch((error) => console.error(error));
Field Value
userId 1
id 1
Answer:
async/await makes asynchronous code easier to read and write by eliminating the need for
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
104
chained .then() calls. An async function returns a promise, and await pauses execution
until the promise resolves.
For Example:
getPostAsync(1);
Field Value
userId 1
id 1
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
105
Answer:
Promise.all() runs multiple promises in parallel and waits for all of them to resolve. If any
promise is rejected, the entire Promise.all() call fails.
For Example:
Promise.all([fetchPosts, fetchUsers])
.then((responses) => Promise.all(responses.map((res) => res.json())))
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Error:', error));
Resource Count
Posts 100
Users 10
Answer:
Debouncing ensures that a function executes only after a specified delay. This is helpful for
actions like search input where you want to wait for the user to stop typing before making
an API call.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
106
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
};
Answer:
Throttling limits a function's execution to once every specified interval. It’s useful for actions
like scroll events to improve performance.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
107
window.addEventListener('scroll', handleScroll);
1 Scrolled!
2 (Ignored)
3 Scrolled!
Question: How can you create and dispatch a custom event in JavaScript?
Answer:
You can create a custom event using the CustomEvent constructor and trigger it using
dispatchEvent().
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
108
document.dispatchEvent(myEvent);
Resulting Output:
Answer:
A closure is when a function retains access to variables from its outer scope, even after the
outer function has finished executing.
For Example:
function createCounter() {
let count = 0;
return () => {
count++;
console.log(`Count: ${count}`);
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
109
Call Output
counter() Count: 1
counter() Count: 2
Answer:
The bind() method creates a new function with a specified this value, which is useful when
passing functions as callbacks.
For Example:
const person = {
name: 'Shardul',
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
110
Question: How do call() and apply() invoke functions with a specific this context?
Answer:
call() and apply() both invoke a function with a specific this value, but call() accepts
arguments individually, while apply() accepts them as an array.
For Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
Resulting Table:
Question: How can you use Web Workers to perform background tasks?
Answer:
Web Workers run scripts in the background, allowing you to perform heavy computations
without blocking the UI thread.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
111
// worker.js
self.onmessage = function (e) {
const result = e.data * 2;
postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(5);
Resulting Table:
5 5*2 10
This demonstrates how Web Workers can offload computational tasks to avoid blocking the
UI.
Question: How can you implement lazy loading of images using the IntersectionObserver
API?
Answer:
Lazy loading delays loading images until they enter the viewport. This improves page
performance, especially for pages with many images. The IntersectionObserver API
detects when an element becomes visible in the viewport and triggers loading.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
112
// JavaScript
const lazyImages = document.querySelectorAll('.lazy');
Resulting Table:
Answer:
A deep clone creates an entirely new object, including all nested objects. Using
JSON.stringify() and JSON.parse() is a quick way to deep clone objects, although it
doesn’t work for functions or undefined values.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
113
Resulting Table:
Answer:
You can use Array.prototype.flat() with Infinity as the depth or use recursion to flatten
a nested array.
For Example:
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
114
Question: How can you detect the user’s browser and platform using the navigator object?
Answer:
The navigator object provides information about the user's browser and platform. This is
useful for browser-specific behavior.
For Example:
Resulting Table:
platform Win32
Answer:
A custom iterator allows an object to be used in a for...of loop by implementing the
[Symbol.iterator]() method.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
115
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.data[index++],
done: index > this.data.length,
}),
};
},
};
Resulting Table:
Iteration Value
1 1
2 2
3 3
Question: How can you use WeakMap to store data without preventing garbage collection?
Answer:
A WeakMap holds weak references to objects, meaning they can be garbage collected if no
other references exist. This is useful for caching without causing memory leaks.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
116
Resulting Table:
Question: How can you use requestAnimationFrame for throttling DOM updates?
Answer:
Using requestAnimationFrame() ensures that animations and updates are synchronized
with the browser’s refresh rate, reducing the number of DOM manipulations.
For Example:
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
console.log('Scroll position:', window.scrollY);
ticking = false;
});
ticking = true;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
117
});
Resulting Table:
Question: How can you handle focus and blur events using JavaScript?
Answer:
The focus and blur events detect when an input gains or loses focus. These events are useful
for form validation or styling.
For Example:
Resulting Table:
Event Output
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
118
Question: How can you create a button that scrolls the page to the top?
Answer:
You can use the window.scrollTo() method to smoothly scroll the page to the top when a
button is clicked.
For Example:
button.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
Resulting Table:
Question: How can you animate elements using CSS transitions and JavaScript?
Answer:
You can combine CSS transitions with JavaScript to animate elements dynamically, such as
moving or resizing them.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
119
// JavaScript
const box = document.getElementById('box');
box.style.transition = 'all 0.5s';
document.addEventListener('click', () => {
box.style.width = '200px';
box.style.height = '200px';
});
Resulting Table:
This example shows how the box smoothly resizes when the page is clicked.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
120
THEORETICAL QUESTIONS
1. What is an object literal in JavaScript?
Answer:
An object literal is the simplest way to create an object in JavaScript. It consists of a comma-
separated list of key-value pairs enclosed in curly braces {}. This approach is often used when
you need a single object with a known set of properties and values. Object literals are highly
readable and allow quick creation of small objects without needing a constructor or class.
For Example:
const person = {
name: "John",
age: 30,
greet: function () {
console.log(`Hello, my name is ${this.name}.`);
}
};
Here, person is created as an object literal with properties name, age, and a greet() method.
The this keyword refers to the object calling the method (person), ensuring access to the
object’s properties within the method.
Answer:
A constructor function is a special function used to create multiple instances of an object
with similar properties. It allows you to initialize an object’s properties dynamically at
runtime. When you use the new keyword with a constructor function, it creates a new object
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
121
and sets the context of this to that new object. Constructors follow the convention of
naming functions with a capital first letter.
For Example:
Here, Person is a constructor function. person1 is a new object created using the new
Person() syntax, inheriting properties and methods defined within the function.
Answer:
In JavaScript, every object has a hidden property called [[Prototype]], which refers to
another object. This is called prototype-based inheritance. When trying to access a property
that doesn't exist on the object itself, JavaScript looks up the prototype chain to find it. This
mechanism enables objects to reuse properties and methods from other objects, promoting
efficient memory usage and modular code.
For Example:
function Animal(type) {
this.type = type;
}
Animal.prototype.speak = function () {
console.log(`${this.type} makes a sound.`);
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
122
In the example above, the speak() method is added to Animal.prototype, allowing all
instances created from Animal to share this method.
Answer:
Object.create() is a built-in method that creates a new object with a specified prototype. It
is a more straightforward way to set up prototype-based inheritance, without needing
constructor functions. It gives more control over the prototype and properties of an object.
For Example:
const animalPrototype = {
speak: function () {
console.log(`${this.type} makes a sound.`);
}
};
Here, dog is created with animalPrototype as its prototype, meaning it inherits the speak
method.
Answer:
Classes in JavaScript are syntactic sugar introduced in ES6 to make working with objects and
inheritance easier. While constructor functions serve the same purpose, classes provide a
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
123
clearer, more structured syntax. With classes, the concept of inheritance and object creation
becomes more readable.
For Example:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name}.`);
}
}
This example shows how the Person class is more organized and intuitive compared to
constructor functions.
Answer:
The extends keyword is used to create a class that inherits from another class. This helps in
reusing code and creating a hierarchy of objects. The child class inherits all properties and
methods of the parent class but can also have its own unique features.
For Example:
class Animal {
constructor(type) {
this.type = type;
}
sound() {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
124
Here, the Dog class extends Animal, inheriting its sound() method.
Answer:
Static methods belong to the class itself, not the instances of the class. They are called using
the class name rather than an object created from the class. Static methods are often utility
functions that relate to the class but don't need to operate on individual instances.
For Example:
class MathUtils {
static add(a, b) {
return a + b;
}
}
In the above example, add() is a static method that can be accessed using MathUtils
directly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
125
Answer:
Getters and setters allow you to encapsulate object properties by controlling access to
them. A getter retrieves the value of a property, and a setter modifies it. Using getters and
setters ensures that any logic or validation can be implemented when properties are
accessed or changed.
For Example:
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(newName) {
if (newName) {
this._name = newName;
}
}
}
Here, the _name property is accessed and modified using the name getter and setter.
Answer:
Implicit binding happens when the this keyword inside a function refers to the object that
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
126
invoked the function. This occurs most often when functions are called as methods on an
object. The value of this is set to the object to the left of the dot operator.
For Example:
const person = {
name: "Charlie",
greet() {
console.log(`Hi, I'm ${this.name}.`);
}
};
Here, this refers to the person object because it is invoking the greet() function.
Answer:
Explicit binding allows you to explicitly set the value of this when calling a function using
the call(), apply(), or bind() methods. This is useful when you want to control the context
of this in a function regardless of how it is invoked.
For Example:
function greet() {
console.log(`Hi, I'm ${this.name}.`);
}
Here, call() ensures that this inside the greet() function refers to the person object, even
though greet is not a method of person.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
127
Answer:
All three methods—call(), apply(), and bind()—are used to explicitly set the value of this
inside a function. However, they differ in how they invoke the function and how arguments
are passed.
For Example:
12. What is the difference between arrow functions and regular functions?
Answer:
Arrow functions (=>) were introduced in ES6 and differ from regular functions in several key
ways. The primary difference is that arrow functions do not have their own this binding.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
128
Instead, they inherit this from the lexical scope (the surrounding context). Arrow functions
also cannot be used as constructors and lack their own arguments object.
For Example:
const person = {
name: "Bob",
greet: function () {
console.log(`Hello, my name is ${this.name}.`); // Regular function: `this`
refers to `person`
},
arrowGreet: () => {
console.log(`Hello, my name is ${this.name}.`); // Arrow function: `this` is
inherited from the outer scope
}
};
In this example, the arrow function inherits this from its lexical scope, which isn't the person
object.
Answer:
The prototype chain is a series of linked objects that JavaScript follows to find properties or
methods. When accessing a property on an object, JavaScript first checks the object itself. If
the property is not found, it looks at the object's prototype, and so on, until the chain ends
with null.
For Example:
function Animal(type) {
this.type = type;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
129
Animal.prototype.speak = function () {
console.log(`${this.type} makes a sound.`);
};
console.log(dog.hasOwnProperty("type")); // true
console.log(dog.hasOwnProperty("speak")); // false, as it's inherited from the
prototype
dog.speak(); // Output: Dog makes a sound.
Answer:
The super() function is used within a subclass to call the constructor of the parent class. This
allows the child class to inherit properties from the parent. You must call super() before
accessing this in the child constructor.
For Example:
class Animal {
constructor(type) {
this.type = type;
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
130
15. What happens if you don’t use new with a constructor function?
Answer:
If a constructor function is called without the new keyword, the this inside the function will
not refer to the new object. Instead, it will point to the global object (window in browsers). This
can lead to unexpected behavior or errors.
For Example:
function Person(name) {
this.name = name;
}
To avoid such issues, always use new when calling a constructor function.
Answer:
Method chaining is a technique where multiple methods are called sequentially on the
same object. Each method returns the object itself (or another object), enabling further calls
in a single statement. This makes code more concise and expressive.
For Example:
class Calculator {
constructor(value = 0) {
this.value = value;
}
add(num) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
131
this.value += num;
return this;
}
subtract(num) {
this.value -= num;
return this;
}
multiply(num) {
this.value *= num;
return this;
}
}
Answer:
A singleton is a design pattern where only one instance of an object is created and shared
across the application. This can be achieved using an IIFE (Immediately Invoked Function
Expression) or an object literal.
For Example:
function createInstance() {
return { message: "I am the only instance" };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
132
}
return instance;
}
};
})();
Answer:
Object.freeze() is a method that prevents modifications to an object. After freezing, you
cannot add, delete, or change properties. It ensures that the object remains immutable.
For Example:
19. What is the difference between shallow copy and deep copy?
Answer:
A shallow copy copies only the top-level properties, while a deep copy copies all nested
objects and properties. Shallow copies can lead to unexpected behavior if the original object
contains references to other objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
133
Answer:
Mixins are a way to add reusable functionality to multiple objects or classes. Instead of using
inheritance, mixins copy properties and methods into other objects, promoting code reuse.
For Example:
const sayHiMixin = {
sayHi() {
console.log(`Hi, my name is ${this.name}.`);
}
};
class Person {
constructor(name) {
this.name = name;
}
}
Object.assign(Person.prototype, sayHiMixin);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
134
21. What is the concept of closures, and how do they relate to object-
oriented programming in JavaScript?
Answer:
A closure is formed when a function retains access to variables from its outer scope, even
after the outer function has completed execution. Closures are commonly used in object-
oriented programming to encapsulate data, mimicking private variables. This is useful for
creating data hiding and ensuring the internal state of an object cannot be accessed directly
from the outside.
For Example:
function Counter() {
let count = 0;
return {
increment: function () {
count++;
console.log(`Count: ${count}`);
},
getCount: function () {
return count;
}
};
}
Here, count is private, accessible only through the returned methods, demonstrating how
closures provide encapsulation.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
135
Answer:
When the new keyword is used, JavaScript performs several steps internally to create a new
object from a constructor function:
For Example:
function Person(name) {
this.name = name;
}
The new keyword ensures that the this inside the constructor points to the newly created
object.
Answer:
Inheritance allows a child class or object to inherit properties and behaviors from a parent
class. Polymorphism allows a method to behave differently based on the context (e.g.,
different classes implementing the same method in their own way). In JavaScript,
polymorphism is often achieved through method overriding.
For Example:
class Animal {
speak() {
console.log("The animal makes a sound.");
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
136
}
}
Answer:
Since JavaScript does not natively support multiple inheritance, mixins allow us to copy
functionality from multiple sources into a class.
For Example:
const CanRun = {
run() {
console.log(`${this.name} is running.`);
}
};
const CanJump = {
jump() {
console.log(`${this.name} is jumping.`);
}
};
class Animal {
constructor(name) {
this.name = name;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
137
}
}
Here, the Animal class acquires methods from both CanRun and CanJump.
Answer:
In classical inheritance (as seen in languages like Java), classes define the structure and
behavior of objects. Prototypal inheritance in JavaScript relies on objects inheriting directly
from other objects via prototypes.
const animal = {
speak() {
console.log(`${this.name} makes a sound.`);
}
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
138
Answer:
JavaScript supports method overriding but does not natively support method overloading
(same method with different parameter signatures). Method overriding occurs when a
subclass provides a specific implementation of a method already defined in the parent class.
class Animal {
sound() {
console.log("Some generic sound.");
}
}
For method overloading, you can implement it by checking the number or type of
parameters.
Answer:
Encapsulation refers to bundling data and methods within an object, hiding the internal
details from external access. This helps in:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
139
class BankAccount {
#balance; // Private field
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
Answer:
With the introduction of private fields in ES2022, JavaScript allows properties and methods
to be truly private using the # prefix. Private fields are only accessible within the class.
For Example:
class Counter {
#count = 0; // Private field
increment() {
this.#count++;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
140
getCount() {
return this.#count;
}
}
Answer:
JavaScript does not have built-in support for abstract classes (classes that cannot be
instantiated and are meant to be extended). However, you can simulate them by throwing
an error in the constructor if someone tries to instantiate the base class directly.
For Example:
class Animal {
constructor() {
if (new.target === Animal) {
throw new Error("Cannot instantiate abstract class.");
}
}
speak() {
throw new Error("Method 'speak()' must be implemented.");
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
141
Answer:
The instanceof operator checks whether an object is an instance of a specific class or
constructor function, considering the object's prototype chain. It is useful for type checking
in JavaScript.
For Example:
class Animal {}
class Dog extends Animal {}
Here, instanceof confirms that myDog is an instance of both Dog and Animal, showing the
inheritance hierarchy.
31. How does JavaScript handle inheritance for built-in objects like Array or
Error?
Answer:
JavaScript allows custom objects to inherit from built-in objects like Array and Error. This is
done using ES6 classes with the extends keyword. When inheriting from built-in objects, you
must call super() to properly initialize the parent object, ensuring the subclass behaves
correctly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
142
For Example:
Here, CustomArray inherits from Array, and the subclass has access to all native array
methods as well as the new sum() method.
32. What are decorators in JavaScript, and how can they be used with
classes?
Answer:
Decorators are functions that modify the behavior of classes, methods, or properties. They
are a feature currently in stage 3 proposal and available in some environments. Decorators
wrap a class or method, adding additional functionality such as logging or validation.
class Calculator {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
143
@logMethod
add(a, b) {
return a + b;
}
}
Here, the @logMethod decorator adds logging functionality to the add() method.
Answer:
Composition and inheritance are two ways to create complex objects. Inheritance creates a
"is-a" relationship (e.g., a Dog is an Animal), while composition uses a "has-a" relationship (e.g.,
a Car has an Engine). Composition is often preferred over inheritance as it leads to more
flexible and maintainable code.
class Engine {
start() {
console.log("Engine started.");
}
}
class Car {
constructor(engine) {
this.engine = engine;
}
drive() {
this.engine.start();
console.log("Car is driving.");
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
144
34. What are the differences between public, private, and protected fields
in JavaScript?
Answer:
In JavaScript, public fields are accessible from anywhere, private fields (prefixed with #) are
accessible only within the class, and protected fields are not natively supported but can be
simulated with conventions like underscores (_).
For Example:
class Person {
#privateField = "This is private";
constructor(name) {
this.name = name; // Public field
}
getPrivateField() {
return this.#privateField;
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
145
Answer:
JavaScript does not natively support multiple inheritance, so it avoids the diamond problem
(a scenario where a child class inherits from two parents that share a common ancestor).
However, developers can use composition or mixins to simulate multiple inheritance
without conflicts.
Answer:
You can define asynchronous methods inside a class using the async keyword. These
methods return a Promise and allow await inside them to handle asynchronous operations.
For Example:
class DataFetcher {
async fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await response.json();
console.log(data);
}
}
Answer:
Both Object.assign() and the spread operator ({...}) are used to copy properties from
one or more source objects into a target object. However:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
146
const obj1 = { a: 1 };
const obj2 = { b: 2 };
console.log(merged1); // Output: { a: 1, b: 2 }
console.log(merged2); // Output: { a: 1, b: 2 }
Answer:
A factory function is a function that returns a new object, whereas a constructor function is
used with the new keyword to create objects. Factory functions offer more flexibility, such as
avoiding the need for this.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
147
Answer:
JavaScript does not have interfaces like other languages, but polymorphism can be achieved
by ensuring different classes implement the same methods, allowing them to be used
interchangeably.
For Example:
class Animal {
speak() {
console.log("Animal speaks.");
}
}
40. What are proxy objects in JavaScript, and how can they be used?
Answer:
A proxy in JavaScript is an object that allows you to intercept and redefine fundamental
operations (e.g., property access). It is created using the Proxy constructor.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
148
const handler = {
get: function (target, property) {
return property in target ? target[property] : `Property ${property} not
found.`;
}
};
Proxies are useful for tasks like validation, logging, and managing property access.
SCENARIO QUESTIONS
41. Scenario: Creating multiple objects with similar properties and methods
using object literals
Question: How would you create multiple objects with the same structure using object
literals? What are the limitations of this approach?
Answer:
When using object literals, you can define properties and methods directly within {}. This
approach is simple and useful for creating a few objects. However, a major limitation is code
repetition—each object will have its own separate copy of the methods, which increases
memory usage. This approach also lacks scalability since maintaining many objects with
similar structures can be cumbersome.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
149
const person1 = {
name: "Alice",
age: 25,
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
};
const person2 = {
name: "Bob",
age: 30,
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
};
While this method works, the greet function is duplicated, which wastes memory.
Question: How can you use constructor functions to create multiple objects of the same type
and share methods efficiently?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
150
Answer:
Using constructor functions, you can efficiently create multiple objects of the same type by
defining shared methods on the prototype. This ensures that all instances created with the
constructor share the same method, thus saving memory. Constructors also make the code
reusable and maintainable.
For Example:
Person.prototype.greet = function () {
console.log(`Hello, my name is ${this.name}.`);
};
Resulting Table:
Both person1 and person2 share the same greet method, ensuring optimal memory usage.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
151
Question: How can one object inherit properties and methods from another using
prototypes?
Answer:
JavaScript allows objects to inherit behavior through prototypes. When a property or
method is not found in an object, JavaScript looks up the prototype chain to find it. The
Object.create() method allows you to create an object with a specified prototype,
facilitating code reuse and inheritance.
For Example:
const animalPrototype = {
speak() {
console.log(`${this.type} makes a sound.`);
}
};
In this example, the dog object inherits the speak method from animalPrototype. This
inheritance reduces redundancy by sharing functionality between objects.
Question: How would you implement inheritance between two classes using extends and
super?
Answer:
The extends keyword allows a class to inherit from another class, and super() is used to call
the parent class's constructor. This approach promotes code reuse and establishes
hierarchical relationships between objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
152
class Animal {
constructor(type) {
this.type = type;
}
sound() {
console.log(`${this.type} makes a sound.`);
}
}
sound() {
console.log(`${this.name} barks.`);
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
153
Question: How can you define a static method in a class, and what is its purpose?
Answer:
Static methods belong to the class itself and are not available on instances. They are typically
used for utility functions that do not depend on instance-specific data.
For Example:
class MathUtils {
static add(a, b) {
return a + b;
}
}
In this example, the add method can only be accessed via the class (MathUtils) and not
through any instance.
Question: How can you use getters and setters in JavaScript to control access to object
properties?
Answer:
Getters and setters allow you to encapsulate properties and add validation logic when
accessing or setting values. This ensures that properties are managed in a controlled manner.
For Example:
class Person {
constructor(name) {
this._name = name;
}
get name() {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
154
return this._name;
}
set name(newName) {
if (newName) {
this._name = newName;
}
}
}
Here, the private property _name is managed via the name getter and setter.
Question: What will be the value of this inside an object’s method, and how does it differ
from arrow functions?
Answer:
In regular methods, this refers to the object that invokes the method. In arrow functions,
this is inherited from the outer context.
For Example:
const person = {
name: "Alice",
greet: function () {
console.log(`Hi, I'm ${this.name}.`);
},
arrowGreet: () => {
console.log(`Hi, I'm ${this.name}.`);
}
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
155
Question: How can you use the call() method to invoke a function with a specific this
value?
Answer:
The call() method allows you to explicitly set the this value inside a function. This is useful
when reusing a function across multiple objects.
For Example:
function greet() {
console.log(`Hi, I'm ${this.name}.`);
}
Question: How can the bind() method be used to preserve the this value across function
calls?
Answer:
The bind() method returns a new function with this set to the provided value, ensuring the
correct context.
For Example:
const person = {
name: "Alice",
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
156
greet() {
console.log(`Hi, I'm ${this.name}.`);
}
};
Question: How does the lexical binding of this work in arrow functions, and how is it
different from regular functions?
Answer:
In arrow functions, this is inherited from the outer scope, making it consistent regardless of
where the function is invoked.
For Example:
function Person() {
this.name = "Alice";
this.greet = () => {
console.log(`Hi, I'm ${this.name}.`);
};
}
Arrow functions are particularly useful in cases where the outer this needs to be preserved.
Question: How can you use Object.create() to create a new object that inherits from
another object?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
157
Answer:
Object.create() allows you to create a new object with a specified prototype, enabling you
to inherit properties and methods from that prototype. This is a cleaner way to implement
prototypal inheritance without using constructor functions.
For Example:
const animal = {
speak() {
console.log(`${this.type} makes a sound.`);
}
};
Resulting Table:
Here, the dog object inherits the speak() method from animal.
Question: How can you add properties to an object after it has been created?
Answer:
You can dynamically add properties to an object by assigning values to new keys. This can
be useful when properties need to be added based on conditions or user input.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
158
Question: How can you check if an object has a specific property as its own property (and not
inherited)?
Answer:
The hasOwnProperty() method checks whether a property exists directly on the object, not
on its prototype chain.
For Example:
Answer:
You can override a prototype method by redefining it on the specific object or subclass.
For Example:
function Animal() {}
Animal.prototype.speak = function () {
console.log("Animal sound");
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
159
};
Answer:
Object.freeze() makes an object immutable, preventing new properties from being
added, existing properties from being changed, or properties from being deleted.
For Example:
Answer:
Object.seal() prevents new properties from being added or existing properties from being
deleted, but allows modifications to existing properties.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
160
Question: How can you create a shallow copy of an object using the spread operator?
Answer:
The spread operator ({...}) allows you to shallow copy an object. This means only the top-
level properties are copied, and nested objects remain as references.
For Example:
copy.name = "Bob";
copy.address.city = "Los Angeles";
Question: How does JavaScript compare objects, and why might two identical objects not be
equal?
Answer:
JavaScript compares objects by reference, not by value. Two objects with the same structure
and content are considered different if they reference different locations in memory.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
161
For Example:
Here, obj1 and obj2 are different objects, even though their content is identical.
Question: How can you use the instanceof operator to determine if an object is an instance
of a particular class?
Answer:
The instanceof operator checks if an object was created by a particular constructor or
inherits from it.
For Example:
class Animal {}
class Dog extends Animal {}
Question: How can you implement method chaining in JavaScript for cleaner code?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
162
Answer:
Method chaining allows multiple methods to be called in a single statement by returning the
object itself from each method.
For Example:
class Calculator {
constructor(value = 0) {
this.value = value;
}
add(num) {
this.value += num;
return this; // Returning the instance to allow chaining
}
subtract(num) {
this.value -= num;
return this;
}
multiply(num) {
this.value *= num;
return this;
}
}
Resulting Table:
Constructor Initialize 10
add(5) Add 5 15
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
163
subtract(2) Subtract 2 13
multiply(3) Multiply by 3 39
Here, each method returns the current instance (this), enabling the next method to be
called in the same statement.
Question: How can you create a deep copy of an object in JavaScript to avoid reference
issues?
Answer:
A deep copy duplicates all levels of an object, ensuring nested objects are also cloned, rather
than sharing references. Using structuredClone() is a simple way to achieve deep copying
without creating custom logic.
For Example:
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
164
This table shows that original and deepCopy are independent, confirming the success of
deep copying.
Question: How can you create a deep copy of an object that contains circular references?
Answer:
Handling circular references requires tracking visited objects during the deep copy. This
avoids infinite recursion and preserves object relationships.
For Example:
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
165
This shows that both obj and copiedObj maintain circular references correctly.
Question: How can a Proxy be used to add validation to object property access?
Answer:
A Proxy intercepts property operations, allowing validation or customization of behavior
when accessing or modifying properties.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
166
Resulting Table:
Property Value
name Alice
age 30
If invalid data (like "thirty") is set, the Proxy throws an error, ensuring correct types.
Question: How can you use mixins to combine behaviors into a class?
Answer:
Mixins allow multiple behaviors to be added to a class without inheritance, making the class
more flexible and reusable.
For Example:
const CanRun = {
run() {
console.log(`${this.name} is running.`);
}
};
const CanSwim = {
swim() {
console.log(`${this.name} is swimming.`);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
167
}
};
class Animal {
constructor(name) {
this.name = name;
}
}
Resulting Table:
The Animal class gains behaviors from both CanRun and CanSwim.
Question: How can you define an asynchronous method in a class to handle API calls?
Answer:
Asynchronous methods allow you to perform tasks like API calls without blocking the main
thread. These methods return a promise, and you can use await to wait for the result.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
168
class DataFetcher {
async fetchData(url) {
const response = await fetch(url);
const data = await response.json();
return data;
}
}
Property Value
id 1
completed false
Question: How can you make an object completely immutable using Object.freeze()?
Answer:
Object.freeze() prevents an object’s properties from being modified, ensuring
immutability. This is useful when you want to ensure data remains constant throughout the
program.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
169
Resulting Table:
Property Value
name Alice
age 25
Question: How would you extend a class to add new functionality without modifying the
original class?
Answer:
You can use inheritance to extend functionality by creating a subclass that adds or overrides
methods from the parent class.
For Example:
class Animal {
sound() {
console.log("Animal sound");
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
170
sound() {
console.log("Bark");
}
}
Resulting Table:
The Dog class extends the Animal class and overrides the sound() method.
Question: What is the difference between shallow and deep copies in JavaScript, and how
can it impact your code?
Answer:
A shallow copy only copies top-level properties, while a deep copy clones all nested objects.
Shallow copies can cause issues if the nested objects are modified.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
171
Resulting Table:
Question: How can you implement method overloading in JavaScript, given that it doesn’t
support it natively?
Answer:
You can simulate method overloading by using conditional checks on the number and type
of arguments.
For Example:
class Calculator {
calculate(a, b, operation = "add") {
if (operation === "add") return a + b;
if (operation === "subtract") return a - b;
throw new Error("Unsupported operation");
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
172
Question: How can you create private fields in a JavaScript class to prevent direct access?
Answer:
Private fields in JavaScript are declared with a # prefix and are only accessible within the
class.
For Example:
class BankAccount {
#balance;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
Resulting Table:
Operation Balance
Initial 100
Deposit 50 150
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
173
Question: How can you use Object.preventExtensions() to stop new properties from
being added to an object?
Answer:
Object.preventExtensions() prevents adding new properties to an object, but existing
properties can still be modified or deleted. This is useful when you want to limit an object’s
structure.
For Example:
Resulting Table:
Property Value
brand Honda
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
174
Answer:
Using Object.defineProperty(), you can set a property to be non-configurable, meaning it
cannot be deleted or reconfigured (though its value can still be modified unless made non-
writable).
For Example:
Resulting Table:
Question: How can you use a class method to return another function that preserves the
original this context?
Answer:
You can use arrow functions inside a class method to return another function, ensuring that
this refers to the class instance.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
175
For Example:
class Person {
constructor(name) {
this.name = name;
}
getGreeter() {
return () => {
console.log(`Hi, I'm ${this.name}.`);
};
}
}
Resulting Table:
Method Output
Question: How can you create dynamic class methods using computed property names?
Answer:
You can use computed property names to define methods dynamically within a class. This
allows methods to be named based on variables or expressions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
176
class Person {
[methodName]() {
console.log("Hello!");
}
}
Resulting Table:
sayHello Hello!
Question: How can you use call() to borrow a method from one object for another?
Answer:
call() allows you to borrow a method from one object and apply it to another by setting
the this context explicitly.
For Example:
function greet() {
console.log(`Hi, I'm ${this.name}.`);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
177
Resulting Table:
Question: How does the prototype chain work when there are multiple levels of inheritance?
Answer:
In JavaScript, objects inherit properties and methods through a prototype chain. If a
property is not found on the object, JavaScript looks up the chain until it reaches null.
For Example:
Resulting Table:
Object Properties
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
178
child age: 10
The child object inherits properties from both parent and grandparent.
77. Scenario: Using bind() to create a new function with fixed arguments
Question: How can you use bind() to create a new function with some pre-filled
arguments?
Answer:
bind() not only binds the this context but can also pre-fill arguments, creating a new
function with fixed parameters.
For Example:
function multiply(a, b) {
return a * b;
}
Resulting Table:
double 5 10
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
179
Question: How can you create multiple instances of a class that inherits properties from a
parent class?
Answer:
Using class-based inheritance, you can create multiple instances that share properties from
the parent class but have their own unique data.
For Example:
class Animal {
constructor(type) {
this.type = type;
}
}
Resulting Table:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
180
Question: How can you compare two objects to check if they have identical content?
Answer:
You can use JSON.stringify() to compare two objects by converting them to strings. This
method works well for shallow comparisons but might not handle circular references.
For Example:
Resulting Table:
Object Content
obj1 {"name":"Alice","age":25}
obj2 {"name":"Alice","age":25}
Question: How can you create a singleton in JavaScript using an IIFE (Immediately Invoked
Function Expression)?
Answer:
A singleton ensures only one instance of an object is created and reused throughout the
application. Using an IIFE, you can create and return a singleton instance.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
181
function createInstance() {
return { message: "I am the only instance" };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
Resulting Table:
Instance Message
Both instance1 and instance2 are the same, demonstrating the singleton pattern.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
182
THEORETICAL QUESTIONS
1. What are callbacks in JavaScript?
Answer:
A callback is a function that is passed as an argument to another function and is executed
once the parent function completes. Since JavaScript executes code line by line
(synchronously), callbacks allow you to handle asynchronous events such as making API calls,
reading files, or setting timers without blocking the main thread.
When the asynchronous operation is finished, the callback is invoked to perform follow-up
actions. This way, other code can continue running while waiting for the asynchronous task
to complete.
For Example:
function fetchData(callback) {
console.log('Fetching data...');
setTimeout(() => {
const data = { name: 'Shardul', age: 25 };
callback(data);
}, 2000); // Simulate a delay
}
fetchData((result) => {
console.log('Data received:', result);
});
Here, fetchData simulates an API request using setTimeout. The callback function is passed
as an argument and is invoked after the timeout completes. The result is only logged when
the data is available, demonstrating how callbacks work.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
183
Answer:
The primary drawback of callbacks is that they can lead to "callback hell" or "pyramid of
doom"—a situation where multiple callbacks are deeply nested, making the code difficult to
read, understand, and maintain. Each nested callback also introduces more complexity,
making error handling more challenging. As the number of nested callbacks increases, the
code becomes harder to manage, often causing bugs.
For Example:
getUserData((user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0], (details) => {
console.log(details);
});
});
});
In this example, multiple asynchronous operations are nested inside one another, making
the code hard to maintain.
Answer:
A promise is a JavaScript object used to handle asynchronous operations. It represents a
value that may be available in the future. A promise has three states:
Promises simplify handling asynchronous tasks and avoid the problem of callback hell by
allowing functions to return a promise, which can be handled using .then() for success and
.catch() for errors.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
184
For Example:
fetchData
.then((data) => console.log(data))
.catch((error) => console.error(error));
In this example, fetchData returns a promise. Once the asynchronous operation is complete,
it resolves, and the result is handled in the .then() block.
Answer:
Error handling in promises is done using the .catch() method. If any error occurs during the
promise execution, it gets propagated to the nearest .catch() block. Proper error handling
is crucial to ensure that exceptions don’t break the application flow and that users receive
appropriate feedback.
For Example:
fetchData
.then((data) => console.log(data))
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
185
In this case, if the operation fails, the reject function is called, and the error message is
handled by the .catch() method.
Answer:
Promise chaining allows you to perform a series of asynchronous operations in a sequence by
linking multiple .then() calls. Each .then() receives the result of the previous promise and
returns a new promise, enabling further operations to be chained. This structure makes the
code more readable and easier to manage compared to deeply nested callbacks.
For Example:
Here, each .then() modifies the result and passes it to the next step, demonstrating a clear
flow of logic.
Answer:
async functions are a way to write cleaner and more readable asynchronous code. An async
function always returns a promise, even if the function appears to return a value. The await
keyword can only be used inside async functions to pause the execution until a promise
resolves, making the code look more like synchronous code. This improves readability and
avoids callback hell or complex promise chains.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
186
For Example:
fetchData();
In this example, the await keyword ensures that the function waits for the promise to resolve
before logging the result.
Answer:
Errors in async functions are handled using try-catch blocks. If any error occurs during the
asynchronous operation, the control jumps to the catch block. This makes it easier to handle
errors compared to using .catch() with promises, especially in complex asynchronous flows.
For Example:
fetchData();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
187
Here, if the promise is rejected, the error is caught by the catch block and logged.
Answer:
The event loop is a mechanism in JavaScript that manages the execution of asynchronous
code. JavaScript is single-threaded, meaning only one operation can run at a time. The event
loop allows asynchronous tasks (like callbacks, promises, and timers) to be executed after the
synchronous code finishes. It ensures that the call stack is clear before processing tasks from
the event queue.
For Example:
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
console.log('End');
Here, setTimeout schedules the task to run later. The synchronous code runs first, followed
by the callback from the event loop.
Answer:
Microtasks are tasks that need to be executed as soon as the current synchronous code
completes. Examples include promise callbacks and MutationObserver callbacks.
Macrotasks are scheduled tasks such as setTimeout, setInterval, and DOM events.
Microtasks have higher priority than macrotasks and are executed before any macrotasks,
even if a macrotask was scheduled first.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
188
console.log('Start');
console.log('End');
Even though the macrotask was scheduled with setTimeout, the microtask runs first.
Answer:
JavaScript is called single-threaded because it executes code one line at a time in a single
thread. This means only one operation can run on the main thread at any given moment.
Even though JavaScript can handle asynchronous operations, those operations do not block
the main thread but are executed via callbacks, promises, or the event loop.
For Example:
console.log('Task 1');
setTimeout(() => console.log('Task 2'), 0);
console.log('Task 3');
The asynchronous task (Task 2) is added to the event queue and executed after the
synchronous code completes
Answer:
The setTimeout function is used to schedule a task to run after a specific delay (in
milliseconds). It allows developers to execute code asynchronously, meaning the scheduled
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
189
task will run only after all current synchronous operations are finished. The function takes two
parameters:
Using setTimeout is useful for creating animations, handling delayed events, or simulating
network operations without blocking the main thread.
For Example:
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
console.log('End');
Here, the setTimeout schedules the callback function to run after 2 seconds, while the
synchronous code continues to run without waiting.
Answer:
setInterval is similar to setTimeout, but instead of executing a function once after a delay,
it repeatedly runs the callback function at the specified interval. This makes setInterval
useful for tasks that need to occur periodically, like updating a clock or checking for new
data. The interval will continue until it is manually stopped using clearInterval.
For Example:
let count = 0;
const intervalId = setInterval(() => {
console.log(`Count: ${++count}`);
if (count === 5) clearInterval(intervalId); // Stop after 5 counts
}, 1000);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
190
Here, the function runs every second and prints the count. It stops when the count reaches 5,
using clearInterval.
Answer:
clearTimeout and clearInterval are used to stop a scheduled setTimeout or setInterval
task, respectively.
● clearTimeout: Prevents a function from running if the specified delay hasn’t finished
yet.
● clearInterval: Stops the repeated execution of a function initiated by setInterval.
For Example:
Here, the timeout is canceled using clearTimeout, so the message will never be logged.
Answer:
● Synchronous code: Executes line by line, blocking the next operation until the current
one completes. This can cause delays if a task takes time (like a network request).
● Asynchronous code: Allows other operations to run while waiting for a long task to
complete, improving performance and responsiveness. JavaScript achieves this using
callbacks, promises, and async/await.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
191
console.log('Start');
console.log('End');
Here, setTimeout is asynchronous, allowing the rest of the code to execute first.
Answer:
● .then() handles the success of a promise and receives the resolved value. It can be
chained to perform multiple operations sequentially.
● .catch() handles the rejection or error from a promise. It ensures that even if
something goes wrong, the error is caught and handled gracefully.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
192
Answer:
Yes, returning a promise from .then() allows you to chain multiple asynchronous
operations. Each .then() waits for the previous promise to resolve before running. This helps
in writing sequential code without callback hell.
For Example:
Here, the first .then() returns a promise, and the next .then() logs the result.
Answer:
JavaScript uses the event loop and task queues to manage multiple asynchronous
operations. When an asynchronous operation (like a setTimeout or a promise) is completed,
its callback is added to the task queue. The event loop checks the task queue and moves
tasks to the call stack when the stack is empty, ensuring non-blocking execution.
● Microtasks (like promises) have higher priority than macrotasks (like setTimeout).
Answer:
If a promise is rejected and no .catch() block is provided, JavaScript raises an
UnhandledPromiseRejectionWarning. In production, unhandled rejections can crash
applications or cause unpredictable behavior, so it is important to handle all rejections
properly.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
193
Answer:
Yes, callback-based functions can be converted to promise-based ones using the Promise
constructor. This process is called promisification. Promises offer better error handling and
cleaner code structure compared to callbacks.
For Example:
function fetchData(callback) {
setTimeout(() => callback('Data fetched'), 1000);
}
function fetchDataPromise() {
return new Promise((resolve) => {
fetchData(resolve);
});
}
Answer:
The async/await syntax makes asynchronous code look and behave more like synchronous
code, improving readability and maintainability. Instead of chaining .then() methods, you
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
194
can write linear code using await. This also simplifies error handling by using try-catch
blocks.
For Example:
fetchData();
Here, the await keyword pauses the function until the promise resolves, avoiding the need
for .then() and making the code more readable.
21. How does the event loop prioritize microtasks and macrotasks?
Answer:
The event loop manages the execution of tasks in JavaScript, ensuring that asynchronous
code executes properly. Tasks are divided into microtasks and macrotasks:
The event loop ensures microtasks are processed immediately to avoid delays, which is why
promises often execute sooner than setTimeout callbacks.
For Example:
console.log('Start');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
195
console.log('End');
Here, although both tasks are scheduled to execute immediately, the microtask (promise)
runs before the macrotask (setTimeout).
Answer:
Both process.nextTick() and setImmediate() are used for scheduling tasks in Node.js, but
they differ in timing:
For Example:
console.log('Start');
console.log('End');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
196
Here, the process.nextTick() callback runs before setImmediate() due to its higher
priority.
Answer:
A stack overflow occurs when too many function calls are made without returning,
exceeding the call stack's capacity. This can happen in cases of infinite recursion or deep
function nesting. JavaScript throws a RangeError when the call stack limit is reached.
For Example:
function recurse() {
recurse(); // Infinite recursion without a base case
}
To prevent stack overflow, ensure recursive functions have a base case to terminate the
recursion.
Answer:
These promise methods help manage multiple asynchronous operations:
● Promise.all: Resolves when all promises are fulfilled; rejects if any promise is
rejected.
● Promise.race: Resolves or rejects as soon as the first promise settles.
● Promise.any: Resolves if at least one promise resolves; rejects only if all promises
reject.
● Promise.allSettled: Returns an array with the results of all promises, regardless of
whether they resolve or reject.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
197
const p1 = Promise.resolve(1);
const p2 = new Promise((_, reject) => setTimeout(() => reject('Error'), 100));
const p3 = Promise.resolve(3);
Answer:
For Example:
(async () => {
for await (const value of asyncGenerator()) {
console.log(value);
}
})();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
198
Answer:
JavaScript doesn’t natively support promise cancellation, but you can achieve it using
AbortController. This allows you to abort ongoing operations, like a network request.
For Example:
fetchData
.then((data) => console.log(data))
.catch((error) => console.error(error)); // Logs: Aborted
Answer:
Backpressure occurs when a readable stream produces data faster than a writable stream
can consume. This leads to a buildup of data in memory, which can degrade performance or
cause memory leaks. To handle backpressure, streams can slow down or pause the flow of
data until the writable stream is ready to consume more.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
199
Answer:
queueMicrotask() is used to schedule a microtask, ensuring it runs after the current
synchronous code completes but before the next event loop iteration. It behaves similarly to
the .then() method in promises.
For Example:
console.log('Start');
Answer:
To execute promises in sequence, avoid Promise.all (which runs promises in parallel).
Instead, use async/await or chain .then() methods to ensure one promise resolves before
starting the next.
For Example:
sequentialTasks();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
200
Answer:
JavaScript is single-threaded, meaning it can only execute one task at a time on the main
thread. However, it achieves concurrency through non-blocking I/O operations and the
event loop. Asynchronous operations (e.g., API calls) run in the background, and their results
are handled later via callbacks or promises, ensuring the main thread remains responsive.
This allows JavaScript to manage multiple tasks efficiently without needing multiple threads,
avoiding common concurrency issues like race conditions.
Answer:
Promise.all is designed to handle multiple promises in parallel and returns an array of
resolved values if all the promises succeed. If any promise fails, it rejects immediately. It also
behaves predictably with non-promise values and empty arrays:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
201
Answer:
A race condition happens when multiple asynchronous operations are executed
simultaneously, but the program’s correctness depends on their order. If they complete in an
unexpected order, the outcome may be wrong or inconsistent, causing bugs. This problem
occurs often when accessing shared data without control over the sequence of operations.
For Example:
Promise.resolve('Settings').then((settings) => {
data.settings = settings;
});
In this example, the timing of the two promises is unpredictable, which may result in an
incomplete state when console.log(data) runs. Using Promise.all() or async/await
ensures the operations complete in a controlled manner.
Answer:
Promise.race returns the result of the first settled promise, regardless of whether it resolves
or rejects. If the first promise to settle is rejected, Promise.race rejects immediately with that
reason, even if other promises could still resolve later.
For Example:
Promise.race([p1, p2])
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
202
34. What are "zombie promises," and how do you avoid them?
Answer:
A zombie promise refers to a promise that continues to run even though its result is no
longer needed. This can happen if the promise was initiated but the user navigates away
from the page, or the operation is no longer relevant. Zombie promises consume resources
and may cause unintended behavior.
For Example:
fetchData
.then((data) => console.log(data))
.catch((error) => console.error(error)); // Logs: Aborted
Here, the promise is canceled before completion using AbortController, preventing zombie
promises.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
203
Answer:
For Example:
Answer:
Promise.allSettled waits for all promises to settle (either resolve or reject) and returns an
array of objects representing the outcomes. It provides both successful results and errors,
ensuring that all promises are accounted for, even if some fail.
For Example:
const promises = [
Promise.resolve('Task 1'),
Promise.reject('Task 2 failed'),
Promise.resolve('Task 3'),
];
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
204
This method ensures all results are logged, regardless of success or failure.
Answer:
Symbol.asyncIterator allows an object to define how it will be asynchronously iterated
using for await...of. It’s useful when working with data streams or APIs that provide data
over time.
For Example:
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield 'First';
yield new Promise((resolve) => setTimeout(() => resolve('Second'), 1000));
},
};
(async () => {
for await (const value of asyncIterable) {
console.log(value);
}
})();
Answer:
Using try-catch with async/await ensures that errors in asynchronous operations are
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
205
caught and handled properly. Each asynchronous operation can be wrapped in its own try-
catch block, or a single block can manage multiple operations.
For Example:
Here, the error in the second promise is caught by the catch block.
Answer:
Using await with Promise.all() allows multiple asynchronous operations to run in parallel
and waits for all of them to complete before proceeding. This improves performance when
tasks are independent.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
206
fetchAllData();
Here, both promises are executed simultaneously, making the code more efficient.
Answer:
Retry logic is helpful when dealing with unreliable operations like API calls. You can
implement retries using recursion or loops. If an operation fails, it retries a specified number
of times before giving up.
For Example:
retryOperation(fetchData, 3);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
207
SCENARIO QUESTIONS
41.
Scenario:
You want to display a loading message while fetching data from an API. Once the data is
fetched, the loading message should disappear, and the data should be displayed.
Question:
How can you achieve this using a callback function to handle asynchronous behavior?
Answer:
A callback function helps execute code after an asynchronous operation completes. In this
scenario, the fetchData function simulates a network request using setTimeout. The loading
message is displayed immediately when the function is called, and once the data is fetched,
the callback function is triggered to display the data and remove the loading message. This is
a typical example of how callbacks help manage sequential actions for asynchronous
operations.
For Example:
function fetchData(callback) {
console.log('Loading...');
setTimeout(() => {
const data = { user: 'Shardul', age: 25 };
callback(data);
}, 2000);
}
function displayData(data) {
console.log('Data:', data);
console.log('Loading complete!');
}
fetchData(displayData);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
208
Output:
Loading...
Data: { user: 'Shardul', age: 25 }
Loading complete!
Here, the fetchData function calls the displayData function only after the data is available.
42.
Scenario:
You are building an online store and need to ensure that product details are loaded before
processing the user’s order.
Question:
How can promises be used to fetch product details and handle both success and error
scenarios?
Answer:
A promise is a more organized way to manage asynchronous code. Instead of passing
callbacks, a promise returns a future result, which can be handled using .then() for success
and .catch() for failure. In this scenario, the fetchProduct function returns a promise that
simulates fetching product data. If the product is successfully fetched, the result is logged. If
the operation fails, the error is handled in the .catch() block.
For Example:
function fetchProduct(productId) {
return new Promise((resolve, reject) => {
const success = true; // Simulate success or failure
setTimeout(() => {
if (success) resolve({ id: productId, name: 'Laptop' });
else reject('Failed to fetch product details');
}, 1000);
});
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
209
fetchProduct(101)
.then((product) => console.log('Product:', product))
.catch((error) => console.error('Error:', error));
Here, the product details are only logged if the promise resolves successfully, ensuring the
order is processed with the correct data.
43.
Scenario:
You need to fetch user details from an API and then fetch the user’s orders based on the
retrieved user ID.
Question:
How can you chain promises to perform these tasks sequentially?
Answer:
Promise chaining ensures that tasks are performed in sequence by returning one promise
from a .then() method and passing its result to the next. In this scenario, the fetchUser
promise resolves with user data, and the fetchOrders promise resolves with orders using the
user’s ID. Each .then() step depends on the result of the previous one.
For Example:
function fetchUser() {
return new Promise((resolve) => {
setTimeout(() => resolve({ id: 1, name: 'Shardul' }), 1000);
});
}
function fetchOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve(['Order 1', 'Order 2']), 1000);
});
}
fetchUser()
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
210
.then((user) => {
console.log('User:', user);
return fetchOrders(user.id);
})
.then((orders) => console.log('Orders:', orders))
.catch((error) => console.error('Error:', error));
Here, the orders are fetched only after the user details are retrieved, maintaining the correct
sequence.
44.
Scenario:
You want to fetch user data from an API using modern async/await syntax to improve code
readability.
Question:
How can you use async/await to fetch and display user data?
Answer:
The async/await syntax simplifies asynchronous code by making it look like synchronous
code. async functions return a promise, and the await keyword pauses the execution until
the promise resolves. This improves code readability and makes it easier to handle errors with
try-catch.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
211
fetchUser();
Here, await ensures that the data is logged only after the promise resolves.
45.
Scenario:
Your web page must display a message if an API call fails to load data within 3 seconds.
Question:
How can you implement a timeout using promises to handle this situation?
Answer:
Using Promise.race, you can race two promises: one representing the API call and another
representing a timeout. If the timeout finishes first, it rejects with a timeout error.
For Example:
function fetchData() {
return new Promise((resolve) => setTimeout(() => resolve('Data Loaded'), 5000));
}
function timeout(ms) {
return new Promise((_, reject) => setTimeout(() => reject('Timeout!'), ms));
}
Promise.race([fetchData(), timeout(3000)])
.then((data) => console.log(data))
.catch((error) => console.error(error));
If the fetch takes longer than 3 seconds, the timeout message is displayed.
46.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
212
Scenario:
You have multiple API calls to make, and you want all of them to execute in parallel.
Question:
How can you use Promise.all to run multiple API calls in parallel and wait for all of them to
complete?
Answer:
Promise.all allows multiple promises to run in parallel. It waits for all promises to resolve
and returns their results in an array. If any promise rejects, Promise.all returns the rejection.
For Example:
Promise.all([apiCall1, apiCall2])
.then((results) => console.log('Results:', results))
.catch((error) => console.error('Error:', error));
47.
Scenario:
You need to cancel a fetch request if the user navigates away from the page.
Question:
How can you implement cancellation using AbortController?
Answer:
AbortController allows you to cancel ongoing fetch requests. The signal from
AbortController is passed to the fetch request, enabling cancellation.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
213
fetch('https://jsonplaceholder.typicode.com/posts', { signal })
.then((response) => response.json())
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Fetch aborted:', error));
48.
Scenario:
You need to ensure that even if one API call fails, the other results are still processed.
Question:
How can you use Promise.allSettled to handle both resolved and rejected promises?
Answer:
Promise.allSettled waits for all promises to settle and returns their results, whether they
resolve or reject.
For Example:
49.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
214
Scenario:
You want to retry an API call up to 3 times if it fails.
Question:
How can you implement retry logic using async/await?
Answer:
Retry logic ensures that operations are retried until they succeed or reach a limit.
For Example:
fetchWithRetry('https://jsonplaceholder.typicode.com/posts', 3)
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Error:', error));
50.
Scenario:
You need to delay the execution of a function by 2 seconds.
Question:
How can you create a reusable delay function using promises?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
215
Answer:
A delay function can be created using promises to pause execution for a specified time.
For Example:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
executeTask();
51.
Scenario:
You need to log the results of multiple promises one by one, in the order they resolve, even
though they are executed in parallel.
Question:
How can you ensure promises run in parallel but log their results sequentially?
Answer:
When multiple promises are executed in parallel using Promise.all, their results are
collected in the same order as the promises were defined, even if they resolve at different
times. Using a for...of loop ensures the results are logged sequentially. This pattern allows
the code to take advantage of parallel execution while maintaining order in the output.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
216
const promises = [
new Promise((resolve) => setTimeout(() => resolve('Result 1'), 2000)),
new Promise((resolve) => setTimeout(() => resolve('Result 2'), 1000)),
new Promise((resolve) => setTimeout(() => resolve('Result 3'), 3000)),
];
logResultsInOrder();
In this example, the promises run in parallel, but the results are logged in order: Result 1,
Result 2, and Result 3.
52.
Scenario:
You want to execute multiple asynchronous functions one after the other, ensuring each one
finishes before the next starts.
Question:
How can you execute asynchronous functions sequentially using async/await?
Answer:
Using async/await ensures that each asynchronous function completes before the next one
starts. Even though asynchronous functions usually run in parallel, using await inside a loop
forces them to execute sequentially.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
217
runTasks();
Here, the execution is strictly sequential: Task 1 completes before Task 2 starts.
53.
Scenario:
You want to catch and handle errors occurring in a specific part of an async function while
allowing other operations to continue.
Question:
How can you handle errors in specific sections of an async function using try-catch?
Answer:
Wrapping different sections of an async function in individual try-catch blocks ensures
errors are handled separately. This allows the function to continue executing even if one
section fails, improving fault tolerance.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
218
try {
const result2 = await Promise.reject('Task 2 Failed');
console.log(result2); // Won't execute
} catch (error) {
console.error('Error in Task 2:', error);
}
}
performTasks();
Here, if Task 2 fails, the error is handled, and the function continues without interruption.
54.
Scenario:
You need to ensure that a function is executed only after a user stops typing for a certain
period.
Question:
How can you implement a debounce function in JavaScript?
Answer:
A debounce function delays the execution of a function until after a specified period of
inactivity. This is useful in scenarios like search input fields, where you want to wait until the
user stops typing before sending a request.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
219
log(); // Call multiple times, only the last one executes after 1 second
log();
log();
The debounce ensures only the final call executes after a 1-second delay.
55.
Scenario:
You want to limit how often a function can run during continuous events like scrolling.
Question:
How can you implement a throttle function in JavaScript?
Answer:
A throttle function ensures a function executes only once within a specified time period,
even if called multiple times. It’s useful for optimizing heavy operations triggered by
continuous events like scrolls or mouse movements.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
220
This ensures the function runs at most once every 1 second, even if called frequently.
56.
Scenario:
You need to implement a countdown timer that decreases every second until it reaches zero.
Question:
How can you create a countdown timer using setInterval?
Answer:
setInterval repeatedly executes a function at regular intervals. In this scenario, the timer
decreases every second and stops when it reaches zero using clearInterval.
For Example:
function countdown(seconds) {
const interval = setInterval(() => {
console.log(seconds);
seconds--;
if (seconds < 0) {
clearInterval(interval); // Stop the timer
console.log('Countdown complete!');
}
}, 1000);
}
countdown(5);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
221
57.
Scenario:
You want to run code only after the entire DOM content has loaded.
Question:
How can you detect when the DOM is fully loaded in JavaScript?
Answer:
The DOMContentLoaded event triggers when the HTML document is completely loaded and
parsed, without waiting for stylesheets, images, or other resources to load.
For Example:
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM fully loaded and parsed');
});
This ensures your script runs only after the DOM is ready.
58.
Scenario:
You need to execute a function only once, even if it’s called multiple times.
Question:
How can you implement a function that executes only once using JavaScript?
Answer:
You can use closures to create a function that runs only once. After the first execution, the
function remembers that it has already run and ignores subsequent calls.
For Example:
function once(func) {
let executed = false;
return function (...args) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
222
if (!executed) {
executed = true;
func(...args);
}
};
}
logOnce(); // Executed!
logOnce(); // Ignored
59.
Scenario:
You want to delay each iteration in a loop by a specific amount of time.
Question:
How can you create a delay between loop iterations using async/await?
Answer:
Using async/await with a delay function allows you to pause between iterations in a loop.
For Example:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
223
delayedLoop();
60.
Scenario:
You want to repeatedly execute a function, ensuring each execution completes before
starting the next one.
Question:
How can you implement a polling function that ensures sequential execution?
Answer:
Using async/await, you can ensure that each polling cycle completes before the next one
starts. This avoids overlapping operations.
For Example:
The polling function logs "Polling..." every 2 seconds, ensuring each execution is sequential.
61.
Scenario:
You need to run multiple asynchronous tasks, but you only want to proceed if a specific
number of them succeed, regardless of the rest.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
224
Question:
How can you implement a logic where you proceed after a minimum number of promises
resolve, using Promise.any?
Answer:
Promise.any resolves with the first fulfilled promise from a list. If all promises are rejected, it
throws an AggregateError. This method is useful when you need at least one success to
continue, ignoring the rest. If no promises succeed, it catches all errors at once.
For Example:
Explanation:
● Promise.any ignores p1 since it fails and returns the first successful promise: p2.
● If all promises reject, it throws an AggregateError.
62.
Scenario:
You need to control how often a function is called to prevent it from being called too
frequently or too slowly.
Question:
How can you combine debounce and throttle to optimize function calls?
Answer:
Debounce delays function execution until a period of inactivity, while throttle ensures a
function runs only once within a given time. Combining them provides smooth control over
frequent events, balancing performance and responsiveness.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
225
// Simulate calls
optimizedFunc();
setTimeout(optimizedFunc, 300); // Delayed call
setTimeout(optimizedFunc, 1200); // Executes after throttle limit
Explanation:
This hybrid function optimizes frequent calls by:
63.
Scenario:
You need to retry an API request, with a longer wait time between retries on each failure.
Question:
How can you implement exponential backoff using async/await?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
226
Answer:
Exponential backoff increases the delay after each failed attempt. It is commonly used in
APIs to avoid overloading servers. The retry delay increases exponentially using 2^i logic.
For Example:
fetchWithRetry('https://jsonplaceholder.typicode.com/posts', 3, 1000)
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Error:', error));
Explanation:
64.
Scenario:
You need to run some tasks sequentially while others should run in parallel to improve
performance.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
227
Question:
How can you combine sequential and parallel tasks using async/await?
Answer:
Use await for sequential operations and Promise.all for parallel tasks. This hybrid approach
helps where certain tasks depend on others, while independent tasks can run in parallel.
For Example:
console.log('Posts:', posts.length);
console.log('Comments:', comments.length);
}
runTasks();
Explanation:
65.
Scenario:
You need to execute a function at regular intervals but ensure each execution completes
before starting the next.
Question:
How can you create a sequential polling system using async/await?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
228
Answer:
A sequential polling system ensures no new polling operation starts until the previous one
completes.
For Example:
Explanation:
The polling operation ensures that each iteration waits for the previous one to finish before
starting the next.
66.
Scenario:
You need to cancel a task if it takes too long to complete.
Question:
How can you implement a timeout mechanism to cancel an async task?
Answer:
Using Promise.race, you can cancel an asynchronous task if it exceeds the allowed time.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
229
try {
return await Promise.race([fetchPromise, timeoutPromise]);
} catch (error) {
controller.abort(); // Cancel fetch if timeout occurs
throw error;
}
}
fetchWithTimeout('https://jsonplaceholder.typicode.com/posts', 1000)
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Error:', error));
Explanation:
If the fetch exceeds the timeout, the controller cancels it.
67.
Scenario:
You need to process tasks one by one and dynamically add tasks to the queue.
Question:
How can you implement a task queue using JavaScript?
Answer:
A task queue processes tasks sequentially, and tasks can be added dynamically during
runtime.
For Example:
class TaskQueue {
constructor() {
this.queue = [];
this.processing = false;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
230
addTask(task) {
this.queue.push(task);
this.processTasks();
}
async processTasks() {
if (this.processing) return;
this.processing = true;
while (this.queue.length) {
const task = this.queue.shift();
await task();
}
this.processing = false;
}
}
Explanation:
68.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
231
Scenario:
You need to pause and resume an asynchronous operation based on user input.
Question:
How can you implement pause and resume functionality for an asynchronous task?
Answer:
Use a flag to control whether the task should run or pause. This allows dynamic control over
execution.
For Example:
function pause() {
paused = true;
}
function resume() {
paused = false;
}
runTask();
setTimeout(pause, 3000); // Pause after 3 seconds
setTimeout(resume, 6000); // Resume after 6 seconds
Explanation:
The task pauses and resumes dynamically based on user input.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
232
69.
Scenario:
You need to process multiple tasks but limit how many run concurrently.
Question:
How can you implement a concurrency limiter in JavaScript?
Answer:
A concurrency limiter ensures only a limited number of tasks run at a time.
For Example:
limiter(tasks, 2);
Explanation:
At most 2 tasks run at a time, improving control over resource usage.
70.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
233
Scenario:
You need to buffer incoming data streams and process them periodically.
Question:
How can you implement buffering for data streams in JavaScript?
Answer:
Buffering collects data and processes it at intervals to optimize performance.
For Example:
function addToBuffer(data) {
buffer.push(data);
}
setInterval(() => {
if (buffer.length) {
console.log('Processing buffer:', buffer);
buffer = [];
}
}, 3000);
addToBuffer('Data 1');
addToBuffer('Data 2');
Explanation:
The buffer processes accumulated data every 3 seconds.
71.
Scenario:
You need to ensure that a set of asynchronous operations runs in sequence, but each
operation depends on the result of the previous one.
Question:
How can you create a dependent promise chain using async/await to ensure sequential
execution?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
234
Answer:
In situations where each asynchronous operation depends on the previous one, using
async/await ensures clear, readable code that executes sequentially. Each operation awaits
the result of the previous one, avoiding nested promise chains.
For Example:
runDependentTasks();
Explanation:
72.
Scenario:
You want to dynamically adjust the delay between two polling cycles based on the response
time or server load.
Question:
How can you implement adaptive polling using async/await?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
235
Answer:
Adaptive polling adjusts the delay between polling cycles based on factors like response
time or server load. This ensures optimal performance and avoids unnecessary network
requests.
For Example:
adaptivePoll(
() => fetch('https://jsonplaceholder.typicode.com/posts').then((res) =>
res.json()),
1000,
5000
);
Explanation:
73.
Scenario:
You need to cache API responses to avoid redundant network calls.
Question:
How can you implement a simple in-memory cache using JavaScript to store API results?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
236
Answer:
A cache stores API results to reduce redundant requests and improve performance. You can
use an in-memory cache with an expiration mechanism to keep the data fresh.
For Example:
fetchWithCache('https://jsonplaceholder.typicode.com/posts');
Explanation:
74.
Scenario:
You want to run multiple tasks in parallel, but stop all tasks immediately if one of them fails.
Question:
How can you abort parallel tasks upon the first failure using Promise.all?
Answer:
Promise.all runs tasks in parallel and rejects immediately if any task fails. This is useful
when the result depends on all tasks succeeding.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
237
For Example:
const task1 = () => new Promise((resolve) => setTimeout(resolve, 1000, 'Task 1'));
const task2 = () => new Promise((_, reject) => setTimeout(reject, 500, 'Task 2
failed'));
const task3 = () => new Promise((resolve) => setTimeout(resolve, 1500, 'Task 3'));
Explanation:
● If task2 fails, all tasks are aborted, and the error is logged.
75.
Scenario:
You need to periodically refresh the cache to ensure your data stays up to date.
Question:
How can you implement automatic cache invalidation using setInterval?
Answer:
Use setInterval to periodically clear outdated cache entries, ensuring your data remains
accurate and fresh.
For Example:
function invalidateCache() {
for (const key in cache) {
if (Date.now() - cache[key].timestamp > 30000) {
console.log(`Invalidating cache for: ${key}`);
delete cache[key];
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
238
}
}
Explanation:
76.
Scenario:
You need to rate-limit API requests to avoid overloading the server.
Question:
How can you implement a rate limiter using JavaScript?
Answer:
A rate limiter ensures only a limited number of requests are made within a specified time
frame.
For Example:
class RateLimiter {
constructor(limit, interval) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
239
this.limit = limit;
this.interval = interval;
this.requests = 0;
this.queue = [];
}
scheduleRequest(requestFn) {
if (this.requests < this.limit) {
this.requests++;
requestFn().finally(() => this.reset());
} else {
this.queue.push(requestFn);
}
}
reset() {
setTimeout(() => {
this.requests--;
if (this.queue.length) this.scheduleRequest(this.queue.shift());
}, this.interval);
}
}
limiter.scheduleRequest(request);
limiter.scheduleRequest(request);
limiter.scheduleRequest(request);
Explanation:
77.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
240
Scenario:
You need to detect memory leaks caused by uncollected promises.
Question:
How can you track unresolved promises to prevent memory leaks?
Answer:
You can track unresolved promises by keeping references to them and checking
periodically if they are resolved.
For Example:
function trackPromise(promise) {
unresolvedPromises.add(promise);
promise.finally(() => unresolvedPromises.delete(promise));
}
setInterval(() => {
console.log(`Unresolved promises: ${unresolvedPromises.size}`);
}, 5000);
// Simulate promises
const p1 = new Promise((resolve) => setTimeout(resolve, 1000));
const p2 = new Promise((resolve) => setTimeout(resolve, 5000));
trackPromise(p1);
trackPromise(p2);
Explanation:
78.
Scenario:
You need to batch API requests and send them together to optimize performance.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
241
Question:
How can you implement request batching using JavaScript?
Answer:
Batching groups multiple requests and sends them together to reduce overhead and
improve performance.
For Example:
function addToBatch(request) {
batch.push(request);
}
function sendBatch() {
console.log('Sending batch:', batch);
batch = [];
}
setInterval(() => {
if (batch.length) sendBatch();
}, 3000); // Send batch every 3 seconds
addToBatch('Request 1');
addToBatch('Request 2');
Explanation:
79.
Scenario:
You need to process a large data stream without blocking the main thread.
Question:
How can you use Web Workers to process data in the background?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
242
Answer:
Web Workers allow you to run JavaScript code in the background thread to prevent
blocking the main UI.
For Example:
// worker.js
self.onmessage = function (e) {
const result = e.data * 2;
postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(10);
Explanation:
80.
Scenario:
You need to retry failed tasks with delays, but only for certain error types.
Question:
How can you implement conditional retries based on error types?
Answer:
Conditional retries allow you to retry tasks only for specific error conditions, improving
efficiency.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
243
// worker.js
self.onmessage = function (e) {
const result = e.data * 2;
postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(10);
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
244
Answer: In JavaScript, errors are generally categorized into three main types: syntax errors,
runtime errors, and logical errors.
● Syntax Errors occur when the code is not written correctly according to JavaScript
syntax rules. These errors prevent the script from running at all. For instance, missing
parentheses or an unexpected token can cause syntax errors.
● Runtime Errors are errors that occur while the code is running. Even though the
syntax is correct, something unexpected happens during execution, such as trying to
access a variable that doesn't exist.
● Logical Errors are the hardest to detect. These occur when the code runs without any
issues, but it doesn’t produce the expected output due to a mistake in the logic.
For Example:
let x = 10;
let y = 20;
console.log(x * y); // This will work fine.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
245
For Example:
try {
let result = someFunction(); // Assume someFunction is not defined
} catch (error) {
console.error("An error occurred: ", error.message);
} finally {
console.log("This will always run, error or not.");
}
Answer: A syntax error occurs when the JavaScript code doesn't follow proper syntax. These
errors prevent the script from being executed at all. Syntax errors are generally caught by the
JavaScript engine during the parsing phase before the code is executed. Common syntax
errors include missing brackets, unclosed strings, or improper keyword usage.
For Example:
In this example, the missing closing parenthesis will cause a syntax error, and the script won’t
run until it's corrected.
Answer: A runtime error occurs when the JavaScript code is syntactically correct but fails to
execute properly. This type of error occurs after the program has started running. Examples
of runtime errors include trying to access variables that are not defined or trying to call a
function that doesn't exist.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
246
try {
let result = someFunction(); // someFunction is not defined
} catch (error) {
console.log("Caught an error:", error.message);
}
In this case, the code tries to execute a function that is not defined, resulting in a runtime
error. The error is caught in the catch block.
Answer: A logical error in JavaScript occurs when the code runs without generating any
errors, but it doesn't produce the correct or expected output. These errors are due to flaws in
the logic of the code, and they are difficult to catch because the code doesn't actually "break."
For Example:
In this case, the code is supposed to calculate the area of a rectangle, but due to a logical
error (using addition instead of multiplication), it returns the wrong result.
Answer: A try-catch block in JavaScript is used to handle exceptions that may occur during
the execution of code. The try block contains code that might throw an error, and the catch
block contains code to handle that error if it occurs. Optionally, you can add a finally block,
which will execute regardless of whether an error occurs or not.
For Example:
try {
let result = divideNumbers(10, 0);
} catch (error) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
247
function divideNumbers(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
In this case, the try block attempts to divide numbers, and if a division by zero occurs, an
error is thrown and caught in the catch block.
Answer: You can manually throw an error in JavaScript using the throw statement. This
allows you to create custom errors based on specific conditions in your code. When an error
is thrown, it interrupts the normal flow of execution and transfers control to the nearest
catch block, where you can handle the error.
For Example:
function checkAge(age) {
if (age < 18) {
throw new Error("You must be at least 18 years old");
}
console.log("Access granted");
}
try {
checkAge(15);
} catch (error) {
console.error(error.message);
}
In this example, the throw statement is used to manually create an error when the age is less
than 18. This error is then caught and handled in the catch block.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
248
8. What are custom errors in JavaScript, and how can they be created?
Answer: Custom errors in JavaScript are user-defined errors that you can throw based on
specific conditions in your code. JavaScript provides built-in error types like TypeError and
RangeError, but you can also create your own custom error types by extending the built-in
Error object.
For Example:
try {
throw new CustomError("This is a custom error");
} catch (error) {
console.error(`${error.name}: ${error.message}`);
}
In this example, a new CustomError class is created by extending the built-in Error class. The
custom error can then be thrown and caught just like any other error.
9. What are browser developer tools, and how are they used for debugging?
Answer: Browser developer tools are built-in features in modern web browsers that help
developers inspect, debug, and optimize their code. These tools provide a variety of
debugging features like inspecting the Document Object Model (DOM), analyzing network
requests, viewing console logs, and stepping through JavaScript code.
● You can open developer tools by pressing F12 or right-clicking on the page and
selecting "Inspect."
● Navigate to the "Console" tab to view logs, errors, and other debug messages.
● Use the "Sources" tab to set breakpoints in your JavaScript code and step through the
execution.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
249
These tools are invaluable for debugging issues in real-time, allowing developers to see how
their code behaves during execution.
10. What are the console methods available in JavaScript for debugging?
For Example:
These methods help track different types of information during development and make
debugging more efficient.
Answer:
The try-catch block helps in handling errors that occur during code execution, while the
finally block is used for code that must execute regardless of whether an error occurs. If no
error is thrown, the catch block is skipped, but the finally block still executes. This ensures
that essential cleanup tasks (like closing resources) are completed.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
250
For Example:
try {
let result = someOperation(); // Assume someOperation is a function
} catch (error) {
console.error("Error occurred:", error.message);
} finally {
console.log("This will always execute.");
}
The above code demonstrates how finally ensures critical code executes even if there is an
error.
Answer:
The catch block only accepts a single parameter (an error object). Inside the catch, the
error object provides detailed information about the exception, including properties like name
(type of error) and message (error message). If you need to handle multiple details, you
extract them from this single error object.
For Example:
try {
throw new ReferenceError("This is a reference error");
} catch (error) {
console.log(`Error Type: ${error.name}`); // "ReferenceError"
console.log(`Error Message: ${error.message}`); // "This is a reference error"
}
Here, even though the catch only has one parameter, you can extract multiple properties
from the error.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
251
Answer:
If a try block does not have a catch block and an error occurs, the script will throw the error
and terminate the execution unless the error is handled at some higher level. However, if you
only need to perform cleanup operations, you can omit the catch block and use a finally
block to run code regardless of success or failure.
For Example:
try {
someUndefinedFunction(); // This will throw a ReferenceError
} finally {
console.log("Cleanup executed.");
}
// Error is not caught, so the program will stop after this block.
This example shows how an error causes the script to stop, but the finally block still runs
before termination.
Answer:
Yes, JavaScript allows nested try-catch blocks. This means you can place one try-catch
block inside another. This is useful when different errors need to be handled separately, or
when an error in the inner block must be handled differently from an error in the outer block.
For Example:
try {
try {
throw new Error("Inner error");
} catch (innerError) {
console.log("Handled inner error:", innerError.message);
}
throw new Error("Outer error");
} catch (outerError) {
console.log("Handled outer error:", outerError.message);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
252
This example demonstrates how the inner and outer try-catch blocks handle their
respective errors independently.
Answer:
The finally block ensures that certain code always executes, no matter what happens in
the try-catch block. This is especially useful for cleanup operations—for example, closing
files, releasing database connections, or stopping loading indicators after an API call.
For Example:
try {
let data = fetchData(); // Assume fetchData makes an API call
} catch (error) {
console.error("API call failed:", error.message);
} finally {
console.log("This will run, regardless of API success or failure.");
}
Even if the fetchData function throws an error, the code inside finally will still run.
Answer:
JavaScript provides several built-in error types to represent different kinds of errors:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
253
● RangeError: Thrown when a value is outside the allowable range (e.g., too large an
array size).
For Example:
try {
console.log(x); // ReferenceError: x is not defined
} catch (error) {
console.error(`${error.name}: ${error.message}`);
}
Answer:
Asynchronous errors occur when working with async operations like API calls or file I/O.
JavaScript provides async/await syntax and Promises to handle these errors. The try-catch
block can also be used inside an async function to catch these errors.
For Example:
In this example, any error during the fetch operation is caught by the try-catch block.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
254
Answer:
For Example:
Errors are more severe than warnings, but both are useful for tracking issues.
Answer:
Breakpoints allow you to pause code execution at specific lines and inspect variables during
runtime. This is especially useful for identifying issues that aren’t immediately obvious from
the code.
For Example:
You can then step through the code using buttons in the developer tools to observe variable
states.
Answer:
The console.group() method allows you to group multiple logs into a collapsible structure,
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
255
which improves readability. console.groupEnd() closes the group. This is useful for
organizing related log messages.
For Example:
console.group("User Information");
console.log("Name: Alice");
console.log("Age: 30");
console.group("Address");
console.log("City: New York");
console.groupEnd(); // Ends "Address" group
console.groupEnd(); // Ends "User Information" group
This example organizes logs into nested groups, making it easier to navigate large sets of
console messages.
Answer:
Rethrowing an error inside a catch block allows you to perform some initial handling (like
logging) and then let the error propagate for further handling elsewhere. This is especially
useful if an error needs different treatments at different levels of your application.
For Example:
function process() {
try {
throw new Error("Initial error");
} catch (error) {
console.warn("Handling error:", error.message); // First handling
throw error; // Rethrow the error
}
}
try {
process();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
256
} catch (error) {
console.error("Caught rethrown error:", error.message); // Final handling
}
Here, the error is caught and logged in the first catch block but rethrown to be handled at
the outer level. This ensures you don’t lose the original error context.
22. How can you create a custom error with additional properties?
Answer:
Creating custom errors allows you to provide more context about the problem by adding
custom properties. You can extend the built-in Error class and add extra data like status
codes or fields where the error occurred.
For Example:
try {
throw new ValidationError("Invalid email format", "email");
} catch (error) {
console.error(`${error.name}: ${error.message} in ${error.field}`);
}
This example creates a ValidationError that provides extra information about which field
caused the error.
23. What is a stack trace in JavaScript, and how can it help in debugging?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
257
Answer:
A stack trace is a detailed list of function calls that shows the path through which the code
execution reached the point where an error occurred. It helps identify the sequence of
function calls leading to the issue and shows the exact file and line number where the error
happened.
For Example:
function a() {
b();
}
function b() {
c();
}
function c() {
throw new Error("Something went wrong");
}
try {
a();
} catch (error) {
console.error(error.stack); // Stack trace of the error
}
The output shows the sequence: c() → b() → a(), helping you trace the origin of the issue.
Answer:
Promise.all() is used to execute multiple promises in parallel and wait for all of them to
resolve. If one promise fails, the entire operation is rejected. You can catch errors using try-
catch inside an async function or with .catch().
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
258
executeAll();
Here, if any promise fails, the entire operation fails, and the error is caught in the catch block.
Answer:
● Synchronous error handling: Errors are caught immediately in sequential code using
try-catch.
● Asynchronous error handling: Errors in asynchronous code (like Promises) are
handled with .catch() or inside an async function using try-catch.
try {
throw new Error("Synchronous error");
} catch (error) {
console.log(error.message);
}
Asynchronous:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
259
Answer:
Memory leaks occur when allocated memory is not released, causing the application to
consume more memory over time. You can use browser developer tools to find and fix such
leaks. Common causes include:
For Example:
In this code, elements keep accumulating in memory without being removed, leading to a
leak.
27. How do you use debugger statements for debugging JavaScript code?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
260
Answer:
The debugger statement pauses the code execution, allowing you to inspect variables and
control flow in the browser’s developer tools. It behaves like a manual breakpoint.
For Example:
function debugExample() {
let x = 10;
debugger; // Execution pauses here
let y = 20;
console.log(x + y);
}
debugExample();
When the browser encounters the debugger statement, it will stop execution, letting you
inspect variables like x and y at that point.
28. What is async/await error handling, and how does it improve over .then()
and .catch()?
Answer:
Using async/await makes asynchronous code look and behave like synchronous code,
making it easier to read and maintain. Instead of chaining .then() and .catch(), you can
use try-catch for clean error handling.
fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => console.error("Error:", error.message));
Using async/await:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
261
try {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error.message);
}
}
getData();
With async/await, the code is cleaner, and errors are handled using try-catch.
29. How do you log errors to a server for better tracking and analysis?
Answer:
Logging errors to a server allows you to track and analyze issues in production. This helps
monitor application performance and identify patterns in user-reported errors. You can send
error logs using fetch or any HTTP library.
For Example:
function logError(error) {
fetch('https://logging-server.com/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: error.message, stack: error.stack })
});
}
try {
throw new Error("An unexpected error");
} catch (error) {
console.error("Logging error:", error.message);
logError(error);
}
This example demonstrates how to log errors remotely for future analysis.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
262
30. What are source maps, and how do they help in debugging minified
JavaScript code?
Answer:
Source maps link the minified code to the original source code, making it easier to debug
production code. When JavaScript is minified, variable names are shortened, and the
structure becomes hard to read. Source maps allow browsers to map the minified code
back to its original form.
For Example:
If a minified JavaScript file includes:
//# sourceMappingURL=script.min.js.map
The browser will use the script.min.js.map file to show the original code in developer tools,
making it easier to understand and debug issues.
This feature is essential for debugging production environments without exposing the raw
source code directly.
31. How can you handle multiple errors within the same try-catch block?
Answer:
Handling multiple error types within the same catch block ensures that you can respond
differently to different error scenarios. You typically inspect the error object’s name or
instanceof to determine the type of error and handle each case accordingly. This technique
avoids scattering try-catch blocks and keeps the logic centralized.
For Example:
try {
let result = someUndefinedFunction(); // Throws ReferenceError
} catch (error) {
if (error instanceof ReferenceError) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
263
console.error("ReferenceError:", error.message);
} else if (error instanceof TypeError) {
console.error("TypeError:", error.message);
} else {
console.error("General Error:", error.message);
}
}
In this example, different error types like ReferenceError and TypeError are identified and
handled uniquely, helping to debug more effectively.
Answer:
The window.onerror function acts as a global error handler for any uncaught errors in the
JavaScript code. This is particularly useful in production environments where unexpected
errors might occur. By capturing the error globally, you can log it to a server or display a
fallback UI to prevent the application from breaking completely.
For Example:
Here, window.onerror logs detailed information about where and how the error occurred,
helping with debugging and recovery.
33. How do you implement a retry mechanism for failed operations in JavaScript?
Answer:
A retry mechanism attempts to re-run an operation a few times in case of failure. This is
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
264
commonly used for network requests, where transient errors like time-outs or server
unavailability can occur. The idea is to retry the operation with a delay to increase the
chance of success.
For Example:
fetchWithRetry("https://api.example.com/data").catch((error) =>
console.error("Failed after multiple retries:", error.message)
);
This code retries the fetch operation until it either succeeds or exhausts the retry attempts.
34. What is the difference between throw and return in error handling?
Answer:
● throw: Interrupts the function’s execution and passes control to the nearest catch
block.
● return: Ends the function and sends a value back to the caller, without raising an
error.
Use throw when you want to indicate that something unexpected or invalid occurred, while
return is used to provide expected results or status values.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
265
For Example:
function checkValue(value) {
if (value < 0) throw new Error("Negative value not allowed");
return value;
}
try {
console.log(checkValue(-1)); // Throws an error
} catch (error) {
console.error("Caught Error:", error.message);
}
In this example, if the value is invalid, the function throws an error; otherwise, it returns the
valid value.
35. How can you create and use Error subclasses to handle different error
scenarios?
Answer:
Creating custom error classes by extending the Error class allows you to define specific
types of errors for different parts of your application, such as DatabaseError or
ValidationError. This makes it easier to categorize and handle errors appropriately.
For Example:
try {
throw new DatabaseError("Database connection failed");
} catch (error) {
if (error instanceof DatabaseError) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
266
This code defines a DatabaseError class and allows specific handling for database-related
issues.
Answer:
Promise.race() returns the result of the first promise to settle (resolve or reject). You can
use this feature to set a timeout for an operation. If the timeout promise settles first, the
operation is considered failed.
For Example:
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout exceeded")), ms)
);
}
fetchWithTimeout("https://api.example.com/data", 5000);
If the fetch operation takes longer than 5 seconds, the timeout promise rejects the
operation.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
267
37. How do you perform structured logging with custom error objects?
Answer:
Structured logging involves logging error data along with contextual information, such as
user ID, timestamp, and error type. This helps developers analyze logs more effectively in
production systems.
For Example:
try {
throw new AppError("Operation failed", 12345);
} catch (error) {
console.log(JSON.stringify(error, Object.getOwnPropertyNames(error)));
}
This example demonstrates how to log an error along with additional metadata, such as
userId and timestamp.
38. What are uncaught promise rejections, and how can you handle them
globally?
Answer:
An uncaught promise rejection occurs when a rejected promise doesn’t have a .catch()
block. These errors can crash the application if not handled. To manage them, use the
unhandledrejection event.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
268
This example captures all unhandled promise rejections globally and logs them.
39. How do you debug and handle cyclic errors in JSON using try-catch?
Answer:
Cyclic errors occur when an object contains circular references, making it impossible to
convert it to JSON. You can catch these errors with try-catch.
For Example:
try {
JSON.stringify(obj); // This throws an error
} catch (error) {
console.error("Cyclic error:", error.message);
}
The error is caught, preventing the application from crashing due to the circular reference.
Answer:
In a UI, graceful error handling involves showing user-friendly messages instead of
technical errors. This keeps the user experience smooth and prevents the interface from
breaking.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
269
try {
throw new Error("Unexpected error");
} catch (error) {
ReactDOM.render(<ErrorBoundary error={error} />,
document.getElementById("root"));
}
This example displays a friendly error message and provides a way to reload the page,
ensuring the user can recover from the error easily.
SCENARIO QUESTIONS
41. Scenario: You are developing a form validation system, and the form crashes
because of an invalid variable name in JavaScript code.
Question: How would you identify and resolve a syntax error in this scenario?
Answer:
Syntax errors occur when the code is incorrectly written according to JavaScript rules. These
errors prevent code from running and are usually flagged during compilation or by the
browser before execution. In the form validation scenario, the console will indicate the exact
line and character where the error occurred.
To resolve:
1. Use Developer Tools (F12): Go to the Console tab to see the syntax error.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
270
For Example:
Fix:
if (formValid) {
console.log("Form is valid");
}
42. Scenario: You call a function that doesn’t exist, and your application crashes
with an error.
Answer:
A runtime error occurs when the code structure is correct, but execution fails due to invalid
operations, such as calling an undefined function. Runtime errors are often due to mistyped
function names or unavailable dependencies. Use try-catch blocks to handle them
gracefully, so the rest of the program can run without interruption.
For Example:
try {
nonExistentFunction(); // ReferenceError: function is not defined
} catch (error) {
console.error("Caught an error:", error.message);
}
console.log("The program continues.");
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
271
Here, even though the function doesn’t exist, the catch block ensures that the error is
logged, and the rest of the program executes.
43. Scenario: You mistakenly implement a discount calculation that adds prices
instead of subtracting a discount.
Question: How would you detect and debug a logical error in JavaScript?
Answer:
Logical errors occur when the code executes successfully but doesn’t produce the correct
output due to flawed logic. These are hard to detect since no syntax or runtime errors occur.
The best way to debug logical errors is by:
For Example:
Corrected:
44. Scenario: You are building an API request function, and you want to ensure
the application does not crash when the API call fails.
Question: How would you handle errors gracefully with try-catch in asynchronous code?
Answer:
In asynchronous operations like fetch requests, errors can occur due to network issues or
invalid URLs. Use try-catch blocks within an async function to handle these errors
gracefully.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
272
For Example:
fetchData("https://api.example.com/data");
This ensures that even if the API call fails, the error is caught and logged without crashing the
app.
45. Scenario: A button click triggers an undefined event handler, causing the
application to crash.
Question: How would you handle errors caused by undefined event handlers in JavaScript?
Answer:
If a button click triggers an undefined event handler, a ReferenceError is thrown. Use try-
catch blocks to handle such cases and prevent the application from crashing.
For Example:
button.addEventListener("click", () => {
try {
eventHandler(); // ReferenceError: eventHandler is not defined
} catch (error) {
console.error("Error:", error.message);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
273
});
This ensures that even if the event handler is undefined, the error is logged, and the rest of
the program continues to run.
46. Scenario: A function throws an error if a parameter is invalid, and you want to
throw custom error messages.
Question: How would you create and throw custom errors in JavaScript?
Answer:
You can throw custom errors to provide meaningful error messages when specific
conditions are not met. This improves readability and helps users or developers understand
the problem.
For Example:
function validateAge(age) {
if (age < 18) throw new Error("Age must be 18 or above");
console.log("Age is valid.");
}
try {
validateAge(16);
} catch (error) {
console.error("Validation Error:", error.message);
}
Here, a custom error message is thrown if the age is below 18, and the error is caught to
prevent the program from crashing.
47. Scenario: You have a recursive function, and you suspect it’s causing a
memory leak.
Question: How would you debug and handle memory leaks in JavaScript?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
274
Answer:
A memory leak occurs when memory that is no longer needed isn’t released. Use the
Memory tab in browser Developer Tools to monitor memory usage over time. Remove
unnecessary references or use WeakMap to handle temporary data.
For Example:
Use clearInterval to stop the function or periodically clear the elements array to avoid
memory build-up.
48. Scenario: You want to group multiple console messages together for better
readability.
Question: How would you use console.group() and console.groupEnd() for debugging?
Answer:
Use console.group() and console.groupEnd() to group related logs, making them easier
to read and navigate in the console.
For Example:
console.group("User Information");
console.log("Name: John Doe");
console.log("Age: 30");
console.group("Address");
console.log("City: New York");
console.groupEnd(); // End Address group
console.groupEnd(); // End User Information group
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
275
49. Scenario: You need to ensure that some code executes regardless of whether
an error occurs.
Answer:
A finally block ensures that code inside it runs no matter what—whether the try block
succeeds or an error occurs. This is useful for cleanup tasks.
For Example:
try {
let data = JSON.parse('{"key": "value"}');
} catch (error) {
console.error("Error:", error.message);
} finally {
console.log("Cleanup completed.");
}
Here, the finally block runs even if the parsing fails or succeeds.
50. Scenario: You have a button that triggers multiple operations, and you want to
log all steps clearly.
Question: How would you use console.table() to display data neatly in the console?
Answer:
Use console.table() to log data in a tabular format, making it easier to analyze arrays or
objects.
For Example:
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
276
console.table(users);
This displays the user data in a readable table format in the console.
51. Scenario: You have multiple asynchronous functions, and one of them fails. You
want to continue executing the rest instead of stopping.
Answer:
Using Promise.allSettled() allows you to execute multiple asynchronous operations and
get a result for each—whether fulfilled or rejected. This ensures that one failed promise does
not stop the others from completing. This is useful when working with independent tasks
where you want to log or process all results, regardless of success or failure.
For Example:
const promises = [
Promise.resolve("Operation 1 success"),
Promise.reject("Operation 2 failed"),
Promise.resolve("Operation 3 success")
];
Promise.allSettled(promises).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.warn("Failure:", result.reason);
}
});
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
277
Even though one promise fails, the others continue to execute, and all results are logged.
52. Scenario: You want to measure the performance of different parts of your code
for debugging.
Answer:
console.time() and console.timeEnd() help measure the execution time of a specific
block of code. This is useful for performance testing to detect bottlenecks.
For Example:
console.time("Loop Execution");
console.timeEnd("Loop Execution");
The console will display how long it took for the loop to execute, helping you identify slow
code sections.
Question: How would you ensure all promise rejections are handled properly?
Answer:
Uncaught promise rejections can lead to unpredictable behavior. Using the
unhandledrejection event ensures that even if promises don’t have .catch() blocks, the
rejections are logged and managed globally.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
278
This ensures no unhandled promise rejection goes unnoticed, improving error tracking.
54. Scenario: You are working with large datasets and want to prevent freezing
the browser with synchronous loops.
Question: How would you use setTimeout to avoid blocking the UI?
Answer:
Long synchronous loops block the event loop, freezing the UI. Breaking tasks into smaller
pieces using setTimeout() allows the browser to handle UI updates between iterations.
For Example:
let i = 0;
function processLargeData() {
if (i < 100000) {
console.log(i++);
setTimeout(processLargeData, 0); // Prevents UI freeze
}
}
processLargeData();
This approach keeps the browser responsive while processing large datasets.
55. Scenario: You have a recursive function, but it reaches the call stack limit.
Question: How would you avoid a stack overflow with tail call optimization?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
279
Answer:
Tail call optimization (TCO) reuses the current stack frame for certain recursive calls,
preventing stack overflow. This optimization occurs if the recursive call is the last operation
in the function.
For Example:
In this example, each recursive call replaces the previous one, keeping the stack from
growing indefinitely.
56. Scenario: You want to ensure a callback function only runs once, even if called
multiple times.
Answer:
A once function ensures that a callback executes only the first time it is called, ignoring
subsequent calls. This is useful for initializing logic that should not run more than once.
For Example:
function once(fn) {
let executed = false;
return function (...args) {
if (!executed) {
executed = true;
fn(...args);
}
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
280
Here, the callback executes only once, even if called multiple times.
57. Scenario: You have nested promises and want to avoid callback hell.
Answer:
Using async/await simplifies handling nested promises, improving code readability. Instead
of chaining .then() calls, you can await each promise sequentially.
For Example:
getData();
This avoids callback hell and makes the code easier to maintain.
58. Scenario: You want to log errors and metadata from multiple asynchronous
tasks.
Question: How would you collect errors and logs efficiently from multiple promises?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
281
Answer:
Use Promise.allSettled() to collect results from multiple promises, including both
successes and errors.
For Example:
const tasks = [
Promise.resolve("Task 1 completed"),
Promise.reject("Task 2 failed"),
Promise.resolve("Task 3 completed")
];
Promise.allSettled(tasks).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.error("Error:", result.reason);
}
});
});
59. Scenario: You want to handle multiple errors differently based on their type.
Question: How would you differentiate between different error types in a catch block?
Answer:
Using instanceof, you can identify error types inside the catch block and handle them
accordingly.
For Example:
try {
JSON.parse("{ invalid json }"); // Throws SyntaxError
} catch (error) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
282
60. Scenario: You want to ensure that a critical operation retries automatically if it
fails.
Question: How would you implement an exponential backoff strategy for retries?
Answer:
Exponential backoff increases the delay between retries, helping to reduce server load. If a
request fails, it retries with increasing delay until the limit is reached.
For Example:
fetchWithRetry("https://api.example.com/data").catch((error) =>
console.error("All retries failed:", error.message)
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
283
);
This strategy ensures retries are attempted gradually to avoid overwhelming the server.
61. Scenario: You want to conditionally retry an API call only for specific HTTP
status codes like 500 or 503.
Answer:
Conditional retries ensure that you only retry requests when necessary, such as in the case of
server errors (e.g., 500, 503) but not for client errors like 404. This prevents wasting resources
on non-recoverable errors. With async/await, you can handle conditional logic smoothly
within the retry mechanism.
For Example:
In this code:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
284
● If the API returns status 500 or 503, it retries until the retries run out.
● For non-retryable errors (e.g., 404), the error is immediately thrown.
62. Scenario: You want to debug an issue where a deeply nested object property is
causing an error.
Question: How would you safely access deeply nested object properties?
Answer:
Accessing deeply nested properties can cause runtime errors if any intermediate property is
undefined. You can prevent this with optional chaining (?.) or use try-catch for robust
error handling.
For Example:
try {
console.log(user.profile.address.city); // Valid access
console.log(user.profile.contact.email); // Error: Cannot read property 'email'
} catch (error) {
console.error("Error accessing property:", error.message);
}
Question: How would you manage loading states with try-catch blocks?
Answer:
Using try-catch with loading indicators ensures that the indicator is shown while the
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
285
operation is in progress and hidden afterward. The finally block guarantees that the
indicator will be removed, even if an error occurs.
For Example:
try {
const response = await fetch(url);
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error.message);
} finally {
loadingIndicator.style.display = "none"; // Hides the indicator regardless
}
}
The loading indicator remains visible during the API call and is hidden whether the operation
succeeds or fails.
64. Scenario: You want to throttle function calls to limit API requests.
Answer:
Throttling ensures a function runs at most once within a given time frame, preventing
excessive API requests. This is useful for scroll events or button clicks that might trigger
rapid, repeated operations.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
286
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
log("Hello"); // Executes
log("This will be ignored"); // Ignored
setTimeout(() => log("This will run"), 1100); // Runs after 1.1s
● Throttling ensures the function runs only once every 1 second, even if it is called
multiple times.
65. Scenario: You need to log errors along with stack traces for debugging.
Question: How would you use the Error object to log stack traces?
Answer:
The Error object in JavaScript provides a stack property that contains the sequence of
function calls leading to the error. This stack trace is essential for debugging complex issues.
For Example:
function a() {
b();
}
function b() {
c();
}
function c() {
throw new Error("Something went wrong");
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
287
try {
a();
} catch (error) {
console.error("Error:", error.message);
console.error("Stack trace:", error.stack);
}
The stack trace shows the entire call hierarchy, making it easier to trace where the error
originated.
66. Scenario: You want to stop requests after multiple failures to avoid
overwhelming a service.
Answer:
A circuit breaker stops making requests if failures exceed a threshold. After a cooldown
period, it resets to allow requests again. This avoids overloading services.
For Example:
class CircuitBreaker {
constructor(failureThreshold, resetTime) {
this.failures = 0;
this.failureThreshold = failureThreshold;
this.resetTime = resetTime;
this.state = "CLOSED";
}
async request(fn) {
if (this.state === "OPEN") {
throw new Error("Circuit breaker is open");
}
try {
const result = await fn();
this.failures = 0; // Reset on success
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
288
return result;
} catch (error) {
this.failures++;
if (this.failures >= this.failureThreshold) {
this.state = "OPEN";
setTimeout(() => (this.state = "CLOSED"), this.resetTime);
}
throw error;
}
}
}
This circuit breaker prevents repeated failures from overloading the service.
Answer:
AbortController allows you to cancel asynchronous tasks like fetch requests.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
289
fetchData("https://api.example.com/data");
68. Scenario: You need to debounce a search input to prevent multiple requests.
Answer:
Debouncing ensures that a function runs only after the user has stopped triggering it for a
set time.
For Example:
search("Ja"); // Ignored
search("Jav"); // Ignored
search("JavaScript"); // Executes after 300ms
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
290
This reduces unnecessary requests, only executing the function after the user finishes
typing.
Answer:
Exponential backoff increases the delay between retries to avoid overwhelming the server.
For Example:
fetchWithBackoff("https://api.example.com/data").catch((error) =>
console.error("All retries failed:", error.message)
);
This technique reduces server load by increasing wait times between retries.
70. Scenario: You want to detect and handle memory leaks in your application.
Question: How would you debug memory leaks using browser developer tools?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
291
Answer:
Memory leaks occur when the app retains unused memory. Use the Memory tab in
Developer Tools to identify such leaks.
For Example:
71.
Scenario: You want to execute multiple promises but stop when the first one completes,
regardless of success or failure.
Question: How would you use Promise.race() to handle this scenario?
Answer:
Promise.race() returns the result of the first promise to either resolve or reject, making it
useful when you only care about the earliest response.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
292
In this case, p3 wins the race by rejecting after 200ms, and the catch block handles the error.
72.
Scenario: You want to ensure that a specific block of code runs only after multiple
asynchronous operations complete successfully.
Question: How would you coordinate with Promise.all() to achieve this?
Answer:
Promise.all() runs multiple promises in parallel and waits until all of them are resolved. If
any promise fails, the entire operation fails.
For Example:
Promise.all([fetchUser, fetchOrders])
.then(([user, orders]) => {
console.log("User:", user);
console.log("Orders:", orders);
})
.catch((error) => console.error("Error fetching data:", error.message));
This code ensures both user and order data are fetched before proceeding.
73.
Scenario: You need to log the sequence of operations and group related logs.
Question: How would you use console.group() and console.groupEnd() for this purpose?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
293
Answer:
console.group() organizes logs into a collapsible group, improving readability in the
console.
For Example:
console.group("User Operations");
console.log("Fetching user...");
console.group("Order Operations");
console.log("Fetching orders...");
console.groupEnd(); // End Order group
console.groupEnd(); // End User Operations group
The logs appear nested, helping you visualize the operation flow.
74.
Scenario: You need to retry an operation only for network errors, not logic errors.
Question: How would you selectively retry based on error type?
Answer:
You can check the error type or message inside the catch block to retry selectively.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
294
}
}
This ensures network errors are retried, while logic errors are thrown immediately.
75.
Answer:
The finally block guarantees that cleanup operations run regardless of success or failure.
For Example:
76.
Scenario: You want to collect data from a stream but stop after the first error.
Question: How would you handle errors in a streaming operation?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
295
Answer:
Use ReadableStream with proper error handling to stop the stream after the first error.
For Example:
77.
Scenario: You need to handle a large number of tasks, but only a few should run
concurrently.
Question: How would you implement concurrency control with a queue?
Answer:
You can create a task queue to ensure only a limited number of tasks run simultaneously.
For Example:
class TaskQueue {
constructor(limit) {
this.limit = limit;
this.running = 0;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
296
this.queue = [];
}
enqueue(task) {
this.queue.push(task);
this.runNext();
}
async runNext() {
if (this.running >= this.limit || this.queue.length === 0) return;
78.
Answer:
Using Promise.race() allows you to implement a timeout by racing the API request against
a timeout promise.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
297
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error("Request timed out")), ms)
);
}
fetchWithTimeout("https://api.example.com/data", 5000)
.then((res) => console.log("Response received"))
.catch((error) => console.error(error.message));
If the API doesn’t respond within 5 seconds, the timeout promise rejects the operation.
79.
Scenario: You need to reduce the frequency of a function call without missing the last input.
Question: How would you implement a debounce function that ensures the last call runs?
Answer:
This debounce function ensures the last call always executes after the user stops triggering
it.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
298
search("Ja");
search("Jav");
search("JavaScript"); // Executes after 300ms with the latest input
80.
Scenario: You want to implement retry logic with a delay between attempts.
Question: How would you implement retry logic with delay and exponential backoff?
Answer:
Use exponential backoff to increase the delay between retries, reducing server load during
retries.
For Example:
fetchWithRetry("https://api.example.com/data")
.catch((error) => console.error("All retries failed:", error.message));
This ensures each retry has a longer delay to avoid overloading the server.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
299
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
300
THEORETICAL QUESTIONS
1. What is destructuring in JavaScript, and how is it used with arrays?
Answer: Destructuring is a feature introduced in ES6 that simplifies the process of extracting
values from arrays or objects and assigning them to individual variables. In the case of arrays,
destructuring matches the array elements by position, meaning the first element in the array
will be assigned to the first variable in the destructuring syntax, the second to the second,
and so on.
This concise syntax helps reduce boilerplate code, especially when working with complex
data structures. If there are fewer variables than array elements, the remaining elements are
ignored. If there are more variables than elements, the extra variables are assigned
undefined.
For Example:
console.log(first); // 10
console.log(second); // 20
This assigns 10 to first and 20 to second. It’s particularly useful in functions that return
multiple values in an array.
Answer: Unlike array destructuring, object destructuring extracts properties from objects
based on their names, not their order. This makes it more flexible, especially when dealing
with complex objects. You can also provide default values and use aliases to rename
properties during destructuring.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
301
For Example:
console.log(userName); // Alice
console.log(age); // 30
Here, the name property is extracted and assigned to a new variable userName. This flexibility
is useful when property names need to be mapped to more meaningful variable names in a
specific context.
Answer:
For Example:
console.log(newArray); // [1, 2, 3, 4]
In this case, the Spread operator combines two arrays into one. The Rest operator, on the
other hand, allows gathering parameters into an array, making it helpful for working with
functions that take variable arguments.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
302
function multiply(...nums) {
return nums.reduce((acc, num) => acc * num, 1);
}
console.log(multiply(2, 3, 4)); // 24
Here, all the passed arguments are collected into the nums array.
Answer: Default parameters let you define fallback values for function parameters. If the
function is called without those arguments, the default values are used instead. This feature
avoids undefined values and provides more readable and error-resistant code.
For Example:
This makes it easier to handle cases where not all arguments are passed to a function.
Without default parameters, you’d have to write additional logic to handle such situations.
5. What are template literals in JavaScript, and how are they different from
regular strings?
Answer: Template literals, introduced in ES6, allow for cleaner and more readable string
interpolation. With regular strings, you would use concatenation, which can get messy with
complex expressions. Template literals use backticks and ${} for embedding variables or
expressions directly inside the string.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
303
Answer: The for...of loop simplifies iterating over iterable objects like arrays, strings, and
collections like Maps or Sets. It iterates directly over the values instead of indices, making it
cleaner and easier to understand.
For Example:
This loop logs each color directly without worrying about indexes. It’s particularly useful when
the index isn’t needed.
Answer: A generator function is defined using function* and can yield multiple values. It
allows pausing and resuming execution, which makes it useful for lazy evaluation and
asynchronous programming.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
304
function* counter() {
yield 1;
yield 2;
yield 3;
}
Each call to next() resumes the generator from where it left off.
Answer: The export and import syntax enables modular code, allowing you to split
functionality into different files and reuse it across your application. You can use both named
exports and default exports.
// math.js
export const PI = 3.14;
export function add(a, b) {
return a + b;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
305
Answer: Dynamic imports allow you to load JavaScript modules only when needed,
improving the performance of large applications. This technique is often used in modern
frameworks like React for code-splitting.
For Example:
function loadMathModule() {
import('./math.js').then(module => {
console.log(module.add(5, 3)); // 8
});
}
loadMathModule();
This way, the module is loaded only when the function is called.
10. What is the Symbol data type in JavaScript, and why is it useful?
Answer: Symbols are unique, immutable values primarily used as keys in objects to avoid
property name conflicts. Even if two symbols have the same description, they are considered
different.
For Example:
Symbols ensure that object properties are unique, which is especially useful when
integrating libraries or frameworks to avoid accidental key conflicts.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
306
Answer:
The for...in and for...of loops are used for different purposes:
● for...in: Iterates over the keys or properties of an object or array. It works well for
objects but can also be used with arrays. However, it may return inherited properties,
which might not always be desirable.
● for...of: Iterates over the values of iterable objects like arrays, strings, or collections
(e.g., Sets, Maps). It is more appropriate for accessing array values directly without
needing the index.
Use for...of when working with array values and for...in when iterating through object
properties.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
307
Answer:
WeakSet is similar to a regular Set but only allows objects as elements and holds weak
references to them. This means that if an object in a WeakSet has no other references, it will
be garbage-collected, improving memory management. This is useful when you want to
track objects without preventing them from being garbage-collected.
For Example:
console.log(weakSet.has(user)); // true
WeakSet cannot store primitive values and doesn’t support iteration since it’s designed for
temporary data storage.
13. What is a WeakMap in JavaScript, and how does it differ from a Map?
Answer:
WeakMap is similar to a Map but only allows objects as keys and holds weak references to
these keys. This ensures that if the object key is no longer referenced elsewhere, it will be
garbage-collected. This makes WeakMap useful for caching or storing metadata for objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
308
In contrast, Map holds strong references to its keys, meaning the keys won't be garbage-
collected.
Answer:
The primary differences between Set and WeakSet are:
● Set can store any type of value, including primitives and objects, and allows iteration.
● WeakSet only stores objects and does not allow iteration.
Answer:
A Map allows you to store key-value pairs with any data type as a key. It preserves the
insertion order and provides methods like set(), get(), delete(), and has().
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
309
console.log(map.get("name")); // Alice
console.log(map.has(1)); // true
Maps are preferable over objects when you need complex key types or insertion order.
16. What are ES modules, and how are they different from CommonJS
modules?
Answer:
ES Modules (ESM) and CommonJS (CJS) are two different module systems in JavaScript.
ESM uses import and export for modular code, while CJS uses require() and
module.exports. ESM modules are statically analyzed, allowing better optimizations by
bundlers, and are supported natively in browsers.
// math.js
export const PI = 3.14;
export function add(a, b) {
return a + b;
}
// app.js
import { PI, add } from './math.js';
console.log(PI); // 3.14
CJS is typically used in Node.js, but ESM is becoming the standard for web applications.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
310
Answer:
An iterator is an object that provides a next() method to access elements one at a time from
a collection. Each call to next() returns an object with two properties:
For Example:
Answer:
For Example:
let a;
console.log(a); // undefined
let b = null;
console.log(b); // null
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
311
undefined usually indicates a missing value, while null is used to represent an intentionally
empty value.
Answer:
The async/await syntax simplifies working with asynchronous code. An async function
always returns a promise, and await pauses the function execution until the promise is
resolved or rejected.
For Example:
fetchData();
This code makes the fetch request easier to read compared to using .then() chains.
Answer:
For Example:
var a = 1;
let b = 2;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
312
const c = 3;
a = 4; // Valid
b = 5; // Valid
// c = 6; // Error: Assignment to constant variable
Using let and const helps prevent bugs caused by variable hoisting and scoping issues.
Answer:
The event loop manages asynchronous tasks by moving completed tasks from the task
queue or microtask queue to the call stack only when the call stack is empty. This ensures
that asynchronous code doesn’t block the main thread.
For Example:
console.log("Start");
The setTimeout callback is delayed until the synchronous code finishes executing.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
313
Answer:
A promise represents the result of an asynchronous operation that can be in three states:
pending, fulfilled, or rejected. Promises simplify chaining asynchronous operations using
.then() and .catch().
● Promise.all: Runs multiple promises concurrently and resolves only when all
succeed or rejects if any fail.
● Promise.race: Resolves or rejects as soon as the first promise settles, regardless of the
result.
For Example:
Answer:
A closure occurs when a function retains access to variables from its outer scope, even after
the outer function has executed. This enables data encapsulation and private variables.
For Example:
function outerFunction(outerVariable) {
return function innerFunction(innerVariable) {
console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
314
The inner function remembers the value of outerVariable even after outerFunction has
finished executing.
24. What is the difference between deep and shallow copy in JavaScript?
Answer:
● Shallow Copy: Only copies the top-level properties, leaving nested objects as
references. Changes to nested objects affect both copies.
● Deep Copy: Creates a new object, recursively copying all properties and nested
objects, making them independent.
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj };
shallowCopy.b.c = 42;
console.log(obj.b.c); // 42 (Affects original)
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
315
Answer:
JavaScript uses automatic garbage collection through the mark-and-sweep algorithm. The
mark phase identifies reachable objects, and the sweep phase removes those that are
unreachable, freeing memory.
For Example:
Answer:
A higher-order function is a function that takes another function as an argument or returns
a function. They are a key concept in functional programming, enabling flexibility and
reusable logic.
For Example:
function higherOrderFunction(callback) {
callback("Hello from callback!");
}
Higher-order functions are used in common methods like map(), filter(), and reduce().
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
316
27. What is the purpose of the this keyword in JavaScript? How does arrow
function behavior differ with this?
Answer:
The this keyword refers to the context from which a function is called. The value of this
changes depending on the calling object.
● Arrow functions don’t have their own this. They inherit it from their lexical scope (the
surrounding code).
For Example:
const obj = {
name: "Alice",
regularFunction: function () {
console.log(this.name); // Alice
},
arrowFunction: () => {
console.log(this.name); // undefined (no lexical `this` in global scope)
}
};
obj.regularFunction();
obj.arrowFunction();
Answer:
Event delegation takes advantage of event bubbling, allowing a parent element to handle
events for its children, even if they are added dynamically. This avoids attaching individual
listeners to each child.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
317
This example ensures the parent handles clicks on any buttons inside it, even if the buttons
are added later.
Answer:
Currying is a technique where a function with multiple parameters is transformed into a
series of functions, each taking a single parameter. This allows partial application, where
some arguments can be pre-filled.
For Example:
function add(a) {
return function (b) {
return a + b;
};
}
Here, add(5) returns a new function that adds 5 to any given input.
Answer:
JavaScript provides several ways to create objects:
Object Literal:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
318
function Person(name) {
this.name = name;
}
const alice = new Person("Alice");
This approach is used to create multiple instances.
Object.create():
class Person {
constructor(name) {
this.name = name;
}
}
const alice = new Person("Alice");
31. What is the call(), apply(), and bind() method in JavaScript? How are
they different?
Answer:
These methods allow us to explicitly set the this context when invoking a function. This is
useful when a function needs to operate within a different context or when reusing methods
across objects.
● call(): Invokes a function with a specific this value and arguments passed
individually.
● apply(): Similar to call(), but arguments are passed as an array.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
319
● bind(): Returns a new function with this bound to the specified object, which can
be invoked later.
For Example:
call() and apply() execute immediately, while bind() creates a new function that can be
invoked later.
Answer:
In prototypal inheritance, objects inherit directly from other objects. Every JavaScript object
has a [[Prototype]] (or __proto__), pointing to another object from which it inherits
properties and methods.
In classical inheritance (like in Java), classes are blueprints used to create objects.
JavaScript’s prototypes make inheritance more dynamic.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
320
child.greet(); // Hello!
Answer:
Decorators are functions that modify the behavior of classes or methods. They add
additional behavior or metadata. Although still experimental, they are useful in frameworks
like Angular.
For Example:
class Example {
@readonly
method() {
console.log("This method is readonly.");
}
}
Answer:
async/await is syntactic sugar over promises. It makes asynchronous code look synchronous.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
321
For Example:
Answer:
Memoization stores the results of function calls based on input arguments. If the same
inputs are encountered again, the cached result is returned.
For Example:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
322
Answer:
Variable shadowing occurs when a variable declared in a child scope uses the same name
as one in the outer scope. The inner variable overrides access to the outer one within its
scope.
For Example:
let a = 1;
function test() {
let a = 2; // Shadows the outer variable
console.log(a); // 2
}
test();
console.log(a); // 1
In the function, a refers to the inner scope variable. Outside the function, it refers to the outer
scope.
37. What are Web Workers in JavaScript, and how do they work?
Answer:
Web Workers allow running JavaScript code in background threads to prevent blocking the
main UI thread. They are especially useful for long-running tasks.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
323
For Example:
// worker.js
self.onmessage = function (e) {
const result = e.data * 2;
postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(5); // Send data to worker
worker.onmessage = function (e) {
console.log(e.data); // 10
};
Answer:
The module pattern creates private scope and exposes public methods. It relies on closures
to keep variables private while exposing certain functions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
324
}
};
})();
Counter.increment(); // 1
Counter.increment(); // 2
console.log(Counter.getCount()); // 2
39. What are dynamic imports in JavaScript, and when should you use
them?
Answer:
Dynamic imports allow loading modules at runtime instead of loading everything at the
start. This is helpful for lazy loading parts of the application.
For Example:
Dynamic imports improve performance by splitting code and loading it only when needed.
40. What is tail call optimization, and how does it work in JavaScript?
Answer:
Tail call optimization (TCO) is an optimization technique where the last function call in a
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
325
function is optimized to reuse the current stack frame. This prevents stack overflow in
recursive functions.
For Example:
TCO reduces memory usage by reusing stack frames, making recursion more efficient.
SCENARIO QUESTIONS
41. How can you use array destructuring to assign multiple values to
variables in JavaScript?
Answer:
Array destructuring allows you to extract values from an array and assign them to individual
variables. This feature simplifies accessing array elements by reducing code redundancy. It
follows the positional order of elements in the array, assigning the first value to the first
variable, the second to the second, and so on.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
326
console.log(first); // Apple
console.log(second); // Banana
console.log(third); // Mango
If the array has fewer elements than variables, the unassigned variables will be undefined.
You can also skip elements by leaving gaps between commas, such as [ , second ].
42. How can you use object destructuring to access specific properties of
an object?
Answer:
Object destructuring allows you to extract properties from objects by matching property
names. It provides an easy way to assign properties to variables with the same names or use
aliases if needed. If the property doesn’t exist in the object, the variable is assigned
undefined.
For Example:
console.log(userName); // Alice
console.log(age); // 30
console.log(country); // USA
Here, the name property is assigned to a variable called userName through aliasing.
43. How can the spread operator be used to combine multiple arrays?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
327
Answer:
The spread operator (...) expands the elements of an iterable, such as arrays, into individual
elements. This allows you to merge multiple arrays or clone an existing array into a new
one. It’s a convenient way to avoid modifying the original arrays.
For Example:
console.log(mergedArray); // [1, 2, 3, 4, 5, 6]
You can also insert additional elements within the new array using spread syntax.
44. How can the rest operator be used to collect multiple function
arguments?
Answer:
The rest operator (...) collects multiple function arguments into an array. This is helpful for
functions that accept a variable number of arguments, ensuring they can be accessed
consistently as an array.
For Example:
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
The ...numbers parameter gathers all function arguments into the numbers array.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
328
Answer:
Default parameters allow you to assign default values to function parameters if no
arguments are provided during the function call. This helps avoid errors when optional
parameters are missing.
For Example:
Answer:
Template literals allow for string interpolation and multi-line strings using backticks (`)
instead of quotes. You can embed variables and expressions using ${} within the string,
making code more readable.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
329
With template literals, you can also create multi-line strings without special characters.
Answer:
The for...of loop simplifies iteration over iterable objects like arrays, strings, and sets. It
directly accesses the values of the iterable, avoiding the need to manage indexes.
For Example:
for...of is especially useful when you don't need the index of elements.
Answer:
Generators are functions that allow pausing and resuming execution. They are defined using
the function* syntax and yield values on demand using the yield keyword.
For Example:
function* generator() {
yield 1;
yield 2;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
330
yield 3;
}
Each call to next() retrieves the next value from the generator.
Answer:
ES6 modules allow splitting JavaScript code across multiple files. You can use the export
keyword to expose variables, functions, or classes, and the import keyword to include them
in other files.
Answer:
Symbols are unique and immutable primitive values that can be used as object property
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
331
keys. Even if two symbols have the same description, they are not considered equal. This
ensures that property names are unique, preventing conflicts.
For Example:
const user = {
[sym1]: 1,
name: "Alice"
};
console.log(user[sym1]); // 1
Symbols are often used to create hidden properties in objects that won't collide with other
property names.
51. How can you use nested destructuring to extract values from deeply
nested objects?
Answer:
In JavaScript, nested destructuring lets you extract properties from nested objects into
variables directly, reducing repetitive code. You specify nested properties within matching
curly braces {}. Default values can be assigned if a property might be undefined.
This is useful when working with APIs that return complex JSON data.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
332
const person = {
name: "Alice",
address: {
city: "New York",
zip: 10001
}
};
console.log(name); // Alice
console.log(city); // New York
console.log(zip); // 10001
52. How can the spread operator be used to flatten arrays in JavaScript?
Answer:
The spread operator (...) expands elements from arrays or objects into new structures.
When flattening arrays, it can spread elements from sub-arrays into the parent array,
effectively reducing nesting.
However, the spread operator only flattens one level deep. For multi-level nested arrays,
Array.flat() should be used.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
333
Answer:
Default parameters ensure that a function works even if certain arguments are not provided.
Instead of returning undefined when an argument is missing, the default value is used.
For Example:
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); // 5 (b defaults to 1)
console.log(multiply(5, 2)); // 10
Here:
Answer:
Template literals (introduced in ES6) make it easy to embed variables or expressions inside
strings using ${}. They also support multi-line strings without requiring special characters
like \n. This simplifies code compared to traditional string concatenation.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
334
55. How does the for...in loop help in iterating over object properties?
Answer:
The for...in loop iterates over the keys of an object (or indices in arrays). This loop is useful
when you need both the property names and values from an object.
For Example:
● If the object has inherited properties, the for...in loop will iterate over them as well.
To avoid this, use:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
335
Answer:
Generators allow pausing and resuming function execution using the yield keyword. This
makes them ideal for lazy evaluation, where values are produced only when needed. This
approach is memory efficient when dealing with large or infinite sequences.
For Example:
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
Each call to next() retrieves the next value, pausing the function at the yield keyword until
the next call.
Answer:
Named imports allow importing only specific exports from a module. This improves
performance by loading only what is needed. If you export multiple things from a
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
336
Here, only the add function is imported, leaving the rest of the module untouched.
Answer:
Dynamic imports allow loading modules at runtime using import(). This enables code
splitting, where only the required parts of the code are loaded on demand, improving
application performance.
For Example:
loadMathModule();
In this example, the math.js module is imported only when the function is called, reducing
the initial load time of the application.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
337
Answer:
A Symbol is a unique and immutable value used as a property key. Even if two symbols have
the same description, they are not considered equal. This ensures property name
uniqueness and avoids naming conflicts.
For Example:
Using symbols ensures that object properties won’t collide, even if other parts of the code
use similar names.
Answer:
A Set stores only unique values. If you try to add duplicate elements, they are ignored. This
makes Set useful for filtering unique values from arrays.
For Example:
console.log([...uniqueNumbers]); // [1, 2, 3, 4, 5]
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
338
The Set automatically removes duplicate values, and the spread operator (...) converts the
Set back into an array.
61. How can you use deep destructuring with default values in JavaScript?
Answer:
Deep destructuring allows you to extract values from nested objects at multiple levels. If
certain properties are missing, default values can be provided to prevent undefined from
causing runtime issues. This feature is especially useful when dealing with data structures
like API responses.
For Example:
const user = {
name: "Alice",
address: {
city: "New York",
zip: 10001
}
};
const {
address: { city, zip = "N/A", state = "Unknown" }
} = user;
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
339
62. How can you use a generator function to generate Fibonacci numbers?
Answer:
The Fibonacci sequence is a series of numbers where each number is the sum of the
previous two. Using a generator, we can yield these numbers on demand, making it
memory efficient. Instead of calculating all numbers at once, a generator yields each number
only when needed.
For Example:
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
Explanation:
Each call to next() advances the generator to the next number in the sequence. The
generator only computes the current value, making it ideal for infinite sequences.
Answer:
Memoization stores the results of previous function calls, preventing redundant calculations.
This is particularly effective for recursive functions that would otherwise recompute the
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
340
same values multiple times. For example, calculating large Fibonacci numbers with recursion
alone would be inefficient without memoization.
For Example:
console.log(fibonacci(10)); // 55
Explanation:
The function stores computed values in memo, so repeated calls with the same input don’t
perform unnecessary recalculations.
Answer:
In JavaScript, closures allow functions to access variables from their outer scope even after
the outer function has executed. This feature can be leveraged to simulate private members
in a class-like structure, ensuring that internal data can’t be directly accessed.
For Example:
function Person(name) {
let _age = 0; // Private variable
this.getName = function() {
return name;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
341
};
this.getAge = function() {
return _age;
};
this.setAge = function(age) {
if (age > 0) _age = age;
};
}
Explanation:
The variable _age is not directly accessible from outside the function. It can only be modified
or accessed through the provided methods.
Answer:
Dynamic imports load modules only when required, helping with code splitting and
improving performance. This technique is useful in large applications where not all modules
are needed at the same time.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
342
Explanation:
Here, the math.js module is imported only when the user selects the "math" component,
reducing the initial load time of the application.
Answer:
Symbols provide unique identifiers for object properties, ensuring that even if different
libraries or parts of the code use the same name, the properties won’t conflict. This makes
symbols particularly useful in library integration.
For Example:
const obj = {
[LIBRARY_SYMBOL]: "Library-specific value",
name: "Alice"
};
Explanation:
Even if another library uses "myLibrary" as a key, it won’t conflict because symbols are
unique.
Answer:
A WeakMap stores key-value pairs where the keys are objects. If the object is no longer
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
343
referenced elsewhere, it will be garbage-collected along with its metadata. This ensures
efficient memory usage without leaks.
For Example:
Explanation:
The WeakMap entry is removed automatically when the object is garbage-collected.
68. How can for...of be used to iterate over key-value pairs in a Map?
Answer:
The for...of loop allows you to iterate directly over the key-value pairs of a Map. Each
iteration returns an array containing the key and value.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
344
// name: Alice
// age: 30
Explanation:
Unlike plain objects, Map preserves insertion order and supports direct iteration.
Answer:
The Singleton pattern ensures that only one instance of a class exists throughout the
application. This is useful for shared state or configuration management.
For Example:
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
this.data = "Shared State";
Singleton.instance = this;
}
}
Explanation:
Here, both instance1 and instance2 refer to the same instance, ensuring that state
remains consistent.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
345
Answer:
A WeakSet holds weak references to objects, allowing them to be garbage-collected when
no longer needed. This prevents memory leaks when tracking temporary objects.
For Example:
console.log(weakSet.has(user)); // true
Explanation:
The object inside the WeakSet will be automatically removed from memory when no other
references to it exist.
71. How does Promise.all() work, and when should you use it?
Answer:
Promise.all() is used to run multiple asynchronous operations in parallel. It returns a
single promise that resolves when all the input promises resolve, or rejects if any one of the
promises rejects. This method is useful when you need to wait for multiple tasks (like
fetching different data sources) to complete before proceeding.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
346
Promise.all([fetchUser(), fetchPosts()])
.then(([user, posts]) => console.log(`Fetched: ${user}, ${posts}`))
.catch((error) => console.error("Error fetching data:", error));
Explanation:
Answer:
Promise.race() resolves or rejects as soon as the first promise settles (whether it resolves
or rejects). This is useful in scenarios where you care about the fastest result, such as setting
timeouts or choosing the fastest server response.
For Example:
Promise.race([slowPromise, fastPromise])
.then((result) => console.log(result)) // Fast
.catch((error) => console.error(error));
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
347
73. How can you use JavaScript getters and setters in a class?
Answer:
Getters and setters allow you to encapsulate access to class properties, providing controlled
access and validation. Getters retrieve the value of a property, and setters update it, ensuring
valid data.
For Example:
class User {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(newName) {
if (newName.length > 0) this._name = newName;
else console.error("Name cannot be empty");
}
}
Explanation:
● Getters and setters allow validation and side effects, such as logging changes, to
occur during access or modification of properties.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
348
Answer:
async/await provides a cleaner syntax for asynchronous operations, avoiding deeply nested
.then() callbacks. An async function always returns a promise, and await pauses the
execution until the promise resolves.
For Example:
fetchData();
Explanation:
● await waits for the promise to resolve before moving to the next line.
● This syntax makes asynchronous code easier to read and maintain.
75. How can default and named exports be used together in ES6 modules?
Answer:
ES6 modules allow you to combine named and default exports. A default export represents
the primary functionality of the module, while named exports provide additional utilities or
constants.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
349
Explanation:
Answer:
Method chaining allows calling multiple methods on the same object sequentially. Each
method must return this to enable the next method call.
For Example:
class Calculator {
constructor(value = 0) {
this.value = value;
}
add(n) {
this.value += n;
return this;
}
subtract(n) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
350
this.value -= n;
return this;
}
multiply(n) {
this.value *= n;
return this;
}
getResult() {
console.log(this.value);
return this.value;
}
}
Explanation:
Answer:
The nullish coalescing operator (??) returns the right-hand operand only if the left-hand
operand is null or undefined. This is useful for assigning meaningful default values
without triggering on falsy values like 0 or "".
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
351
Explanation:
● ?? only checks for null or undefined, making it more accurate than || for default
assignments.
78. How can Set be used to remove duplicate elements from an array?
Answer:
A Set stores only unique values. By converting an array into a Set and back into an array, you
can easily remove duplicates.
For Example:
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
Explanation:
● The Set removes duplicates, and the spread operator converts the Set back into an
array.
Answer:
clearTimeout and clearInterval are used to cancel time-based operations started with
setTimeout or setInterval.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
352
Explanation:
Answer:
A Map can use any data type, including objects, as keys. This makes it ideal for associating
metadata or tracking state with objects.
For Example:
console.log(map.has(objKey)); // true
Explanation:
● Unlike regular objects, Map maintains key-value pairs where keys can be objects. This
makes it more powerful for complex data management.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
353
THEORETICAL QUESTIONS
1. What is currying in JavaScript?
Answer:
Currying is a functional programming technique in JavaScript that transforms a function
with multiple parameters into a sequence of functions, each taking a single parameter.
Instead of providing all the arguments at once, you provide them one by one across multiple
function calls. This makes functions more flexible and reusable. It allows developers to create
more specialized functions from generic ones, enabling a clear separation of concerns and
avoiding repetitive code.
For Example:
function curryAdd(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
In the example above, curryAdd takes one argument at a time, but the final result is
calculated when all three functions are invoked.
Answer:
Partial application refers to a function that takes a few arguments initially and returns
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
354
another function that can take the rest of the arguments later. It’s like pre-filling some
arguments of a function so it can be reused efficiently. This pattern enhances code
reusability, especially when some arguments remain constant across multiple calls.
The primary difference between currying and partial application is that currying transforms
functions to accept one argument at a time, whereas partial application uses the original
function but locks in a subset of the parameters.
For Example:
function multiply(a, b, c) {
return a * b * c;
}
Here, partialMultiply binds the first argument to 2 and returns a new function that
expects the remaining two arguments.
Answer:
Memoization is an optimization technique that improves performance by caching the results
of expensive function calls. If the function is called with the same input multiple times, the
cached result is returned instead of recalculating the value. Memoization is helpful in
recursive algorithms such as Fibonacci, where intermediate results are reused.
This technique reduces redundant calculations and improves efficiency, especially when
dealing with time-consuming or resource-intensive functions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
355
if (cache[key]) {
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
};
console.log(fibonacci(10)); // Output: 55
In this example, memoize caches the result of Fibonacci calculations to avoid redundant
function calls.
Answer:
Throttling ensures that a function is called at most once within a specified time interval, even
if it is triggered multiple times. It is commonly used in scenarios where events like scrolling,
resizing, or button clicks need to be optimized. With throttling, the function execution is
spaced out, avoiding unnecessary overhead.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
356
};
}
In this example, the handleScroll function is throttled to ensure it only runs once per
second, even if the scroll event fires continuously.
Answer:
Debouncing ensures that a function is only called after a specified period of inactivity. Unlike
throttling, which spaces out function calls over time, debouncing triggers the function only
once, after the event stops firing. It is commonly used in search fields to prevent unnecessary
API calls with every keystroke.
For Example:
document.getElementById('searchInput').addEventListener('input', handleInput);
This example uses debouncing to ensure that the search query is logged only after the user
stops typing for 500ms.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
357
Answer:
The Singleton pattern restricts the instantiation of a class to a single instance throughout the
application. It ensures that only one object is created, which can be shared among different
parts of the application. This pattern is often used for managing shared resources such as a
database connection or a logging service.
For Example:
Both obj1 and obj2 refer to the same instance, demonstrating the Singleton pattern.
Answer:
The Observer pattern allows objects (observers) to be notified of state changes in another
object (subject). It is commonly used in event-driven programming, where multiple
components need to react to certain events or state changes.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
358
For Example:
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach((observer) => observer.update(data));
}
}
class Observer {
update(data) {
console.log('Received:', data);
}
}
This example demonstrates how observers receive updates from the subject when it sends
notifications.
Answer:
The Factory pattern provides a way to create objects without specifying the exact class of the
object being created. It is useful for scenarios where multiple types of objects share a
common interface but differ in their behavior or properties.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
359
class Car {
constructor() {
this.type = 'Car';
}
}
class Bike {
constructor() {
this.type = 'Bike';
}
}
function VehicleFactory(vehicleType) {
if (vehicleType === 'car') return new Car();
if (vehicleType === 'bike') return new Bike();
}
The factory method abstracts the creation logic, making the code more maintainable.
Answer:
The Module pattern helps encapsulate variables and methods within a function, exposing
only the parts needed through a public API. It prevents global namespace pollution and
organizes code into smaller, manageable units.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
360
getCount: function () {
return counter;
},
};
})();
CounterModule.increment();
console.log(CounterModule.getCount()); // Output: 1
This example encapsulates the counter variable, preventing direct access from outside.
Answer:
A pure function always produces the same output for the same input and has no side effects
(like modifying variables outside its scope). Pure functions are central to functional
programming because they make code easier to reason about and test.
For Example:
function add(a, b) {
return a + b;
}
The add function is pure because it always returns the same result for the same inputs and
does not modify any external state.
Answer:
Immutability means once a variable or object is created, it cannot be changed. Instead of
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
361
modifying an existing object or array, we create a new one with the required changes. This
concept is central to functional programming, where maintaining the state without side
effects is essential. Immutability ensures that no other part of the code accidentally changes
the data, reducing bugs and making code easier to debug.
For Example:
Here, instead of changing arr, we use the spread operator to create a new array newArr. This
prevents unintended modifications to the original data.
Answer:
Function composition is a technique to combine multiple functions into one. It means
passing the result of one function as input to another, chaining them together. This approach
makes code more readable and easier to test since each function handles one task.
For Example:
console.log(composedFunction(2)); // Output: 12
Here, composedFunction adds 2 to the input and then multiplies the result by 3, showing
how functions are combined.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
362
Answer:
The map method transforms an array by applying a function to each of its elements, returning
a new array without modifying the original. It’s useful when you need to modify or format
data elements in an array.
For Example:
Here, map creates a new array where each element is doubled, demonstrating transformation
using the map method.
Answer:
The filter method extracts elements from an array that meet a specific condition, returning
a new array. It’s useful when you need to remove unwanted data or pick specific elements
from an array.
For Example:
In this example, the filter method returns a new array containing only even numbers.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
363
Answer:
The reduce method processes an array and reduces it to a single value using a reducer
function. It’s commonly used for summing values or accumulating data into a single result.
For Example:
console.log(sum); // Output: 10
Here, reduce sums all elements in the array, starting with an initial value of 0.
Answer:
map and forEach both iterate over an array, but their purposes differ. map returns a new array
with transformed elements, while forEach performs an operation for each element but
doesn’t return anything.
For Example:
Here, forEach logs elements individually, while map creates a new array with squared values.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
364
Answer:
The some method checks if at least one element in an array matches a condition. It returns
true if any element satisfies the condition; otherwise, it returns false.
For Example:
Here, some checks if the array contains any even numbers, and returns true as soon as it
finds one.
Answer:
The every method verifies whether all elements in an array meet a specific condition. It
returns true only if every element passes the test; otherwise, it returns false.
For Example:
In this example, every confirms that all numbers in the array are even.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
365
Answer:
A higher-order function is a function that takes another function as an argument or returns a
function. These functions are powerful because they allow abstracting and reusing behavior
across multiple parts of a program.
For Example:
function higherOrderFunction(func) {
return function (name) {
return func(name);
};
}
Here, the higher-order function returns another function that can greet a user by name.
Answer:
A closure is a function that remembers the variables from its outer scope, even after the
outer function has finished executing. Closures allow functions to access and maintain state
between function calls, making them useful for encapsulating private variables and building
factories.
For Example:
function outerFunction(outerVariable) {
return function innerFunction(innerVariable) {
console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
366
Answer:
In synchronous programming, code runs sequentially, meaning each line waits for the
previous one to finish before executing. This behavior is easy to understand but can lead to
performance bottlenecks if a time-consuming operation (like reading files or fetching data)
blocks the execution of the remaining code.
For Example:
console.log('Start');
setTimeout(() => {
console.log('Async Task');
}, 1000);
console.log('End');
// Output: Start, End, Async Task (after 1 second)
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
367
Answer:
JavaScript is single-threaded, meaning it can execute one task at a time. However, with the
help of the event loop, JavaScript can handle asynchronous operations like timers and
network requests. The call stack holds synchronous code, while asynchronous callbacks are
queued in the callback queue or microtask queue. When the call stack is empty, the event
loop picks tasks from these queues to execute.
For Example:
console.log('Start');
setTimeout(() => {
console.log('Timeout Callback');
}, 0);
console.log('End');
// Output: Start, End, Timeout Callback
Although the timer is set to 0ms, it gets queued and only runs after the synchronous code
finishes.
Answer:
A Promise represents the result of an asynchronous operation. It can either be resolved
(fulfilled) or rejected (failed). Promises allow writing asynchronous code without the
complexity of deeply nested callbacks.
For Example:
fetchData
.then((result) => console.log(result)) // Output: Data fetched!
.catch((error) => console.error(error));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
368
The fetchData promise resolves after 1 second, triggering the .then() method to display the
result.
Answer:
async and await simplify working with promises by making asynchronous code look
synchronous. An async function returns a Promise, and await pauses the function execution
until the Promise resolves. This approach reduces callback complexity and makes code more
readable.
For Example:
Here, await makes the function wait for the promise to resolve before proceeding to the next
line.
25. What are JavaScript generators, and how are they used?
Answer:
Generators are special functions that can be paused and resumed. They allow lazy
evaluation, meaning they generate values one at a time instead of all at once. This can be
useful when working with large datasets.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
369
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
Each yield pauses the function and returns a value. Calling next() resumes the generator
from where it left off.
26. What is the difference between shallow copy and deep copy in
JavaScript?
Answer:
A shallow copy copies the top-level properties, but nested objects still reference the original.
A deep copy, however, recursively copies all properties, including nested objects, ensuring
complete independence from the original data.
For Example:
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
shallowCopy.b.c = 42;
Here, both original and shallowCopy reference the same nested object, leading to
unintentional changes.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
370
27. How does JavaScript handle memory leaks, and what are common
causes?
Answer:
JavaScript uses garbage collection to manage memory by automatically removing objects
that are no longer referenced. However, memory leaks occur when references to unused
objects persist, preventing them from being collected.
Common causes:
For Example:
Answer:
Tail recursion occurs when the recursive call is the last operation in the function. Some
JavaScript engines optimize tail-recursive functions to avoid stack overflow, reusing the
same stack frame for each call.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
371
Here, the recursive call to factorial is the last operation, making it tail-recursive.
Answer:
Symbol is a unique and immutable primitive value introduced in ES6. It is often used to
create unique property keys to prevent naming conflicts, especially in large codebases or
when extending objects.
For Example:
Even though both symbols have the same description, they are unique.
Answer:
A Proxy allows developers to intercept and customize operations performed on an object,
such as getting or setting properties. This is useful for validation, logging, or data binding in
frameworks like Vue.js.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
372
obj[prop] = value;
return true;
},
};
This Proxy logs every get and set operation, providing a way to monitor and control access to
the object.
31. What is the purpose of the Reflect API in JavaScript, and how is it
used?
Answer:
The Reflect API provides built-in methods to operate on objects (e.g., accessing, setting, or
defining properties) in a more predictable way. Unlike regular property access or assignment
(e.g., obj.name), Reflect methods return true or false to indicate success or failure. It helps
developers create robust code by standardizing object operations and is often used with
Proxies to implement default behaviors.
For Example:
The Reflect methods provide cleaner alternatives to regular operations, especially when
working with Proxies or advanced object manipulation.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
373
32. How do JavaScript WeakMap and WeakSet differ from Map and Set?
Answer:
● WeakMap and WeakSet store only objects (no primitives) and weakly reference
them, allowing these objects to be garbage collected when no other references exist.
● Map and Set, on the other hand, strongly reference the objects, preventing their
garbage collection.
For Example:
In the example, the object will be garbage collected once it is no longer referenced, as
WeakMap holds only weak references, preventing memory leaks.
33. What is the difference between call, apply, and bind methods in
JavaScript?
Answer:
● call: Invokes a function with a specific this context and individual arguments.
● apply: Works like call but accepts an array of arguments.
● bind: Returns a new function with the specified this context and optional
arguments, but doesn't execute it immediately.
For Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
374
These methods allow developers to explicitly control the this value of a function, which is
crucial in complex JavaScript applications.
Answer:
● Promise.all resolves only when all promises in the array are fulfilled (or rejects if any
of them fails).
● Promise.race resolves as soon as the first promise in the array settles (either resolved
or rejected).
For Example:
Promise.all waits for all promises, but Promise.race returns the result of the first one to
settle.
35. What are mixins in JavaScript, and how are they implemented?
Answer:
Mixins are a way to share behavior across multiple classes or objects without inheritance.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
375
JavaScript doesn’t support multiple inheritance, so mixins allow code reuse by copying
methods and properties into objects or classes.
For Example:
const sayHelloMixin = {
sayHello() {
console.log(`Hello, ${this.name}`);
},
};
class User {
constructor(name) {
this.name = name;
}
}
Object.assign(User.prototype, sayHelloMixin);
Mixins enable behavior sharing across unrelated objects by assigning properties dynamically.
Answer:
Throttling ensures that a function is called at most once in a specified period, no matter how
often the triggering event occurs. This is especially useful for scroll or resize events, where
frequent updates can degrade performance.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
376
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
In the example, the log function is called only once per second, no matter how often the
scroll event fires.
Answer:
Memoization stores the results of expensive function calls. If the function is called with the
same inputs again, the cached result is returned. This improves performance by avoiding
repeated calculations.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
377
Answer:
● == compares values with type coercion, meaning it converts the operands to the
same type before comparing.
● === performs strict comparison, ensuring that both the type and value are the same.
For Example:
Answer:
Destructuring allows extracting values from arrays or objects into variables directly, reducing
boilerplate code and improving readability.
For Example:
In the example, name and age are extracted from the user object and assigned to variables
with the same names.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
378
Answer:
Dynamic import allows importing JavaScript modules at runtime, rather than at the
beginning of the script. It is useful for code splitting, as it loads modules only when needed,
improving initial load time.
For Example:
loadModule();
Here, myModule.js is imported only when loadModule is called, ensuring that the module is
loaded on demand, reducing the initial bundle size.
SCENARIO QUESTIONS
41. Scenario: You need to create a function that adds three numbers, but
you want to partially apply some of its parameters for reuse.
Question: How can you use partial application to simplify this process?
Answer:
Partial application allows you to lock in some arguments of a function, creating a new
function that only requires the remaining arguments. This is particularly helpful when certain
values remain constant across multiple calls. In JavaScript, this is commonly implemented
using closures or the bind() method. By partially applying parameters, you avoid repetitive
code, improving both efficiency and readability.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
379
function add(a, b, c) {
return a + b + c;
}
Here, the addFive function always adds 5 as the first parameter, requiring only the remaining
two inputs during function calls.
Answer:
Memoization improves performance by caching the results of previous function calls and
returning the cached value when the same inputs are encountered. This is useful in recursive
algorithms like Fibonacci or factorial, where the same calculations may be repeated multiple
times. Memoization reduces time complexity by storing and reusing results, avoiding
redundant work.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
380
return result;
};
};
console.log(fibonacci(10)); // Output: 55
In this example, memoization saves intermediate results, avoiding redundant calculations for
the Fibonacci sequence.
43. Scenario: You want to ensure a button click event handler is not
triggered excessively when the user clicks repeatedly.
Question: How can you use throttling to control the frequency of function calls?
Answer:
Throttling ensures a function runs at most once within a specified interval, even if it is
triggered multiple times. This is useful for optimizing performance in events that can fire
continuously, like scrolling, resizing, or button clicks. Throttling prevents system overload by
spacing out function executions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
381
document.getElementById('myButton').addEventListener('click', handleClick);
Here, the handleClick function executes only once every two seconds, even if the button is
clicked repeatedly.
44. Scenario: You need a central object to manage subscriptions and notify
subscribers when an event occurs.
Answer:
The Observer pattern allows a subject to maintain a list of observers and notify them of state
changes. It is useful in event-driven architectures, where multiple components react to
specific events. This pattern ensures a decoupled design, where the subject and observers
are loosely connected.
For Example:
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach((observer) => observer.update(data));
}
}
class Observer {
update(data) {
console.log('Received:', data);
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
382
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notify('Event triggered');
// Output: Received: Event triggered
// Received: Event triggered
In this example, both observers receive notifications whenever the subject triggers an event.
45. Scenario: You need to ensure that only one instance of a logging service
exists across the application.
Question: How can you use the Singleton pattern to achieve this?
Answer:
The Singleton pattern ensures that only one instance of a class exists and provides a global
point of access to it. This pattern is useful for services like logging, configuration
management, or database connections, where creating multiple instances is unnecessary or
inefficient.
For Example:
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
log(message) {
this.logs.push(message);
console.log('Log:', message);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
383
}
}
logger1.log('First message');
logger2.log('Second message');
Both logger1 and logger2 refer to the same instance, ensuring consistent logging across
the application.
46. Scenario: You need a function that processes a list of user objects, but
only the active ones should be included in the result.
Question: How can you use the filter method to implement this?
Answer:
The filter method creates a new array containing only elements that meet a specific
condition. This method is ideal for extracting subsets of data based on certain criteria.
For Example:
const users = [
{ name: 'Alice', active: true },
{ name: 'Bob', active: false },
{ name: 'Charlie', active: true }
];
console.log(activeUsers);
// Output: [{ name: 'Alice', active: true }, { name: 'Charlie', active: true }]
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
384
In this example, only users marked as active are included in the result.
47. Scenario: You want to encapsulate a counter so that its value can only
be incremented through a provided method.
Answer:
The Module pattern allows encapsulation of data and methods, exposing only what is
necessary through a public API. This pattern helps control access to variables and provides a
clean, maintainable structure.
For Example:
CounterModule.increment();
CounterModule.increment();
console.log(CounterModule.getCount()); // Output: 2
In this example, the count variable is private and can only be modified using the increment
method.
48. Scenario: You need to combine two functions that manipulate strings
to create a single transformation.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
385
Answer:
Function composition allows you to combine smaller functions into a single function that
performs a sequence of operations. It helps keep code modular and easy to maintain.
For Example:
Here, the shout function applies both toUpperCase and exclaim transformations.
Answer:
The reduce method accumulates values from an array into a single result. It is useful for
operations like summing, finding averages, or computing products.
For Example:
console.log(sum); // Output: 15
In this example, reduce sums the array elements starting with an initial value of 0.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
386
50. Scenario: You need a function that converts an array of names into
uppercase.
Question: How can the map method be used for this transformation?
Answer:
The map method creates a new array by applying a function to each element of the original
array. It is useful for transforming data without altering the original collection.
For Example:
Here, the map method converts each name to uppercase, creating a new array with the
transformed values.
51. Scenario: You need to ensure that a function only executes after a user
stops typing in a search field.
Answer:
Debouncing ensures a function is invoked only after a specified delay has passed since the
last event. It is commonly used in search input fields to prevent excessive API calls or
computations as the user types. If the event (like input) is triggered again before the delay
ends, the timer resets. This technique improves performance and reduces server load.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
387
document.getElementById('searchInput').addEventListener('input', handleSearch);
Here, the search function triggers only after the user stops typing for 500ms, ensuring only
necessary API calls are made.
52. Scenario: You want to create a unique identifier for object properties
that won’t conflict with other keys.
Answer:
A Symbol is a unique and immutable data type used primarily for creating non-colliding
property keys. Even if two symbols share the same description, they are treated as distinct.
This prevents accidental property name conflicts, which is useful when adding metadata or
private fields to objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
388
Symbols help keep properties hidden from standard iteration (like Object.keys) and avoid
key collisions.
Answer:
Promise.all waits for all promises to either resolve or reject. It returns a single promise that
resolves with an array of results when all promises succeed or rejects with the reason if any
promise fails. This is useful for parallel execution of asynchronous operations.
For Example:
Here, both promises are executed concurrently, and their results are combined into an array
when both succeed.
54. Scenario: You need to run asynchronous tasks, but only care about the
result of the first one to complete.
Answer:
Promise.race returns a promise that resolves or rejects as soon as the first promise settles
(either resolved or rejected). This is useful when you need the fastest result among several
operations.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
389
Here, promise2 resolves first, so its result is returned, even though promise1 is still pending.
Answer:
Optional chaining (?.) allows you to access nested properties safely, without worrying about
whether each property in the chain exists. If a property is undefined or null, the expression
short-circuits and returns undefined instead of throwing an error. This makes the code more
robust.
For Example:
56. Scenario: You need to split a large function into smaller modules and
load them dynamically based on user actions.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
390
Answer:
Dynamic imports allow JavaScript to load modules on demand instead of during the initial
load. This improves performance by loading only the necessary code at runtime, enabling
code-splitting for better application performance.
For Example:
document.getElementById('loadButton').addEventListener('click', loadModule);
Here, the module is loaded only when the button is clicked, reducing the initial load time and
improving performance.
57. Scenario: You need to maintain a private counter and expose only
limited methods to interact with it.
Answer:
Closures allow you to create private variables that are only accessible through specific
functions. A closure is formed when an inner function retains access to the outer function’s
variables, even after the outer function has executed. This pattern is useful for encapsulating
state.
For Example:
function createCounter() {
let count = 0;
return {
increment: () => count++,
getCount: () => count,
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
391
Here, count is private and only accessible through the provided methods.
58. Scenario: You want to reuse multiple behaviors across different classes
without using inheritance.
Answer:
Mixins allow you to copy behaviors from one object or class to another, enabling code reuse
without inheritance. This technique provides flexibility to extend multiple classes with shared
functionality.
For Example:
const sayHelloMixin = {
sayHello() {
console.log(`Hello, ${this.name}`);
},
};
class Person {
constructor(name) {
this.name = name;
}
}
Object.assign(Person.prototype, sayHelloMixin);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
392
59. Scenario: You need to encapsulate multiple services into a single class
with a clear public interface.
Question: How can the Factory pattern be used to simplify object creation?
Answer:
The Factory pattern simplifies object creation by encapsulating the logic within a factory
function. This pattern decouples the client code from the specific class implementations,
making the code more maintainable and scalable.
For Example:
class Car {
constructor() {
this.type = 'Car';
}
}
class Bike {
constructor() {
this.type = 'Bike';
}
}
function VehicleFactory(vehicleType) {
if (vehicleType === 'car') return new Car();
if (vehicleType === 'bike') return new Bike();
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
393
Question: How can the Module pattern be used to manage component state?
Answer:
The Module pattern helps encapsulate state and behavior within a component, exposing
only the necessary methods through a public API. This ensures controlled access to the
state, promoting a clean, maintainable structure.
For Example:
CounterComponent.increment();
console.log(CounterComponent.getCount()); // Output: 1
Here, the counter state is encapsulated, ensuring that it can only be modified using the
provided methods.
61. Scenario: You need to create a function that performs an expensive API
request but ensures it is only executed once, no matter how many times
the function is called.
Question: How can you use the Singleton pattern to handle this scenario?
Answer:
The Singleton pattern ensures that a class or function is instantiated only once. This is useful
when making expensive or resource-heavy API calls, ensuring the function result is reused
without redundant executions. You can use closures to store the result of the first API call
and return the cached value on subsequent calls.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
394
return {
getInstance: async function () {
if (!instance) {
instance = await createInstance();
}
return instance;
},
};
})();
In this example, the API request is made only once, and subsequent calls return the cached
result.
Answer:
Throttling limits the frequency at which a function executes by ensuring that it runs at most
once every specified interval. This is helpful in scenarios like analytics, where continuous data
sending could overload the server.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
395
let inThrottle;
return function (...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
In this example, analytics data is sent only once every 5 seconds, regardless of how often it is
triggered.
Question: How can you use functional programming techniques to achieve this?
Answer:
In functional programming, immutability means that objects are not modified directly.
Instead, new objects are created with the necessary updates. Using the spread operator or
deep cloning techniques ensures that nested objects remain immutable.
For Example:
const user = {
name: 'Alice',
address: {
city: 'Wonderland',
postalCode: '12345'
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
396
};
const updatedUser = {
...user,
address: { ...user.address, city: 'Dreamland' }
};
Here, the original object remains unchanged, and the new object reflects the updated city
value.
64. Scenario: You need to repeatedly fetch data but avoid unnecessary
duplicate requests.
Answer:
Memoization can be applied to API requests to cache responses and avoid redundant
network calls for the same input. If the same endpoint is requested again, the cached result
is returned.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
397
Here, the API response is cached, and subsequent calls return the cached data.
Question: How can you use async and await to achieve this?
Answer:
The async and await keywords allow you to write asynchronous code in a sequential
manner, improving readability. Each task waits for the previous one to complete before
starting the next.
For Example:
fetchTodosSequentially();
// Logs todo1 and todo2 sequentially, ensuring the first completes before the
second starts
Here, the second request starts only after the first one completes.
66. Scenario: You need to create a reusable button component that tracks
how many times it has been clicked.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
398
Answer:
Closures allow encapsulating the click count variable inside the function, ensuring that it is
not accessible directly but is still maintained across invocations.
For Example:
function createButtonCounter() {
let count = 0;
return () => {
count++;
console.log(`Button clicked ${count} times`);
};
}
document.getElementById('myButton').addEventListener('click', buttonClickHandler);
Each click increments the internal count variable, which is preserved across multiple clicks.
67. Scenario: You need to observe changes in an object and notify specific
listeners about the updates.
Question: How can the Observer pattern be implemented to handle this scenario?
Answer:
The Observer pattern allows objects to subscribe to a subject and receive notifications when
the subject’s state changes. This pattern is useful for tracking changes in state or events.
For Example:
class Observable {
constructor() {
this.observers = [];
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
399
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach((observer) => observer(data));
}
}
observable.notify('State changed');
// Output: Observer 1: State changed
// Observer 2: State changed
This example demonstrates how multiple observers react to state changes in the observable.
68. Scenario: You need to generate a sequence of numbers, but the next
value should be computed only when required.
Answer:
Generators allow lazy evaluation, meaning the next value is generated only when requested.
This is useful for sequences or computations where calculating all values upfront is
unnecessary.
For Example:
function* numberGenerator() {
let i = 0;
while (true) {
yield i++;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
400
Here, each next() call generates the next number in the sequence on demand.
Answer:
The Module pattern helps encapsulate logic, exposing only necessary methods through a
public API. This ensures that internal state and behavior are hidden from other parts of the
application.
For Example:
return {
setActiveTab: (index) => {
activeTab = index;
console.log(`Active tab set to ${activeTab}`);
},
getActiveTab: () => activeTab,
};
})();
TabsModule.setActiveTab(2);
console.log(TabsModule.getActiveTab()); // Output: 2
This example encapsulates the active tab logic, providing controlled access to it.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
401
Question: How can JavaScript Proxies be used to intercept and modify object
behavior?
Answer:
Proxies allow you to intercept operations on an object, such as property access or
assignment, enabling custom behavior (e.g., logging or validation).
For Example:
In this example, the proxy logs all property accesses and modifications, giving full control
over object behavior.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
402
Answer:
Using async and await, you can ensure that asynchronous tasks execute sequentially in a
readable, synchronous-like manner. Each await pauses the function execution until the
awaited promise resolves, ensuring the next operation only starts after the previous one
finishes. This is ideal when tasks depend on each other, such as fetching user data and using
it to retrieve related information (e.g., user posts).
For Example:
fetchUserData();
// Output: First logs user data, then logs user posts
In this example, the second request waits for the user data before proceeding to fetch their
posts.
72. Scenario: You need to dynamically validate and control object property
access.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
403
Answer:
Proxies allow intercepting operations on an object, such as getting or setting properties, by
using custom logic. For example, you can ensure that only valid data is assigned to a property
by adding validation logic in the set trap.
For Example:
try {
proxy.age = 'thirty'; // Throws an error
} catch (e) {
console.error(e.message); // Output: Age must be a number
}
Here, the proxy enforces that the age property can only accept numbers, ensuring data
consistency.
73. Scenario: You want to ensure a complex function only executes if the
conditions are met, without nesting multiple if statements.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
404
Answer:
Function composition allows chaining multiple functions in a way that avoids deep nesting.
Each function performs a specific task, and the final composed function checks conditions
and acts accordingly.
For Example:
The composition ensures that multiple conditions are checked sequentially without nested
logic.
74. Scenario: You need to optimize multiple event listeners that fire
frequently, such as scroll events.
Question: How can you use both throttling and debouncing to manage event
listeners efficiently?
Answer:
Throttling ensures a function runs at most once every specified interval, while debouncing
delays execution until the event stops firing. Using both techniques, you can efficiently
manage event listeners.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
405
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);
Here, throttling and debouncing optimize event handling, reducing unnecessary calls.
75. Scenario: You need a factory function that creates objects with behavior
based on a configuration.
Answer:
The Factory pattern abstracts object creation, allowing objects to be generated based on
input configurations. This simplifies complex object creation and promotes reusable code.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
406
function AnimalFactory(type) {
const animals = {
dog: () => ({ type: 'dog', sound: () => console.log('Bark!') }),
cat: () => ({ type: 'cat', sound: () => console.log('Meow!') }),
};
return animals[type] ? animals[type]() : null;
}
Here, the factory function dynamically creates animal objects based on the provided type.
Answer:
The reduce method applies a reducer function to each element of the array, accumulating
the result in a single value. This is useful for operations like summing, averaging, or merging
objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
407
77. Scenario: You need to merge two objects without mutating the
originals.
Answer:
Using the spread operator, you can merge objects into a new one without modifying the
original objects, ensuring immutability.
For Example:
This approach preserves the original objects and returns a new merged object.
Answer:
Proxies allow you to intercept property changes, making it possible to log or react to
modifications in real time.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
408
},
};
Answer:
Promise.allSettled waits for all promises to settle, returning an array of results, regardless
of whether the promises resolve or reject.
For Example:
This ensures all results are handled, even if some operations fail.
80. Scenario: You need to generate unique IDs for multiple objects to avoid
conflicts.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
409
Answer:
Symbols generate unique values, ensuring that property keys do not collide, even with the
same description.
For Example:
const obj = {
[id1]: 'ID 1',
[id2]: 'ID 2',
};
console.log(obj[id1]); // Output: ID 1
console.log(obj[id2]); // Output: ID 2
Here, the symbols ensure that each ID key is unique, even though they share the same
description.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
410
THEORETICAL QUESTIONS
1. What is a component in React.js?
Answer:
In React, a component is a building block of the user interface (UI). It helps split the UI into
smaller, reusable pieces, each of which can manage its own logic and appearance.
Components can be functional (simpler and without lifecycle methods) or class-based (which
can use lifecycle methods). Functional components are preferred today because they are
simpler and, with hooks, allow managing state and side effects.
Components also accept props (short for "properties") to receive data from their parent
components, making them dynamic and reusable.
For Example:
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage:
<Welcome name="Shardul" />;
Here, Welcome is a functional component that takes name as a prop and displays it. This
component can be reused with different names passed as props.
Answer:
JSX stands for JavaScript XML. It allows writing HTML-like syntax directly inside JavaScript,
which makes it easy to design UI components. JSX also allows embedding JavaScript
expressions inside {} within the HTML-like structure. Under the hood, JSX is transformed by a
compiler like Babel into plain JavaScript calls using React.createElement().
JSX improves code readability by co-locating the markup with the logic.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
411
For Example:
Answer:
In React, state and props are two ways of managing data in components:
1. State: This is internal to the component and can be changed by the component itself
using hooks like useState. When state changes, the component re-renders
automatically.
2. Props: These are read-only properties passed from a parent component to a child
component. They allow data to flow down the component tree but cannot be
modified by the child component.
For Example:
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Here, count is a state managed by the Counter component. Clicking the button updates the
state, causing the component to re-render with the new value.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
412
Answer:
React Hooks are functions that let you use state and lifecycle methods inside functional
components. Before hooks, these features were only available in class components. Two of
the most common hooks are:
For Example:
function App() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Dependency array to control re-runs
return (
<button onClick={() => setCount(count + 1)}>Click Me</button>
);
}
In this example, useEffect updates the document title whenever the count changes.
Answer:
React Router is a routing library used in React applications to manage navigation between
different views or components. It provides features like:
● Defining routes
● Navigating between pages without full-page reloads (SPA behavior)
● Handling dynamic URLs and route parameters
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
413
It uses the history API to push and replace routes without page refresh.
For Example:
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
This code demonstrates routing between two components, Home and About.
Answer:
The Context API in React provides a way to share data across multiple components without
passing props down every level of the component tree. This prevents prop drilling, which
happens when props are passed through many layers unnecessarily. The createContext
function creates a context, and the Provider component supplies the context value.
For Example:
function App() {
return (
<UserContext.Provider value={{ name: 'Shardul' }}>
<User />
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
414
</UserContext.Provider>
);
}
function User() {
const user = React.useContext(UserContext);
return <h1>Hello, {user.name}!</h1>;
}
Here, the User component consumes the UserContext without receiving props directly.
Answer:
Vue.js is a progressive framework for building user interfaces. It focuses on the ViewModel
layer and is easy to integrate with other libraries. Vue is known for:
Answer:
Data binding in Vue.js connects the UI elements to the data model. Vue supports:
● One-way data binding: Binding data from the component to the view using v-bind.
● Two-way data binding: Binding data between the view and the component using v-
model.
For Example:
<div id="app">
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
415
<script>
new Vue({
el: '#app',
data: {
message: 'Hello, Vue!'
}
});
</script>
The v-model directive ensures that changes in the input are reflected in the message, and
vice versa.
Answer:
Vuex is a state management library for Vue.js. It provides a centralized store where the state
of the entire application is kept, ensuring consistent state across components. It allows:
Answer:
Node.js is a JavaScript runtime built on Chrome’s V8 engine. It allows running JavaScript
outside the browser, making it ideal for server-side programming. Node.js is asynchronous
and event-driven, meaning it can handle multiple requests without blocking the main
thread. This makes it suitable for real-time applications.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
416
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
This code creates a simple HTTP server that responds with "Hello, World!" when accessed.
Answer:
Express.js is a minimal and flexible web framework built on top of Node.js that helps in
building web applications and APIs easily. It abstracts much of the underlying complexities of
creating a Node.js server, providing a simpler way to handle routing, middleware, and HTTP
requests. Express is widely used for building RESTful APIs, serving web pages, or managing
real-time applications using frameworks like Socket.IO. Its modular nature allows developers
to add only the necessary components, keeping the application lightweight.
For Example:
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
417
Here, an Express server is created that listens on port 3000. When someone accesses the root
URL (/), it responds with “Hello, World!”.
Answer:
Middleware functions in Express.js are functions that execute during the lifecycle of a
request to the server. These functions have access to the request (req), response (res), and a
function called next, which passes control to the next middleware in the sequence.
Middleware is commonly used for logging, authentication, handling errors, and modifying
request/response objects.
For Example:
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
In this example, the middleware logs every request before the response is sent. The call to
next() ensures the request moves to the next middleware or route handler.
Answer:
File handling in Node.js refers to working with the file system to perform operations like
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
418
reading, writing, updating, deleting, and renaming files. Node.js provides the fs (File System)
module, which supports both synchronous and asynchronous file operations. Asynchronous
operations are preferred since they do not block the event loop.
For Example:
const fs = require('fs');
This example creates a new file named example.txt and writes data to it. If an error occurs, it
is handled with the callback function.
Answer:
Two-way data binding in Angular synchronizes data between the model (component) and
the view (template). When the user updates the value in the view, it reflects in the model
automatically, and vice versa. This is achieved using the ngModel directive, which allows
Angular to connect form elements with the component’s data properties.
For Example:
In this example, whatever the user types into the input field will be reflected in the name
variable in the component, and the paragraph will display the updated value in real-time.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
419
Answer:
A service in Angular is a class that contains reusable business logic or data-sharing logic,
which can be injected into components or other services. Services make the code modular,
maintainable, and testable by separating logic from components. Angular uses dependency
injection (DI) to provide instances of services where needed.
For Example:
@Injectable({
providedIn: 'root', // Automatically provided throughout the app
})
export class DataService {
getData() {
return ['Angular', 'React', 'Vue'];
}
}
Here, the DataService is marked as @Injectable, meaning it can be injected into other
components using Angular’s dependency injection.
Answer:
Dependency Injection (DI) is a design pattern in Angular that provides a way to supply
components with their dependencies, such as services. Angular’s DI system ensures that
services or dependencies are instantiated only once and reused where needed. This
promotes modular, maintainable, and testable code by keeping component logic separate
from dependencies.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
420
Answer:
The Vue CLI (Command Line Interface) is a tool that helps developers scaffold and configure
Vue.js projects quickly. It provides a project structure with all necessary tools (like Webpack
and Babel) configured out of the box. Vue CLI also supports hot module replacement (HMR),
which updates parts of the page without a full reload, speeding up development.
For Example:
This command initializes a new Vue project with a pre-configured structure and tools for
development and production builds.
Answer:
The event loop in Node.js is the mechanism that allows it to handle non-blocking,
asynchronous operations. Even though JavaScript is single-threaded, the event loop enables
Node.js to perform I/O operations in the background, such as reading files or making
network requests, without blocking the main thread. The event loop continuously checks for
pending tasks and executes their callbacks when ready.
Answer:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
421
This command creates a new React application without requiring the developer to install
create-react-app globally.
Answer:
Directives in Angular are instructions that tell Angular to do something with a DOM
element. There are three types of directives:
1. Structural directives: These alter the structure of the DOM, adding or removing
elements (e.g., *ngIf, *ngFor).
2. Attribute directives: These change the behavior or appearance of an element (e.g.,
ngClass, ngStyle).
3. Component directives: Custom components are also treated as directives because
they attach behavior to a template.
For Example:
The *ngIf directive adds or removes the paragraph based on the value of isVisible. If
isVisible is true, the paragraph is displayed; otherwise, it is removed from the DOM.
21. What is the Virtual DOM in React, and how does it improve
performance?
Answer:
The Virtual DOM is an abstraction of the real DOM. It allows React to manage UI updates
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
422
This process, known as diffing, ensures smooth and fast updates, enhancing the
performance of the app, especially for large, dynamic interfaces.
Answer:
A Higher-Order Component (HOC) is a pattern for code reuse in React. It takes a
component as input and returns a new component with additional logic. This pattern allows
you to reuse functionality across multiple components without duplicating code. HOCs are
often used to add cross-cutting concerns such as authentication, logging, or fetching data.
HOCs do not modify the original component but instead wrap it in another component with
extra logic.
For Example:
Here, LoggedHello is an enhanced version of Hello that logs messages whenever it renders.
23. What are React Portals, and when should you use them?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
423
Answer:
React Portals allow rendering a component’s content outside its natural parent component
hierarchy in the DOM. This is helpful when dealing with modals, tooltips, and pop-ups, as it
allows better control over positioning and z-index.
For Example:
ReactDOM.createPortal(
<div className="modal">I am rendered via a Portal!</div>,
document.getElementById('portal-root')
);
The portal ensures that the modal content is rendered at the portal-root div, avoiding any
layout issues from being nested inside deeply nested parent components.
24. What is lazy loading in React, and how do you implement it?
Answer:
Lazy loading in React delays the loading of a component until it is required. This is
particularly useful in large applications where loading all components upfront can slow
down the initial page load. With lazy loading, components are loaded on-demand, improving
performance by reducing the bundle size.
For Example:
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
424
In this example, MyComponent is not loaded until it’s needed. While it loads, the fallback UI
(Loading...) is shown.
25. What are Vue.js lifecycle hooks, and why are they important?
Answer:
Lifecycle hooks in Vue.js allow developers to execute code at different stages of a
component’s life. These hooks include:
These hooks are useful for fetching data, setting up subscriptions, and cleaning up
resources.
For Example:
export default {
mounted() {
console.log('Component has been mounted.');
},
};
Answer:
The watch property in Vue.js is used to monitor changes in reactive data and execute code in
response. Unlike computed properties, which cache values, watch functions are used when
you need to perform side effects like making API calls or updating the DOM.
For Example:
export default {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
425
data() {
return { count: 0 };
},
watch: {
count(newValue) {
console.log('Count changed to:', newValue);
},
},
};
Here, whenever count changes, the watch function logs the new value.
Answer:
In Express.js, middleware functions are executed in sequence using chaining. Each
middleware function must call the next() function to pass control to the next middleware.
This pattern allows for complex request flows where tasks like logging, authentication, and
validation happen in stages.
For Example:
Here, both middleware functions execute sequentially before the final response is sent.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
426
Answer:
For Example:
console.log('Start');
console.log('End');
Output:
Start
End
Next Tick
Set Immediate
Answer:
Angular provides two ways to manage forms:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
427
This example creates a form with two fields: name and age.
Answer:
ngx-bootstrap is an Angular library that provides ready-to-use Bootstrap components. It
allows developers to use Bootstrap elements like modals, dropdowns, and date pickers in
Angular applications, with seamless integration of Angular’s templating and reactive forms.
For Example:
@NgModule({
imports: [BsDropdownModule.forRoot()],
})
export class AppModule {}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
428
Answer:
Server-Side Rendering (SSR) in React involves rendering the initial HTML of a component on
the server and sending it to the client. This results in a faster first contentful paint (FCP),
enhancing SEO because search engines can easily index the pre-rendered HTML. On the
other hand, Client-Side Rendering (CSR) sends a blank HTML shell to the client and renders
components dynamically through JavaScript. While CSR offers smoother transitions for
interactive elements, it may result in slower initial load times.
32. What is the difference between Redux and Context API in React?
Answer:
● Redux: A state management library that maintains the entire application state in a
single store. State transitions in Redux are predictable because they are driven by
actions and reducers. Redux is ideal for managing global states that are complex and
shared across many components (e.g., authentication state, user profiles).
● Context API: Built into React, it is a simpler way to share state across components
without passing props manually. While Context API works well for small applications
or lightweight global state like theming, it becomes cumbersome for large
applications with deep component hierarchies.
33. What are Vue.js Mixins, and how are they used?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
429
Answer:
In Vue.js, mixins provide a way to share reusable logic between components. Components
that use the same logic (like handling API responses or timers) can inherit this functionality
from a mixin. Mixins can contain data, methods, computed properties, and lifecycle hooks.
When conflicts arise between component methods and mixin methods, the component’s
implementation takes precedence.
For Example:
const dataMixin = {
data() {
return {
message: 'Mixin message',
};
},
methods: {
showMessage() {
console.log(this.message);
},
},
};
export default {
mixins: [dataMixin],
mounted() {
this.showMessage(); // Logs: Mixin message
},
};
This component inherits data and methods from dataMixin and uses them during its
lifecycle.
Answer:
The Observer Pattern in Node.js is implemented through EventEmitters and streams. In this
pattern, objects (observers) subscribe to events emitted by a subject. When an event occurs,
all subscribed observers are notified. Streams follow this pattern by emitting data events in
chunks, notifying listeners about each chunk received.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
430
This design allows Node.js to handle asynchronous I/O efficiently, as streams can process
data incrementally without waiting for the complete data to arrive.
35. What is Dependency Injection in Angular, and how does it differ from
Service Locator?
Answer:
In Dependency Injection (DI), components receive their dependencies externally through
injection. Angular’s DI framework allows you to inject services into components or other
services, promoting loose coupling and better testability. In contrast, the Service Locator
Pattern requires components to request their dependencies from a centralized registry,
which leads to tighter coupling between components and dependencies.
Answer:
Node.js uses the child_process module to create child processes that run shell commands
or separate scripts. These processes allow the main event loop to remain non-blocking,
enabling tasks like heavy computations or file handling to run in parallel.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
431
This example runs the ls command, listing the files in the current directory.
Answer:
Angular Guards are functions that control access to routes in an application. These guards
ensure that users can only access specific routes if certain conditions are met (e.g., being
authenticated). There are several types of route guards:
For Example:
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
canActivate(): boolean {
return confirm('Are you logged in?');
}
}
This example asks the user for confirmation before navigating to a protected route.
Answer:
IndexedDB is a low-level, asynchronous storage API that allows storing structured data in
the browser. It is useful for large datasets, such as user data or offline applications, since it
supports transactions and queries. Unlike LocalStorage, which only stores key-value pairs
synchronously, IndexedDB supports object stores for complex data structures and provides
greater storage capacity.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
432
39. How does WebSocket work in Node.js, and what are its advantages?
Answer:
WebSocket is a protocol that provides full-duplex communication over a single TCP
connection. Unlike HTTP, which is request-response-based, WebSocket allows both the
client and server to send messages at any time. This makes it ideal for real-time
applications, such as chat apps or stock trading platforms.
For Example:
This example sets up a WebSocket server that sends a welcome message to new
connections.
Answer:
A Progressive Web App (PWA) is a web application that provides offline capabilities, push
notifications, and installability like a native mobile app. PWAs leverage service workers to
cache resources, allowing them to function offline. Angular makes it easy to add PWA
capabilities using the @angular/pwa package.
For Example:
ng add @angular/pwa
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
433
This command configures service workers and updates the Angular project to behave as a
PWA. Once configured, the app can cache static assets and serve them offline, improving
performance and reliability.
SCENARIO QUESTIONS
41. Scenario: A developer is building a product listing page in React and
needs to display each product in a reusable way.
Question: How can React components and props help in creating reusable product cards for
the product listing page?
Answer:
In React, components allow developers to create reusable elements that encapsulate both
logic and UI. To customize the output of each component, props (short for properties) are
passed to them. This way, the product listing page can use the same ProductCard
component for multiple products by passing product-specific data via props.
Using components and props promotes reusability and clean code by avoiding duplicate
structures across the application.
For Example:
return (
<div>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
434
In this example, the ProductCard component is reused for multiple products by passing the
name and price props.
42. Scenario: You need to toggle between a light and dark theme in a
React application.
Question: How can useState and useEffect hooks be used to toggle between light and
dark themes in React?
Answer:
In React, the useState hook manages the theme’s current state (light or dark). The
useEffect hook is used to apply the selected theme to the document body whenever the
state changes. This setup allows users to switch between themes and ensures the theme
persists throughout the session.
For Example:
useEffect(() => {
document.body.className = theme;
}, [theme]);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Theme
</button>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
435
Here, useState tracks the current theme, and useEffect updates the document's body class
whenever the theme changes.
43. Scenario: A React application needs multiple pages, such as Home and
About, with navigation between them.
Question: How can React Router be used to implement routing between the Home and
About pages?
Answer:
React Router provides a way to manage client-side navigation in single-page applications
(SPA). It allows the creation of multiple pages, each represented by a route, without
reloading the entire page. Using Routes and Link, developers can define routes and provide
seamless navigation between components.
For Example:
function Home() {
return <h2>Home Page</h2>;
}
function About() {
return <h2>About Page</h2>;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
436
</Router>
);
}
In this example, Router wraps the entire app, and links allow navigation between the Home
and About pages.
44. Scenario: You need to manage a large state tree in a Vue.js application.
Question: How can Vuex be used to manage global state effectively across components?
Answer:
Vuex is a state management library for Vue.js applications that provides a centralized store
for managing global state. It allows multiple components to access and modify shared state
without relying on prop drilling. State changes in Vuex are handled through mutations and
actions to ensure predictable state transitions.
For Example:
// store.js
import { createStore } from 'vuex';
// main.js
import { createApp } from 'vue';
import { store } from './store';
import App from './App.vue';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
437
// App.vue
<template>
<div>
<p>Counter: {{ $store.state.counter }}</p>
<button @click="$store.commit('increment')">Increment</button>
</div>
</template>
Here, the Vuex store manages the state, and the component interacts with the state through
mutations.
45. Scenario: You are building an Express.js API that requires logging
request details.
Question: How can middleware be used in Express.js to log incoming HTTP requests?
Answer:
Middleware functions in Express.js are functions that execute during the request-response
lifecycle. Logging incoming requests helps in monitoring and debugging. Middleware
functions can be registered using app.use() to run on every request.
For Example:
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} request to ${req.url}`);
next();
});
In this example, the middleware logs each request before passing control to the next
handler.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
438
46. Scenario: You need to create a service in Angular to share data between
components.
Question: How can a shared service be implemented and injected into multiple Angular
components?
Answer:
Angular services are classes that contain reusable logic, and they can be shared across
components using dependency injection (DI). A service is created with the @Injectable
decorator and provided in the root module to make it globally accessible.
For Example:
// data.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class DataService {
getData() {
return ['Angular', 'React', 'Vue'];
}
}
// app.component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-root',
template: `<ul><li *ngFor="let item of data">{{ item }}</li></ul>`,
})
export class AppComponent {
data: string[];
constructor(private dataService: DataService) {
this.data = this.dataService.getData();
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
439
The DataService is injected into the component and used to retrieve data.
Answer:
In Node.js, the multer middleware is commonly used to handle file uploads. It processes
incoming files and stores them on the server.
For Example:
This example accepts file uploads and stores them in the uploads directory.
48. Scenario: You want to ensure form inputs are synchronized between
the template and component in Angular.
Answer:
In Angular, two-way data binding allows synchronization between the component’s state
and the form inputs using the ngModel directive.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
440
Here, changes in the input field reflect in the name variable, and vice versa.
49. Scenario: You need to create a RESTful API endpoint in Express.js that
returns a list of users.
Answer:
Express.js simplifies the creation of RESTful endpoints. A GET endpoint can be defined using
the app.get() method to serve data.
For Example:
50. Scenario: You need to fetch data from an API and display it in a Vue
component.
Question: How can data be fetched asynchronously in Vue using lifecycle hooks?
Answer:
Vue’s mounted() lifecycle hook can be used to fetch data when the component is initialized.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
441
export default {
data() {
return { users: [] };
},
async mounted() {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
this.users = await response.json();
},
template: `<ul><li v-for="user in users">{{ user.name }}</li></ul>`,
};
This example fetches data from an API and displays it in the component.
51. Scenario: You need to build a simple form in React to collect user data.
Question: How can you manage form state using useState in React?
Answer:
Managing form inputs in React involves using the useState hook to store and update the
form data. Each input field can have its own state, or a single state object can track the entire
form. Using controlled components ensures that the form values are in sync with the
component’s state.
For Example:
function UserForm() {
const [formData, setFormData] = useState({ name: '', email: '' });
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
442
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}
This example collects name and email inputs, and logs the form data on submission.
52. Scenario: You need to pass data from a parent component to a child
component in React.
Question: How can props be used to pass data between components in React?
Answer:
In React, props (properties) allow the parent component to pass data to child components.
Props are immutable, meaning the child component cannot modify them directly. This
pattern promotes unidirectional data flow, where data flows from parent to child.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
443
For Example:
function Parent() {
return <Child message="Hello from Parent!" />;
}
Here, the Parent component passes the message prop to the Child component, which
displays it.
Question: How can the v-for directive be used to render lists in Vue.js?
Answer:
In Vue.js, the v-for directive allows developers to render lists by looping through an array of
data. Each item in the array is rendered as a new element.
For Example:
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<script>
export default {
data() {
return {
items: ['Apple', 'Banana', 'Orange'],
};
},
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
444
</script>
Answer:
In Express.js, error-handling middleware catches and processes errors in a centralized way.
The middleware function must include four parameters: (err, req, res, next).
For Example:
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send('Internal Server Error');
});
This example catches errors thrown by routes and returns a 500 status with a message.
Question: How can the v-bind directive be used to bind attributes dynamically in Vue.js?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
445
Answer:
The v-bind directive binds HTML attributes to Vue data. It allows dynamic updating of
attributes based on the component’s state.
For Example:
<script>
export default {
data() {
return {
imageUrl: 'https://via.placeholder.com/150',
};
},
};
</script>
56. Scenario: You need to build a RESTful API that supports JSON parsing in
Express.js.
Answer:
Express.js provides the express.json() middleware to parse incoming JSON payloads. This
middleware ensures that req.body contains the parsed data from the request.
For Example:
app.use(express.json());
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
446
console.log(req.body);
res.send('Data received');
});
This example accepts and logs JSON data sent to the /data endpoint.
57. Scenario: You need to create and register a new component in Vue.js.
Question: How can a component be created and used within another Vue.js component?
Answer:
In Vue.js, components can be registered locally or globally. A locally registered component is
available only within the parent component.
For Example:
// ChildComponent.vue
<template>
<p>This is a child component.</p>
</template>
// ParentComponent.vue
<template>
<ChildComponent />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
};
</script>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
447
The ParentComponent imports and registers the ChildComponent for use in its template.
58. Scenario: You want to control the loading of a Vue.js component based
on a condition.
Question: How can the v-if directive be used to conditionally render elements in Vue.js?
Answer:
The v-if directive allows conditional rendering of elements in Vue. If the condition evaluates
to true, the element is rendered; otherwise, it is not.
For Example:
<script>
export default {
data() {
return { isVisible: true };
},
};
</script>
59. Scenario: You need to create a service that performs API calls in
Angular.
Question: How can an Angular service make HTTP requests using HttpClient?
Answer:
In Angular, the HttpClient service is used to perform HTTP requests. It is injected into
services to encapsulate logic for API calls.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
448
For Example:
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(private http: HttpClient) {}
getData(): Observable<any> {
return this.http.get('https://jsonplaceholder.typicode.com/posts');
}
}
Answer:
In Node.js, async/await simplifies working with promises, making asynchronous code easier
to read and write. It allows you to wait for a promise to resolve before continuing execution.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
449
}
};
fetchData();
This example uses async/await to fetch and log data from an API.
Answer:
React.memo is a higher-order component (HOC) that optimizes functional components by
preventing unnecessary re-renders. If a parent component re-renders, all its child
components are also re-rendered, even if their props have not changed. With React.memo,
the component only re-renders when its props or state change. This saves processing time
and improves performance.
How it works:
React.memo performs a shallow comparison between the previous and new props. If the
values are identical, the component is not re-rendered. For deep comparisons or complex
logic, a custom comparison function can be provided.
For Example:
function Parent() {
const [count, setCount] = React.useState(0);
return (
<div>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
450
Explanation:
Even if the parent component re-renders every time the count changes, the Child
component will not re-render because its name prop remains the same. This reduces
unnecessary re-rendering.
Question: How does the useReducer hook manage complex state logic in React?
Answer:
The useReducer hook is an alternative to useState for managing more complex state
transitions. It follows a similar concept to Redux, using a reducer function to control how the
state changes based on actions.
How it works:
● Reducer function: A function that takes the current state and an action, returning
the updated state.
● Dispatch function: Used to trigger state changes by passing action objects.
This is useful for components with multiple state variables that are tightly related or for when
the next state depends on the previous state.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
451
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
Explanation:
The reducer function determines how the state changes based on the action type. dispatch
triggers the actions, allowing the state to update in a predictable way.
63. Scenario: You need to share state globally across multiple components
in React.
Question: How can the Context API be used to share global state in React?
Answer:
The Context API provides a way to share global state across multiple components without
having to pass props through multiple layers (prop drilling). It allows components to
consume state directly from the context without receiving it as props from parent
components.
How it works:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
452
For Example:
function App() {
return (
<UserContext.Provider value={{ name: 'Shardul' }}>
<User />
</UserContext.Provider>
);
}
function User() {
const user = React.useContext(UserContext);
return <h1>Hello, {user.name}!</h1>;
}
Explanation:
The User component accesses the global state (name) directly from UserContext using
useContext(). This eliminates the need for prop drilling, making the code more
maintainable.
Question: How can Vue’s computed properties be used to optimize API calls?
Answer:
Computed properties in Vue.js are reactive and cached based on their dependencies. If the
dependencies do not change, Vue returns the cached result instead of recomputing the
value. This makes computed properties ideal for reducing unnecessary calculations or API
requests.
For Example:
export default {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
453
data() {
return { query: '', results: [] };
},
computed: {
filteredResults() {
return this.results.filter((item) =>
item.toLowerCase().includes(this.query.toLowerCase())
);
},
},
async mounted() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
this.results = await response.json();
},
};
Explanation:
The filteredResults computed property recalculates only when the query changes. If the
query remains the same, Vue returns the cached value, improving performance.
Answer:
CORS (Cross-Origin Resource Sharing) allows a server to accept requests from a different
domain. By default, browsers block cross-origin requests for security reasons. In Express.js,
the cors middleware enables CORS by setting the appropriate headers.
For Example:
app.use(cors());
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
454
Explanation:
The cors middleware enables cross-origin requests for all routes in the Express application.
Question: How can Angular’s environment files be used to manage sensitive configurations?
Answer:
Angular provides environment files to manage configuration settings based on the build
environment (e.g., development, production). This ensures that sensitive data, like API keys, is
kept out of the source code and is environment-specific.
For Example:
// environment.ts (Development)
export const environment = {
production: false,
apiUrl: 'https://dev.api.com',
};
// environment.prod.ts (Production)
export const environment = {
production: true,
apiUrl: 'https://prod.api.com',
};
// service.ts
import { environment } from '../environments/environment';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
455
Explanation:
The correct environment file is applied based on the build environment, ensuring that
sensitive information is only used in the appropriate context.
67. Scenario: You need to create reusable templates for Vue components.
Answer:
Slots in Vue allow components to accept dynamic content from the parent component.
They enable greater flexibility by letting the parent inject content into specific sections of the
child component’s template.
For Example:
<template>
<div>
<slot name="header"></slot>
<p>This is the default content.</p>
<slot></slot>
</div>
</template>
Explanation:
Slots allow the parent component to dynamically customize the content of the child
component.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
456
Answer:
WebSocket enables bidirectional communication between the server and clients. This
makes it ideal for real-time applications like chat apps, where both parties need to send and
receive data without repeatedly polling the server.
For Example:
Explanation:
The server sends a welcome message when a client connects, and logs any messages
received from the client.
Question: How can you manage multiple promises using Promise.all() in Node.js?
Answer:
Promise.all() runs multiple promises in parallel and waits for all of them to resolve. If any
promise fails, the entire operation is rejected. This is useful for batch processing.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
457
console.log('Posts:', posts);
console.log('Users:', users);
}
fetchData();
Explanation:
This example fetches posts and users in parallel, reducing wait time.
70. Scenario: You want to create lazy-loaded modules in Angular for better
performance.
Answer:
Lazy loading in Angular defers the loading of modules until they are required, improving the
initial load time. This is configured through the loadChildren property in routes.
For Example:
Explanation:
The AdminModule is only loaded when the user navigates to the /admin route, optimizing
performance.
71. Scenario: You need to persist React state across browser reloads.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
458
Question: How can you use localStorage to persist state in a React application?
Answer:
In React, localStorage provides a way to store data in the browser's storage, which persists
even after the user reloads or closes the tab. This is useful for user preferences, shopping
carts, or form inputs that need to be retained between sessions. Using useState and
useEffect, you can initialize the state from localStorage and update the storage whenever
the state changes.
Detailed Steps:
For Example:
function Counter() {
const [count, setCount] = useState(() => {
const savedCount = localStorage.getItem('count');
return savedCount ? JSON.parse(savedCount) : 0;
});
useEffect(() => {
localStorage.setItem('count', JSON.stringify(count));
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
459
Explanation:
● State Initialization: When the component mounts, the useState hook tries to retrieve
the count from localStorage.
● Data Synchronization: useEffect ensures that every time the count changes, the
value is saved in localStorage.
This makes the state persistent, meaning the user will see the same counter value
even after refreshing the page.
72. Scenario: You need to debounce an input field to avoid excessive API
calls in React.
Question: How can you implement a debounced input field using setTimeout in React?
Answer:
Debouncing ensures that a function (such as an API call) only executes after a specified
period of inactivity. It’s useful in search inputs where users may type quickly, and calling the
API on every keystroke would be inefficient. By using setTimeout and clearTimeout, we can
delay the execution and cancel previous calls if the user continues typing.
Detailed Steps:
1. Use useState to track the input value and the debounced query separately.
2. In useEffect, set a timer that updates the debounced query after a delay (500ms).
3. Clear the timer when the component unmounts or when the input changes before
the timer completes.
For Example:
function DebouncedInput() {
const [query, setQuery] = useState('');
const [debouncedQuery, setDebouncedQuery] = useState(query);
useEffect(() => {
const handler = setTimeout(() => setDebouncedQuery(query), 500);
return () => clearTimeout(handler);
}, [query]);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
460
useEffect(() => {
if (debouncedQuery) {
console.log('API Call with:', debouncedQuery);
}
}, [debouncedQuery]);
return (
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}
Explanation:
● Delaying API calls: The API is called only if the user stops typing for 500ms.
● Preventing redundant requests: Previous calls are cancelled using clearTimeout if
the user continues typing before the delay expires.
This approach improves performance and reduces server load.
73. Scenario: You need to manage deeply nested state in a Vuex store.
Question: How can Vuex modules help organize state in large Vue.js applications?
Answer:
As applications grow, managing large and deeply nested states becomes challenging. Vuex
modules allow developers to split the state into logical pieces based on the domain (e.g.,
user, products). Each module maintains its own state, mutations, actions, and getters,
promoting better organization.
Detailed Steps:
1. Create a Vuex module that encapsulates state logic for a specific domain (e.g., user).
2. Register the module inside the main Vuex store.
3. Access the module's state in components using map helpers or $store.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
461
For Example:
// store/user.js
export const user = {
state: () => ({
name: '',
email: ''
}),
mutations: {
setUser(state, user) {
state.name = user.name;
state.email = user.email;
}
}
};
// store/index.js
import { createStore } from 'vuex';
import { user } from './user';
Explanation:
The user module is isolated, making it easier to manage and maintain. Each module can
have its own logic, helping with modularization in larger Vue applications.
74. Scenario: You need to authenticate users in an Express.js API with JWT.
Answer:
JWT (JSON Web Token) is a popular way to implement stateless authentication. Upon login,
the server creates a JWT token that contains encoded user information. This token is sent to
the client and included in subsequent requests for authorization.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
462
Detailed Steps:
For Example:
Explanation:
The /login route creates a JWT token on successful login. The /protected route verifies the
token, ensuring the request is authorized.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
463
Answer:
In Vue.js, lazy loading defers the loading of components until they are needed. This
technique improves the initial load time by splitting the code into smaller chunks, loaded on
demand.
For Example:
export default {
components: {
LazyComponent
}
};
Explanation:
This example loads the LazyComponent only when it is required, enhancing performance by
reducing the initial bundle size.
Answer:
Role-based access control (RBAC) restricts access to certain routes or components based on
the user's role. Angular provides route guards, such as CanActivate, to enforce this control
by checking if the user has the correct role before allowing access.
Detailed Steps:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
464
@Injectable({
providedIn: 'root',
})
export class RoleGuard implements CanActivate {
canActivate(): boolean {
const userRole = 'admin'; // Assume this is fetched from user data
return userRole === 'admin'; // Allow only admins to access the route
}
}
Router Configuration:
Explanation:
This guard ensures that only users with the admin role can access the /admin route. If the
user lacks the correct role, the route is blocked.
77. Scenario: You need to prevent memory leaks in a React component that
uses timers.
Answer:
When a component uses timers or subscriptions, React’s useEffect cleanup function
ensures these resources are released when the component unmounts. This prevents
memory leaks, which could degrade performance.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
465
function TimerComponent() {
useEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000);
Explanation:
The cleanup function returned by useEffect clears the timer when the component
unmounts, ensuring no unnecessary processes are left running.
Question: How can the process module help monitor performance in Node.js?
Answer:
Node.js provides the process module to monitor system metrics like memory usage and
uptime. This is useful for detecting memory leaks or analyzing the server's performance over
time.
For Example:
Explanation:
● process.memoryUsage() returns an object with details about heap and RSS memory.
● process.uptime() shows how long the Node.js process has been running.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
466
These metrics help debug performance issues and ensure the application is optimized.
79. Scenario: You need to handle file uploads in Angular with progress
tracking.
Question: How can HttpClient be used to upload files with progress tracking?
Answer:
In Angular, the HttpClient service allows you to monitor file upload progress using the
reportProgress option. This helps provide user feedback during uploads.
For Example:
uploadFile(file: File) {
const formData = new FormData();
formData.append('file', file);
this.http.post('/upload', formData, {
reportProgress: true,
observe: 'events',
}).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentDone = Math.round((100 * event.loaded) / (event.total || 1));
console.log(`File is ${percentDone}% uploaded.`);
}
});
}
Explanation:
This example logs the upload progress to the console. Users get real-time feedback about
the upload status.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
467
Answer:
Server-side rendering (SSR) improves performance and SEO by rendering the page on the
server and sending fully-formed HTML to the browser. Nuxt.js simplifies SSR for Vue.js
applications.
<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from SSR!'
};
}
};
</script>
Explanation:
When a user requests this page, the server renders the HTML and sends it to the browser,
resulting in faster load times and improved SEO compared to client-side rendering.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
468
THEORETICAL QUESTIONS
1. What is Unit Testing in JavaScript?
Answer:
Unit testing focuses on verifying that small units of code—such as individual functions,
methods, or components—work as expected. The idea is to test each part of the code in
isolation, ensuring that every function behaves correctly for both typical and edge cases.
These tests are quick to run and help detect bugs early. Unit tests are also important for
regression testing, making sure that code changes don't accidentally introduce new bugs.
For Example:
A function like sum(a, b) is tested with various inputs to verify it behaves consistently:
function sum(a, b) {
return a + b;
}
The above Jest test ensures that the function returns 3 when adding 1 and 2. If the function
changes in the future, this test will catch any incorrect behavior.
Answer:
Jest is a popular testing framework for JavaScript, primarily used for unit testing. It provides
features like assertions, mocks, and test coverage out of the box. One reason Jest is widely
used is its simplicity and ease of integration, especially with React applications.
Jest is well-suited for both synchronous and asynchronous testing. It also offers snapshot
testing, which ensures that UI components don’t change unexpectedly over time. Jest can
execute tests in parallel, making it faster for large projects.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
469
For Example:
This test demonstrates how Jest handles promises. The fetchData function is asynchronous,
and the test ensures it resolves to the correct value.
Answer:
Mocha is another JavaScript testing framework, but unlike Jest, it is more flexible and
lightweight. Mocha only provides the test runner and doesn't include assertions or mocking
libraries by default. Developers often use it with Chai (for assertions) and Sinon (for mocks
and spies).
The main difference is that Jest is a complete testing suite with built-in features, while
Mocha offers more flexibility, making it useful for more complex testing scenarios where
customization is required.
For Example:
function add(a, b) {
return a + b;
}
describe('Addition', () => {
it('should return 5 when adding 2 and 3', () => {
expect(add(2, 3)).to.equal(5);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
470
});
});
This Mocha test uses Chai to assert that the add function returns the correct value.
Answer:
Integration testing focuses on testing how different modules or components of an
application work together. It ensures that the interaction between these parts is functioning
correctly. For example, you might test the connection between your frontend and backend
or how your API interacts with the database.
For Example:
function getUser() {
return { id: 1, name: 'John Doe' };
}
Here, we ensure that the service returns the expected user object. Although it’s simple,
integration tests for complex applications involve checking the behavior across multiple
layers, such as controllers interacting with services.
Answer:
End-to-End (E2E) testing validates the entire flow of an application, simulating real-world
user interactions. E2E tests are crucial for mission-critical user journeys like login, payment
processing, or checkout flows. They help ensure that every component of the system—from
the user interface to the backend services—works correctly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
471
For Example:
This Cypress test automates the login flow and verifies that the user is redirected to the
dashboard after successful login.
Answer:
Chrome DevTools can be used to step through code and identify issues during tests. If you're
running tests with Jest, you can use the --inspect-brk flag to open a debugging session.
Chrome DevTools allows you to set breakpoints, inspect variables, and trace function calls,
making it easier to pinpoint errors.
For Example:
To debug a Jest test, run:
Then open Chrome DevTools by navigating to chrome://inspect. Attach to the running test
process and step through the code to debug.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
472
Answer:
Mocks are objects or functions that simulate the behavior of real dependencies, such as APIs
or databases, during testing. Mocks help isolate the code under test by providing controlled
responses. This ensures that tests are not affected by external factors, like network delays.
For Example:
Using Jest to mock a function:
Here, fetchData is mocked to return 'mock data' instead of making an actual network call,
ensuring that the test runs quickly and reliably.
Answer:
Assertions verify that the output of code matches the expected value. In JavaScript testing,
assertion libraries like Jest and Chai provide methods such as toBe, toEqual, and
toHaveLength to validate test results. If an assertion fails, the test will fail, highlighting the
error.
For Example:
Here, the assertion ensures that the expression evaluates to 4. If it doesn’t, the test will fail,
alerting the developer to an issue.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
473
Answer:
Cypress is a popular E2E testing tool that provides a complete framework with built-in
assertions and a GUI for running tests. Setting up Cypress involves installing it via npm and
running it through the Cypress test runner.
For Example:
This test checks if the homepage loads and displays the text "Welcome".
10. What are the key differences between Unit, Integration, and E2E
Testing?
Answer:
These three types of testing serve different purposes in software development:
Each type plays a role in ensuring software quality. Unit tests are fast and precise,
integration tests catch issues in component interaction, and E2E tests ensure the overall
user experience works smoothly.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
474
Answer:
Test coverage measures how much of your code is executed when running tests, ensuring
critical logic paths are not missed. It gives a percentage-based view of which statements,
branches, functions, and lines were covered during testing. While high coverage is not a
guarantee of a bug-free code, it reduces the chances of defects going unnoticed, especially
in complex systems.
Tools like Istanbul and Jest generate coverage reports, highlighting which parts of the code
were not executed. This ensures developers focus on writing more tests for uncovered logic.
However, test coverage should be used as a guideline, not a strict metric—100% coverage
doesn't always mean the code is fully tested, as tests can be poorly designed.
For Example:
Running coverage reports in Jest:
jest --coverage
The output will display metrics like statement coverage (85%) and branch coverage (75%),
allowing you to identify weak spots in testing.
Answer:
Testing asynchronous code can be tricky since the test runner needs to wait for promises to
resolve or callbacks to complete. Jest makes this easy by supporting async/await or
returning promises directly in test functions. If the asynchronous operation fails, the test will
automatically catch the failure. You can also use the done() callback to signal when an
asynchronous operation completes, but using async/await is preferred for readability.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
475
For Example:
Testing asynchronous code with async/await:
This test ensures that the fetchData function returns the correct value asynchronously.
13. What are test doubles, and how are they used in JavaScript?
Answer:
Test doubles are substitutes for real objects, allowing you to isolate the code under test from
its dependencies. They prevent tests from being affected by external systems such as
databases or APIs, ensuring repeatability and control. There are several types of test doubles:
For Example:
Using a Jest spy:
myFunction('test');
expect(myFunction).toHaveBeenCalledWith('test');
Here, the spy confirms that myFunction was called with 'test'.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
476
Answer:
Testing for exceptions ensures that your code handles errors gracefully. In Jest, the toThrow
assertion verifies that a function raises a specific error when given invalid input. This type of
testing is crucial for robust error handling and ensuring that exceptions don't lead to system
crashes or unpredictable behavior.
For Example:
Testing a division function that throws an error:
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
This test ensures that dividing by zero raises the appropriate error.
Answer:
A test runner automates the execution of test cases, providing results in a structured format.
Test runners streamline the process of running multiple tests and help monitor their
progress. They also integrate with CI/CD pipelines to run tests automatically after each code
commit, ensuring the application remains stable throughout development.
For Example:
Running tests with Jest:
npx jest
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
477
This command finds all test files (usually named *.test.js) and executes them, reporting
the results in the console.
Answer:
Cypress provides a framework for E2E testing by interacting with web pages in real browsers.
It simulates user actions (like clicks and form submissions) and verifies the application’s
behavior. Cypress also offers a visual GUI to run tests interactively, which is useful for
debugging.
For Example:
A simple Cypress test:
This test ensures that the homepage contains the word "Welcome".
Answer:
Mocking HTTP requests is essential for testing components that rely on external APIs. It
ensures tests run independently of network conditions. By mocking fetch calls with Jest, you
can simulate server responses and verify how your code handles them.
For Example:
Mocking an API request:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
478
This test verifies that the component correctly handles the mocked API response.
18. How can you use setup and teardown methods in tests?
Answer:
Setup and teardown methods, such as beforeEach and afterEach, ensure that the testing
environment is consistent across tests. These methods initialize resources before each test
runs and clean up afterward, preventing test pollution (when one test affects another).
For Example:
Using setup and teardown with Jest:
let value;
beforeEach(() => {
value = 0; // Setup
});
afterEach(() => {
value = null; // Teardown
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
479
This ensures that each test starts with a fresh value and avoids side effects from other tests.
Answer:
Snapshot testing ensures that the UI or function output doesn’t change unexpectedly. It
captures the output of a component and saves it as a snapshot. When the test is run again,
the output is compared with the saved snapshot. If there are changes, the test fails,
prompting the developer to review and update the snapshot if necessary.
For Example:
Creating a snapshot with Jest:
function getMessage() {
return 'Hello, world!';
}
The first time this test runs, a snapshot is saved. Future runs compare the current output to
this snapshot.
Answer:
Test-Driven Development (TDD) is a software development practice where developers write
tests before writing the actual code. The process involves three steps:
1. Write a failing test: Start by writing a test that specifies the desired functionality.
2. Write code to pass the test: Implement the simplest code to make the test pass.
3. Refactor: Improve the code while ensuring all tests remain green.
This practice ensures better code quality and encourages developers to think about edge
cases and requirements upfront.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
480
For Example:
function add(a, b) {
return a + b;
}
TDD helps ensure that every piece of functionality is covered by tests, reducing the chances
of bugs.
21. How do you handle API response delays and timeouts in JavaScript
tests?
Answer:
In real-world scenarios, APIs might respond slowly, and testing such cases ensures the
application can gracefully handle delays or timeouts. To test for these scenarios, you can
mock delayed responses or timeouts in your tests. In Jest, you can use
jest.useFakeTimers() to simulate delayed operations without slowing down your tests.
For Example:
Simulating a delayed API response:
function fetchData() {
return new Promise((resolve) =>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
481
This test simulates a delayed response and ensures the correct behavior is verified without
actually waiting for 3 seconds.
22. How do you ensure test isolation when dealing with shared states in
JavaScript?
Answer:
Test isolation ensures that the outcome of one test does not affect other tests. When tests
share state (e.g., global variables or modules), it can lead to flaky tests (tests that fail
intermittently). To ensure isolation, each test should either reset state after running or use
mock objects that are unique to the test.
For Example:
Using beforeEach to reset a shared state:
let counter;
beforeEach(() => {
counter = 0; // Reset state before each test
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
482
This approach ensures each test starts with a fresh counter value.
Answer:
Mocking modules allows you to replace an entire module with a mock version during
testing. This is useful when you want to isolate the code under test from external
dependencies, such as database modules or third-party libraries. Jest provides a simple way
to mock modules with jest.mock().
For Example:
Mocking a module:
// userService.js
module.exports = () => 'Real User';
// userService.test.js
jest.mock('./userService', () => () => 'Mocked User');
Here, the userService module is mocked to return a custom value during the test.
24. How can you test React components using Jest and React Testing
Library?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
483
Answer:
Testing React components involves verifying that they render correctly and behave as
expected under different conditions. The React Testing Library works with Jest to render
components in a lightweight environment and provide utilities to query the DOM.
For Example:
Testing a React component:
This test ensures the component renders the correct greeting message based on the name
prop.
Answer:
Parameterized tests allow you to run the same test logic with multiple inputs and expected
outputs. This reduces code duplication and ensures comprehensive testing across different
scenarios. Jest supports parameterized tests through the test.each method.
For Example:
Using parameterized tests:
test.each([
[1, 2, 3],
[2, 3, 5],
[3, 5, 8],
])('adds %i and %i to equal %i', (a, b, expected) => {
expect(a + b).toBe(expected);
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
484
This test runs three different scenarios using the same test logic.
Answer:
Middleware in Express is used to handle requests and responses before they reach the route
handlers. When testing middleware, it is essential to verify that it behaves correctly for
various request scenarios.
For Example:
Testing a middleware function:
This test ensures that the middleware correctly modifies the req object and calls the next
function.
27. How do you use Jest timers to test functions with delays?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
485
Answer:
Jest provides fake timers to control and manipulate time-based operations. This feature is
useful for testing functions with setTimeout, setInterval, or any delayed logic without
slowing down your tests.
For Example:
Using Jest fake timers:
function delayedFunction(callback) {
setTimeout(() => callback('done'), 1000);
}
delayedFunction(callback);
jest.runAllTimers();
expect(callback).toHaveBeenCalledWith('done');
});
This test ensures the callback is invoked correctly after the simulated delay.
28. How do you use spies to test function calls and arguments in
JavaScript?
Answer:
Spies allow you to monitor how a function is called during a test, including the number of
calls and the arguments used. Jest spies can wrap existing functions or be created as
standalone mocks.
For Example:
Using a spy in Jest:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
486
This test ensures the function was called with the correct argument and returned the
expected value.
Answer:
Global setup and teardown functions run before and after all tests in a test suite. These
functions are used to initialize resources (like databases) or clean up after tests complete.
Jest provides hooks like beforeAll and afterAll for global setup and teardown.
For Example:
Using global setup and teardown:
beforeAll(() => {
console.log('Setting up resources');
});
afterAll(() => {
console.log('Cleaning up resources');
});
This ensures that setup and cleanup happen exactly once for all tests.
30. How do you generate code coverage reports and interpret them?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
487
Answer:
Generating code coverage reports helps you understand which parts of the codebase are
being tested. Jest includes built-in tools to generate such reports. These reports display the
percentage of code covered across statements, branches, and functions.
For Example:
Generating a coverage report:
jest --coverage
This report highlights areas of code that need additional tests, helping developers prioritize
testing efforts.
31. How do you test a Redux store and actions in JavaScript applications?
Answer:
Testing a Redux store involves verifying that actions trigger the correct state changes.
Actions represent events or user interactions, while the store holds the application’s state. For
Redux, it’s essential to ensure:
For Example:
Testing Redux actions and reducers with Jest:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
488
// actions.js
const increment = () => ({ type: 'INCREMENT' });
// reducer.js
const reducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
};
// actions.test.js
test('increment action returns correct payload', () => {
expect(increment()).toEqual({ type: 'INCREMENT' });
});
// reducer.test.js
test('reducer increments state', () => {
expect(reducer(0, { type: 'INCREMENT' })).toBe(1);
});
This test ensures that actions return the correct type and the reducer updates the state
properly.
Answer:
When testing code that makes HTTP requests using Axios or the native fetch() API, it’s
important to mock these calls to avoid actual network requests. Jest provides utilities to
mock Axios or fetch calls, ensuring tests are fast and isolated.
For Example:
Mocking an Axios request:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
489
This test ensures that the mocked Axios request returns the expected data.
Answer:
WebSocket connections allow real-time communication between the server and client. To
test WebSocket behavior, you can mock WebSocket instances and simulate message
events. The tests ensure the client responds correctly to messages or connection changes.
For Example:
Mocking WebSocket:
mockWebSocket.onmessage = onMessage;
mockWebSocket.onmessage({ data: 'Hello' });
This test verifies that the WebSocket message handler processes messages correctly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
490
Answer:
Testing custom React hooks ensures they behave correctly when state or props change.
React Testing Library provides utilities like renderHook() to test hooks independently.
For Example:
Testing a custom hook:
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount((prev) => prev + 1);
return { count, increment };
}
This test ensures that the hook increments the counter as expected.
Answer:
Race conditions occur when multiple asynchronous operations complete at unpredictable
times, potentially leading to incorrect behavior. Tests should ensure that code handles race
conditions gracefully by properly waiting for promises or using Promise.all().
For Example:
Testing with Promise.all():
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
491
function fetchData(url) {
return new Promise((resolve) => setTimeout(() => resolve(url), 100));
}
This ensures the promises resolve as expected, avoiding race condition issues.
Answer:
Performance testing ensures that code runs efficiently under different conditions. You can
use Node.js performance hooks or browser APIs like performance.now() to measure
execution times.
For Example:
Measuring performance in a function:
// Function to be tested
for (let i = 0; i < 1000000; i++) {}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
492
Answer:
Memory leaks occur when unused memory is not released, causing the application to
consume more memory over time. In Node.js, you can use tools like heap snapshots and
node-memwatch to monitor memory usage during tests.
For Example:
Using global.gc() to force garbage collection:
expect(usedAfter).toBeGreaterThan(usedBefore);
});
Answer:
Cross-browser testing ensures the application behaves consistently across different browsers.
Tools like Selenium, Cypress, or Playwright automate browser interactions for testing
compatibility.
For Example:
Using Playwright to test cross-browser behavior:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
493
This test verifies that the page loads correctly in multiple browsers.
Answer:
Testing GraphQL APIs ensures the queries and mutations behave as expected. You can use
tools like Apollo Client with Jest to simulate GraphQL operations.
For Example:
Testing a GraphQL query:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
494
This test ensures the GraphQL query returns the expected data.
Answer:
Accessibility testing ensures your application is usable by people with disabilities. Tools like
axe-core can be used with Jest to test for accessibility violations.
For Example:
Using axe-core for accessibility testing:
expect.extend(toHaveNoViolations);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
495
SCENARIO QUESTIONS
41. Scenario: Testing a function that adds two numbers using Jest
Question: How would you write a Jest unit test for a simple addition function?
Answer:
Unit tests focus on small, isolated pieces of code, such as individual functions. To test an
addition function with Jest, you need to ensure the function returns the expected sum for
various inputs.
For Example:
// add.js
function add(a, b) {
return a + b;
}
module.exports = add;
// add.test.js
const add = require('./add');
In this example, two test cases ensure that the add function behaves correctly for both
positive and negative numbers.
Question: How do you test an asynchronous function that fetches data using Jest?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
496
Answer:
Testing asynchronous functions ensures that promises resolve or reject as expected. With
Jest, you can use async/await to wait for the function’s result and verify its correctness.
For Example:
// fetchData.js
async function fetchData() {
return 'data loaded';
}
module.exports = fetchData;
// fetchData.test.js
const fetchData = require('./fetchData');
This test waits for fetchData() to resolve and ensures it returns the expected string.
Question: How would you mock a dependency using Jest when testing a function?
Answer:
Sometimes functions depend on external modules, such as APIs. You can mock those
dependencies to isolate your tests and control their behavior.
For Example:
// getUser.js
const fetch = require('node-fetch');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
497
module.exports = getUser;
// getUser.test.js
const getUser = require('./getUser');
jest.mock('node-fetch', () => jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ name: 'Shardul' }),
})
));
Question: How do you write an integration test for two functions working together?
Answer:
Integration tests ensure that multiple functions or modules work as expected when used
together.
For Example:
// mathOperations.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
498
return a * b;
}
// mathOperations.test.js
const { add, multiply } = require('./mathOperations');
This integration test ensures that both the add and multiply functions interact correctly.
Question: How would you test a login flow on a web application using Cypress?
Answer:
End-to-end tests verify that complete user flows work as intended. In this case, Cypress
simulates the login process and ensures the user lands on the correct page.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
499
This Cypress test automates the login process and ensures the user is redirected to the
dashboard.
Question: How do you use Chrome DevTools to debug failed Jest tests?
Answer:
You can run Jest with the --inspect-brk flag and attach Chrome DevTools to debug failed
tests step by step.
For Example:
1.
2. Open Chrome DevTools by navigating to chrome://inspect.
3. Click on "Inspect" next to the Node.js process.
4. Set breakpoints and step through the test code to identify issues.
Question: How do you test a function to ensure it handles API failures correctly?
Answer:
It’s essential to verify that your code handles API errors gracefully. You can mock an API call
to reject with an error and ensure the function responds correctly.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
500
try {
const response = await fetch('https://api.example.com/data');
return response.json();
} catch (error) {
return 'API Error';
}
};
This test ensures the function returns "API Error" when the fetch request fails.
Answer:
Middleware functions process requests before passing them to route handlers. Testing
middleware ensures it behaves correctly when modifying the req or res objects.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
501
This test verifies that the middleware adds a user object to the request.
Answer:
Button click handlers trigger specific actions. It’s important to test these handlers to ensure
they behave correctly when invoked.
For Example:
function handleClick(callback) {
callback('Button clicked');
}
This test ensures that the click handler triggers the callback with the correct message.
Answer:
Simulating network delays in Cypress helps test how the UI behaves under slow network
conditions. You can use the cy.intercept() method to control response timings.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
502
For Example:
cy.visit('https://example.com');
cy.get('#loadingSpinner').should('be.visible');
cy.wait('@getData');
cy.get('#loadingSpinner').should('not.exist');
});
});
This test verifies that a loading spinner is displayed during a simulated network delay.
Question: How do you write a unit test using Mocha and Chai for a function that checks if a
number is even?
Answer:
Mocha is a JavaScript test runner that allows you to structure tests, while Chai provides
assertion methods to verify outcomes. In this scenario, we’ll test a function that checks if a
number is even. Mocha will run the test, and Chai will assert that the function behaves
correctly.
For Example:
// even.js
function isEven(num) {
return num % 2 === 0;
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
503
module.exports = isEven;
// even.test.js
const { expect } = require('chai');
const isEven = require('./even');
describe('isEven', () => {
it('should return true for even numbers', () => {
expect(isEven(4)).to.be.true;
});
This test ensures that the isEven function returns true for even numbers and false for odd
ones.
Question: How do you test that a function throws an error using Mocha and Chai?
Answer:
Testing error handling is essential to ensure that your code behaves correctly under failure
scenarios. Mocha and Chai allow you to assert that a function throws an expected error
when provided with invalid input.
For Example:
// divide.js
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
module.exports = divide;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
504
// divide.test.js
const { expect } = require('chai');
const divide = require('./divide');
describe('divide', () => {
it('should throw an error when dividing by zero', () => {
expect(() => divide(4, 0)).to.throw('Division by zero');
});
This test verifies that dividing by zero throws the correct error and valid inputs return the
expected result.
Question: How do you test an Express API endpoint using Mocha and Supertest?
Answer:
Supertest allows you to test Express endpoints by making simulated HTTP requests. You can
use it with Mocha to ensure that your API behaves as expected under different scenarios.
For Example:
// server.js
const express = require('express');
const app = express();
module.exports = app;
// server.test.js
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
505
This test ensures that the /status endpoint returns the correct response and status code.
Question: How do you test a React component that increments a counter using React
Testing Library?
Answer:
Testing React components ensures they respond correctly to user interactions. In this
example, we’ll test a simple counter component that increments when a button is clicked.
For Example:
// Counter.js
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
506
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
This test ensures that clicking the button increments the counter.
Question: How do you test a function that uses setTimeout with Jest fake timers?
Answer:
Jest’s fake timers allow you to simulate the passage of time in tests, making it easier to test
functions that rely on timeouts or intervals.
For Example:
// delayedMessage.js
function delayedMessage(callback) {
setTimeout(() => {
callback('Hello after 1 second');
}, 1000);
}
module.exports = delayedMessage;
// delayedMessage.test.js
const delayedMessage = require('./delayedMessage');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
507
delayedMessage(callback);
jest.runAllTimers();
This test ensures that the callback is called with the correct message after the simulated
delay.
Question: How do you test input validation logic in a React form component?
Answer:
Testing input validation ensures that the form behaves correctly when users enter valid or
invalid data.
For Example:
// LoginForm.js
import React, { useState } from 'react';
function LoginForm() {
const [error, setError] = useState('');
return (
<form onSubmit={handleSubmit}>
<input name="username" />
<button type="submit">Submit</button>
{error && <p>{error}</p>}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
508
</form>
);
}
// LoginForm.test.js
import { render, fireEvent, screen } from '@testing-library/react';
import LoginForm from './LoginForm';
This test ensures the form displays an error if the username input is left empty.
Answer:
Testing retry logic ensures that network requests are retried when the initial attempt fails.
For Example:
// fetchWithRetry.js
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (response.ok) return response.json();
} catch (error) {
if (i === retries - 1) throw error;
}
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
509
module.exports = fetchWithRetry;
// fetchWithRetry.test.js
global.fetch = jest.fn(() =>
Promise.reject(new Error('Network error'))
);
await expect(fetchWithRetry('https://api.example.com')).rejects.toThrow('Network
error');
expect(fetch).toHaveBeenCalledTimes(3);
});
Question: How do you test a custom hook that manages state in React?
Answer:
Testing hooks ensures that state management logic behaves as expected.
For Example:
// useCounter.js
import { useState } from 'react';
// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
510
This test verifies the increment function updates the state correctly.
Question: How do you test a function with edge cases using Jest?
Answer:
Testing edge cases ensures your code behaves correctly for unusual inputs.
For Example:
function reverseString(str) {
if (!str) return '';
return str.split('').reverse().join('');
}
This test ensures the function handles both normal and edge cases correctly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
511
Question: How do you test that CSS classes change based on component state in React?
Answer:
Testing CSS class changes ensures that UI elements respond to state changes as expected.
For Example:
// ToggleButton.js
import React, { useState } from 'react';
function ToggleButton() {
const [active, setActive] = useState(false);
return (
<button className={active ? 'active' : ''} onClick={() => setActive(!active)}>
Toggle
</button>
);
}
// ToggleButton.test.js
import { render, fireEvent, screen } from '@testing-library/react';
import ToggleButton from './ToggleButton';
fireEvent.click(button);
expect(button).toHaveClass('active');
fireEvent.click(button);
expect(button).not.toHaveClass('active');
});
This test ensures the button's CSS class toggles based on the active state.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
512
Question: How do you mock multiple dependencies in a JavaScript function using Jest?
Answer:
When a function depends on multiple modules, mocking them allows you to isolate the
function under test. This ensures your test is not affected by changes or failures in external
modules.
For Example:
// userService.js
const getUser = require('./getUser');
const sendEmail = require('./sendEmail');
module.exports = notifyUser;
// userService.test.js
jest.mock('./getUser', () => jest.fn(() => ({ email: '[email protected]' })));
jest.mock('./sendEmail', () => jest.fn());
This test ensures the function interacts correctly with both dependencies.
Question: How do you test Redux async actions using Thunk middleware?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
513
Answer:
Testing async actions ensures they dispatch the correct actions in the expected order. Redux
Thunk allows actions to return functions, making it possible to handle asynchronous logic.
For Example:
// actions.js
export const fetchData = () => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const data = await fetch('/api/data').then((res) => res.json());
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch {
dispatch({ type: 'FETCH_ERROR' });
}
};
// actions.test.js
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { fetchData } from './actions';
expect(store.getActions()).toEqual([
{ type: 'FETCH_START' },
{ type: 'FETCH_SUCCESS', payload: { data: 'Test data' } },
]);
});
This test ensures that the correct actions are dispatched during the async flow.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
514
63. Scenario: Simulating user events with Cypress across multiple pages
Question: How do you test a multi-page flow in a web application using Cypress?
Answer:
E2E tests using Cypress ensure the entire user journey works as expected across multiple
pages. This includes checking for navigation, input handling, and state persistence between
pages.
For Example:
This test verifies that the registration flow works across multiple pages.
Answer:
Testing file uploads ensures that the system can correctly handle and process user-uploaded
files.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
515
Question: How do you test that only authenticated users can access protected routes?
Answer:
Testing protected routes ensures that only authenticated users can access certain endpoints.
For Example:
// authMiddleware.js
function authMiddleware(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
res.status(401).send('Unauthorized');
}
}
module.exports = authMiddleware;
// authMiddleware.test.js
const request = require('supertest');
const express = require('express');
const authMiddleware = require('./authMiddleware');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
516
This test ensures that unauthorized users cannot access protected routes.
Answer:
Testing WebSocket reconnections ensures that the system can reconnect automatically
when the connection is lost.
For Example:
socket.onclose = () => {
setTimeout(() => connectWebSocket(url, onMessage), 1000);
};
socket.onmessage = onMessage;
return socket;
}
socket.onclose();
jest.runAllTimers();
expect(onMessage).not.toHaveBeenCalled(); // Verify no message received on
reconnect
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
517
67. Scenario: Testing animations with Jest and React Testing Library
Question: How do you test CSS animations triggered by state changes in React?
Answer:
Testing animations ensures that state changes trigger the correct CSS classes.
For Example:
// FadeComponent.js
import React, { useState } from 'react';
function FadeComponent() {
const [visible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(!visible)}>Toggle</button>
<div className={visible ? 'fade-in' : 'fade-out'}>Content</div>
</div>
);
}
// FadeComponent.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import FadeComponent from './FadeComponent';
fireEvent.click(button);
expect(content).toHaveClass('fade-in');
fireEvent.click(button);
expect(content).toHaveClass('fade-out');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
518
});
This test ensures the correct animation class is applied based on state.
Question: How do you test state changes in components that use the React Context API?
Answer:
Testing components that consume React Context ensures they respond correctly to state
changes from the context.
For Example:
// ThemeContext.js
import React, { createContext, useContext, useState } from 'react';
// ThemeToggle.js
import React from 'react';
import { useTheme } from './ThemeContext';
function ThemeToggle() {
const { theme, setTheme } = useTheme();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
519
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
);
}
// ThemeToggle.test.js
import { render, fireEvent } from '@testing-library/react';
import { ThemeProvider } from './ThemeContext';
import ThemeToggle from './ThemeToggle';
This test verifies that clicking the button toggles the theme correctly.
Question: How do you test a custom event listener for DOM elements?
Answer:
Testing custom event listeners ensures correct interaction handling with DOM elements.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
520
element.addEventListener('customEvent', callback);
}
// Test
test('calls callback on custom event', () => {
const element = document.createElement('div');
const callback = jest.fn();
addCustomListener(element, callback);
const event = new Event('customEvent');
element.dispatchEvent(event);
expect(callback).toHaveBeenCalled();
});
This test ensures the custom event triggers the callback correctly.
70. Scenario: Testing complex form submission logic with async validation
Question: How do you test an async form validation and submission logic?
Answer:
Testing forms with async validation ensures users cannot submit invalid data.
For Example:
// validate.js
export async function validateUsername(username) {
return username === 'validUser' ? 'Valid' : 'Invalid';
}
// validate.test.js
test('validates username asynchronously', async () => {
const result = await validateUsername('validUser');
expect(result).toBe('Valid');
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
521
This test ensures the form behaves correctly based on async validation.
Question: How do you test a component that conditionally renders content based on a
feature flag?
Answer:
Testing feature flags ensures that components behave correctly under different conditions
based on configuration values. This is particularly important in A/B testing scenarios.
For Example:
// FeatureComponent.js
import React from 'react';
// FeatureComponent.test.js
import { render, screen } from '@testing-library/react';
import FeatureComponent from './FeatureComponent';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
522
This test checks the correct rendering of the component based on the feature flag.
Question: How do you test a function that implements caching to avoid redundant API calls?
Answer:
Testing caching ensures that the function retrieves data from the cache instead of making
unnecessary API calls. This can improve performance and reduce network load.
For Example:
// cache.js
const cache = new Map();
module.exports = fetchWithCache;
// cache.test.js
global.fetch = jest.fn(() =>
Promise.resolve({ json: () => Promise.resolve({ data: 'Test data' }) })
);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
523
Question: How do you test a custom hook that fetches data from an API using React Testing
Library?
Answer:
Testing custom hooks that perform API calls requires mocking the fetch requests and
verifying the behavior based on different loading and error states.
For Example:
// useFetch.js
import { useState, useEffect } from 'react';
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
524
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
// useFetch.test.js
import { renderHook } from '@testing-library/react-hooks';
import { useFetch } from './useFetch';
global.fetch = jest.fn();
expect(result.current.loading).toBe(true);
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
expect(result.current.data).toEqual({ data: 'Test data' });
expect(result.current.error).toBeNull();
});
expect(result.current.loading).toBe(true);
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
525
expect(result.current.data).toBeNull();
expect(result.current.error).toEqual(new Error('API Error'));
});
This test ensures the hook handles successful data fetching and errors appropriately.
Question: How do you test a form submission with validation logic using React Testing
Library?
Answer:
Testing forms involves ensuring that the form validates inputs correctly before submission.
This helps confirm that users cannot submit invalid data.
For Example:
// SignUpForm.js
import React, { useState } from 'react';
function SignUpForm() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
return (
<form onSubmit={handleSubmit}>
<input
type="email"
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
526
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<button type="submit">Sign Up</button>
{error && <p>{error}</p>}
</form>
);
}
// SignUpForm.test.js
import { render, fireEvent, screen } from '@testing-library/react';
import SignUpForm from './SignUpForm';
This test ensures that the form displays an error message for invalid email input.
Question: How do you test a complex React component that interacts with Redux for state
management?
Answer:
When testing React components that interact with Redux, you can use a mock store to
isolate the component and verify its interaction with the store.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
527
// Counter.js
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
</div>
);
}
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import Counter from './Counter';
fireEvent.click(screen.getByText('Increment'));
const actions = store.getActions();
expect(actions).toEqual([{ type: 'INCREMENT' }]);
});
This test checks if clicking the increment button dispatches the correct action.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
528
Question: How do you test that a loading spinner displays during data fetching in a React
component?
Answer:
Testing components that show a loading spinner during data fetching ensures that users
receive feedback while waiting for data.
For Example:
// DataFetchingComponent.js
import React, { useEffect, useState } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
setLoading(false);
};
fetchData();
}, []);
return (
<div>
{loading ? <p>Loading...</p> : <p>Data: {JSON.stringify(data)}</p>}
</div>
);
}
// DataFetchingComponent.test.js
import { render, screen } from '@testing-library/react';
import DataFetchingComponent from './DataFetchingComponent';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
529
Promise.resolve({
json: () => Promise.resolve({ data: 'Test data' }),
})
);
This test verifies that the loading message is displayed while data is being fetched.
Question: How do you test a pagination component that fetches data for each page?
Answer:
Testing pagination ensures that the correct data is fetched for each page and displayed to
the user.
For Example:
// PaginationComponent.js
import React, { useEffect, useState } from 'react';
function PaginationComponent() {
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`/api/data?page=${currentPage}`);
const result = await response.json();
setData(result);
};
fetchData();
}, [currentPage]);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
530
return (
<div>
{data.map((item) => (
<p key={item.id}>{item.name}</p>
))}
<button onClick={() => setCurrentPage((prev) => prev + 1)}>Next</button>
</div>
);
}
// PaginationComponent.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import PaginationComponent from './PaginationComponent';
fetch.mockResolvedValueOnce({
json: () => Promise.resolve([{ id: 2, name: 'Item 2' }]),
});
This test ensures that the correct data is fetched and displayed when navigating between
pages.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
531
Question: How do you test a modal component that opens and closes based on user
interactions?
Answer:
Testing modal components involves verifying that they open and close correctly in response
to user actions.
For Example:
// Modal.js
import React, { useState } from 'react';
return (
<div>
<div>Modal Content</div>
<button onClick={onClose}>Close</button>
</div>
);
}
// Modal.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Modal from './Modal';
expect(screen.getByText('Modal Content')).toBeInTheDocument();
fireEvent.click(screen.getByText('Close'));
expect(onClose).toHaveBeenCalled();
});
This test ensures that the modal opens when isOpen is true and calls the onClose function
when the close button is clicked.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
532
Question: How do you test an infinite scroll component that loads more data as the user
scrolls down?
Answer:
Testing infinite scroll ensures that new data is fetched as the user scrolls to the bottom of the
component.
For Example:
// InfiniteScroll.js
import React, { useEffect, useState } from 'react';
function InfiniteScroll() {
const [items, setItems] = useState([]);
useEffect(() => {
const loadMoreItems = () => {
fetch('/api/items')
.then((res) => res.json())
.then((data) => setItems((prev) => [...prev, ...data]));
};
loadMoreItems();
window.addEventListener('scroll', loadMoreItems);
return () => {
window.removeEventListener('scroll', loadMoreItems);
};
}, []);
return (
<div>
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
533
// InfiniteScroll.test.js
import { render } from '@testing-library/react';
import InfiniteScroll from './InfiniteScroll';
window.scrollTo(0, document.body.scrollHeight);
fetch.mockResolvedValueOnce({
json: () => Promise.resolve([{ id: 2, name: 'Item 2' }]),
});
This test ensures that additional items are fetched and rendered when the user scrolls.
Question: How do you test that localized strings are rendered correctly in a React
component?
Answer:
Testing localization ensures that the correct strings are displayed based on the user’s
language preference. You can use a localization library and verify that the appropriate
translations are rendered.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
534
// LocalizationContext.js
import React, { createContext, useContext } from 'react';
return (
<LocalizationContext.Provider value={strings[language]}>
{children}
</LocalizationContext.Provider>
);
};
// Welcome.js
import React from 'react';
import { useLocalization } from './LocalizationContext';
function Welcome() {
const { welcome } = useLocalization();
return <h1>{welcome}</h1>;
}
// Welcome.test.js
import { render } from '@testing-library/react';
import { LocalizationProvider } from './LocalizationContext';
import Welcome from './Welcome';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
535
expect(screen.getByText('Welcome')).toBeInTheDocument();
});
This test checks that the correct localized strings are rendered based on the provided
language context.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
536
THEORETICAL QUESTIONS
1. What is serverless architecture, and how does it benefit JavaScript
applications?
Answer:
Serverless architecture allows developers to build and run applications without managing
server infrastructure. Instead of provisioning servers, developers use cloud services like AWS
Lambda or Google Cloud Functions, which automatically scale and manage the execution
environment. This approach benefits JavaScript applications by simplifying deployment and
reducing operational costs.
For example, when using AWS Lambda, developers can upload their JavaScript code, define
triggers (like HTTP requests via API Gateway), and the service handles scaling based on
demand. This means that resources are only used when needed, leading to cost savings.
For Example:
Here's a simple AWS Lambda function written in JavaScript:
Answer:
AWS Lambda is a serverless compute service that lets developers run code without
provisioning or managing servers. It supports JavaScript through Node.js, allowing
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
537
developers to write Lambda functions in JavaScript. When an event occurs (like an API call or
a scheduled task), AWS Lambda runs the specified code in response.
Lambda scales automatically with the number of incoming requests, so there is no need to
worry about infrastructure management. This architecture is particularly useful for
microservices or backend functionalities.
For Example:
To set up a simple AWS Lambda function in JavaScript:
In this example, the function logs the incoming event and returns a success message.
3. What are Cloud Functions, and how do they differ from AWS Lambda?
Answer:
Cloud Functions are Google Cloud's serverless execution environment that allows developers
to run code in response to events. They differ from AWS Lambda in terms of service
integration, environment setup, and the specific cloud ecosystem they belong to. While both
services provide similar functionality in terms of scaling and cost efficiency, they are tailored
to their respective cloud platforms.
Cloud Functions support a variety of languages, including JavaScript (Node.js), and integrate
seamlessly with other Google services like Firebase, Pub/Sub, and Google Cloud Storage. This
makes them ideal for building applications within the Google Cloud ecosystem.
For Example:
Here’s a simple Google Cloud Function example in JavaScript:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
538
Answer:
Edge computing refers to the practice of processing data closer to the source of generation
rather than relying on a centralized data center. This is particularly significant in JavaScript
development as it reduces latency, improves performance, and enhances user experience by
allowing real-time data processing.
With the rise of IoT devices and mobile applications, JavaScript can run on edge servers, such
as those provided by Cloudflare Workers or Deno Deploy, enabling developers to build
applications that respond quickly to user actions.
For Example:
In a Cloudflare Worker, you can create an API that runs at the edge:
This example shows a simple Worker that responds to fetch events with a message.
Answer:
Deno is a modern runtime for JavaScript and TypeScript that is built on Rust and V8. It differs
from Node.js in several key aspects, including security, module management, and built-in
TypeScript support.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
539
Deno executes code in a secure sandbox by default, requiring explicit permissions to access
files, network, and environment variables. This enhances security compared to Node.js, where
the code has full access to the operating system by default.
For Example:
Here’s how you would import a module in Deno:
In this example, Deno serves an HTTP response while demonstrating its module system.
Answer:
JavaScript can be effectively used in IoT development through frameworks and libraries that
enable communication with IoT devices. Platforms like Node-RED or Johnny-Five provide
tools for building IoT applications using JavaScript.
These tools allow developers to control hardware, gather data from sensors, and
communicate with cloud services, all using JavaScript, making it accessible for web
developers to enter the IoT space.
For Example:
Here’s a basic example using Johnny-Five to control an LED:
board.on("ready", () => {
const led = new Led(13);
led.blink(500); // Blink LED every 500ms
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
540
Answer:
TensorFlow.js is a library that enables developers to define, train, and run machine learning
models in JavaScript. It allows for both browser-based and Node.js environments, making it
versatile for various applications.
Using TensorFlow.js, developers can leverage pre-trained models or build custom models to
perform tasks like image recognition, natural language processing, and more. This opens up
possibilities for adding AI capabilities directly into web applications without needing a
backend.
For Example:
Here’s a simple example of loading a pre-trained model to classify an image:
In this example, an image is classified using the MobileNet model, demonstrating how to
implement AI in a JavaScript application.
Answer:
Brain.js is a JavaScript library for neural networks that allows developers to create and train
networks in a straightforward manner. It provides an easy interface for defining different
types of networks, including feedforward and recurrent networks, and can be run in both
Node.js and browser environments.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
541
Brain.js is ideal for projects requiring basic machine learning functionalities, such as pattern
recognition, prediction, and classification tasks. It is particularly useful for developers who
may not have a strong background in machine learning but want to incorporate AI features
into their applications.
For Example:
Here’s a simple neural network example using Brain.js:
net.train([{ input: [0, 0], output: [0] }, { input: [0, 1], output: [1] }]);
In this example, the neural network learns the XOR function, demonstrating how Brain.js can
be used for simple AI tasks.
Answer:
WebAssembly (Wasm) is a binary instruction format that allows code written in languages
like C, C++, and Rust to run in the browser at near-native speed. It complements JavaScript by
enabling performance-intensive tasks to be executed more efficiently than JavaScript alone.
For Example:
Here’s a simple way to compile and use WebAssembly in a JavaScript application:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
542
}
loadWasm();
In this example, a WebAssembly module is loaded and its exported function is called from
JavaScript.
10. What are Progressive Web Apps (PWAs), and what role does JavaScript
play in their development?
Answer:
Progressive Web Apps (PWAs) are web applications that utilize modern web capabilities to
deliver an app-like experience to users. They are designed to work on any platform that uses
a standards-compliant browser and offer features like offline access, push notifications, and
installation on the user’s device.
For Example:
Here’s how you might register a service worker in a PWA:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('Service Worker registered with scope:',
registration.scope);
});
});
}
In this example, a service worker is registered, allowing the PWA to cache resources and
provide offline functionality.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
543
Answer:
Modern frontend tools like Vite significantly improve JavaScript development by providing
faster builds and enhanced development experiences. Vite leverages native ES modules and
serves files over native ESM in the browser, resulting in faster refresh times during
development. This approach eliminates the need for complex bundling during the
development phase, which can slow down the feedback loop.
Additionally, Vite includes features like hot module replacement (HMR), which allows
developers to see changes in real-time without losing application state. These improvements
streamline the development process, making it more efficient and enjoyable.
For Example:
To create a new project with Vite, you can use the following command:
This command initializes a new Vite project, providing a fast and modern setup for building
JavaScript applications.
Answer:
TurboRepo is a high-performance build system for JavaScript and TypeScript monorepos. It
optimizes the build process by leveraging caching and parallel execution, enabling
developers to work with large codebases efficiently.
By using TurboRepo, teams can significantly reduce build times, enhance productivity, and
streamline workflows across multiple packages within a monorepo. It also simplifies the
management of dependencies and versioning, making it easier to maintain complex
projects.
For Example:
To add TurboRepo to a project, you can initialize it with:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
544
This command sets up TurboRepo, allowing you to configure builds and manage tasks across
your monorepo efficiently.
Answer:
Next.js is a popular React framework that simplifies server-side rendering (SSR) for JavaScript
applications. It allows developers to build applications that can render pages on the server,
improving performance and SEO by delivering fully rendered HTML to the client.
With Next.js, developers can create pages that load data asynchronously during the server-
side rendering process, resulting in faster initial load times and a better user experience. The
framework also supports static site generation, allowing developers to pre-render pages at
build time.
For Example:
Here’s how you can implement SSR in a Next.js page:
In this example, the getServerSideProps function fetches data at request time, ensuring
that the page is rendered with up-to-date content.
Answer:
Micro-frontends is an architectural style where a web application is built as a composition of
smaller, independent frontend applications. Each micro-frontend can be developed,
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
545
deployed, and scaled independently, allowing teams to work on different parts of the
application without impacting each other.
This approach brings several advantages, including improved scalability, easier maintenance,
and the ability to use different technologies for different parts of the application. It also
facilitates collaboration across teams, as each team can own and manage their respective
micro-frontend.
For Example:
In a micro-frontend architecture, you might have separate teams handling different parts of
an application, like the user profile, dashboard, and settings. Each team can choose their
technology stack and deploy independently, while a shell application orchestrates the overall
user experience.
15. What are the best practices for building Progressive Web Apps with
JavaScript?
Answer:
Building Progressive Web Apps (PWAs) with JavaScript involves following several best
practices to ensure optimal performance and user experience. Some key practices include:
1. Service Worker Implementation: Use service workers for caching assets and enabling
offline functionality.
2. Responsive Design: Ensure the app is responsive and works on various devices and
screen sizes.
3. Performance Optimization: Optimize loading times by using techniques like lazy
loading and code splitting.
4. HTTPS: Serve the application over HTTPS to ensure security.
5. Manifest File: Include a web app manifest to control how the app appears when
installed on a device.
For Example:
A simple service worker implementation might look like this:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
546
This example demonstrates how to cache essential files during the service worker installation
process.
Answer:
Declarative UI is a programming paradigm where developers describe what the UI should
look like based on the application's state rather than how to change the UI. Modern
JavaScript frameworks like React and Vue.js embrace this approach, allowing developers to
build user interfaces that automatically update when the underlying data changes.
In declarative UI, the framework takes care of updating the DOM, resulting in more
predictable and easier-to-maintain code. This leads to enhanced development speed and
reduced bugs, as developers can focus on defining the desired state without worrying about
manual DOM manipulation.
For Example:
In React, a declarative approach might look like this:
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Here, the UI automatically reflects the current count value without requiring direct DOM
manipulation.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
547
Answer:
State management libraries, such as Redux and MobX, play a crucial role in managing the
state of modern JavaScript applications. They provide a centralized store to hold the
application’s state, allowing components to access and update state in a predictable manner.
Using state management libraries helps in maintaining consistency across the application,
especially in large and complex applications where multiple components depend on shared
state. These libraries also facilitate debugging and testing, as developers can track state
changes over time.
For Example:
Here’s a basic example of using Redux for state management:
In this example, Redux is used to manage a simple count state, showcasing its ability to
handle state changes.
Answer:
Routing in modern JavaScript applications is typically handled by libraries such as React
Router for React applications or Vue Router for Vue.js applications. These libraries enable
developers to define navigation paths within the application, allowing users to move
between different views or components based on the URL.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
548
Implementing routing involves creating routes that map URLs to specific components,
enabling a single-page application (SPA) experience. This approach improves performance
by loading content dynamically without refreshing the entire page.
For Example:
Here’s how you would set up basic routing in a React application using React Router:
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
</Switch>
</Router>
);
}
In this example, the application routes to the Home component for the root path and the
About component for the /about path.
Answer:
Code splitting is a technique that allows developers to break down their JavaScript
applications into smaller, manageable chunks that can be loaded on demand. This is
particularly significant in large applications where loading the entire codebase at once can
lead to long loading times and a poor user experience.
By implementing code splitting, developers can improve performance, as only the necessary
code for the current view is loaded initially. This reduces the initial load time and improves
responsiveness, especially in single-page applications (SPAs).
For Example:
In a React application, you can use dynamic imports for code splitting:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
549
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</React.Suspense>
);
}
In this example, the OtherComponent is loaded only when needed, improving the initial
loading performance of the application.
20. Explain the role of build tools like Webpack in JavaScript development.
Answer:
Webpack is a powerful module bundler that plays a crucial role in modern JavaScript
development. It processes and bundles JavaScript files, as well as other assets like CSS,
images, and HTML, into a single output file or multiple files optimized for production.
Using Webpack allows developers to take advantage of features like code splitting, tree
shaking, and asset management, which improve application performance and
maintainability. Webpack’s extensive plugin ecosystem also enables customization and
enhancement of the build process, making it a valuable tool in the JavaScript development
workflow.
For Example:
A basic Webpack configuration might look like this:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
550
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
};
In this example, Webpack is configured to bundle JavaScript files from the src directory into
a single bundle.js file in the dist directory, using Babel for transpilation.
Answer:
Closures are a fundamental concept in JavaScript that occurs when a function retains access
to its lexical scope, even when the function is executed outside that scope. This is possible
because functions in JavaScript create their own scope and can "close over" variables that
were defined in that scope, allowing for data encapsulation and privacy.
Closures are useful for creating private variables, implementing factory functions, and
managing state in asynchronous operations.
For Example:
Here’s an example demonstrating closures:
function makeCounter() {
let count = 0; // Private variable
return function() {
count++; // Accessing the private variable
return count;
};
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
551
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
In this example, the inner function retains access to the count variable, demonstrating how
closures allow for private state.
Answer:
Synchronous programming executes code sequentially, meaning each operation must
complete before the next one begins. This can lead to blocking, where the execution of the
entire program halts while waiting for time-consuming tasks, such as network requests or
file reading.
Asynchronous programming, on the other hand, allows operations to run concurrently. This
is particularly useful for tasks that take time to complete, as it enables the program to
continue executing other code while waiting for the asynchronous task to finish. JavaScript
handles asynchronous programming through callbacks, promises, and async/await syntax,
allowing for more responsive applications.
For Example:
Here's a comparison of synchronous and asynchronous code:
// Synchronous
console.log('Start');
console.log('Synchronous operation');
console.log('End');
// Asynchronous
console.log('Start');
setTimeout(() => {
console.log('Asynchronous operation');
}, 1000);
console.log('End');
In the asynchronous example, the "End" message is logged before the asynchronous
operation completes, demonstrating non-blocking behavior.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
552
Answer:
Optimizing performance in JavaScript applications involves various techniques that aim to
enhance speed and efficiency. Key strategies include:
For Example:
Here’s an example of debouncing a function:
window.addEventListener('resize', handleResize);
In this example, the handleResize function is debounced, ensuring it only executes after the
resize event has stopped for 200 milliseconds, improving performance.
24. What is the event loop in JavaScript, and how does it work?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
553
Answer:
The event loop is a fundamental mechanism in JavaScript that allows for asynchronous
programming. It continuously checks the call stack for pending function calls and the
message queue for any messages (events) that need to be processed. When the call stack is
empty, the event loop dequeues messages from the message queue and executes their
associated callback functions.
This mechanism enables JavaScript to handle asynchronous operations without blocking the
main thread, allowing for smooth user interactions and responsiveness.
For Example:
Here’s a simple demonstration of the event loop:
console.log('Start');
setTimeout(() => {
console.log('Timeout callback');
}, 0);
console.log('End');
This shows that the synchronous code runs first, and only after the call stack is empty does
the timeout callback execute, illustrating how the event loop processes asynchronous tasks.
25. What are Promises and how do they improve error handling in
JavaScript?
Answer:
Promises are objects that represent the eventual completion (or failure) of an asynchronous
operation and its resulting value. They provide a more manageable way to handle
asynchronous operations compared to callbacks, particularly regarding error handling.
With promises, you can chain .then() methods for success and use .catch() for errors,
allowing for cleaner and more readable code. Promises help avoid "callback hell" and make it
easier to manage complex asynchronous flows.
For Example:
Here’s how you can use promises:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
554
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
In this example, the promise resolves successfully, and the resulting message is logged,
demonstrating how promises improve error handling and code clarity.
Answer:
In JavaScript, null and undefined are both primitive values but serve different purposes.
undefined is the default value assigned to uninitialized variables or when a function does not
return a value. It signifies that a variable has been declared but has not yet been assigned a
value.
On the other hand, null is an intentional assignment value that represents the absence of
any object value. It indicates that a variable should be empty or has been deliberately set to
have no value.
For Example:
Here’s a demonstration of their differences:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
555
In this example, a is undefined, while b is null, showcasing their distinct meanings and uses.
27. How do you implement inheritance in JavaScript, and what are the
different ways to achieve it?
Answer:
Inheritance in JavaScript can be implemented using several methods, including:
For Example:
Here’s an example using ES6 classes:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
556
console.log(`${this.name} barks.`);
}
}
In this example, the Dog class inherits from the Animal class, overriding the speak method to
provide specific behavior.
28. What are template literals in JavaScript, and how do they differ from
traditional string concatenation?
Answer:
Template literals are a feature introduced in ECMAScript 2015 (ES6) that allow for easier and
more readable string manipulation. They use backticks (`) instead of quotes, enabling multi-
line strings and embedded expressions through placeholders indicated by the
${expression} syntax.
1. Multi-line Strings: They allow for easy creation of strings that span multiple lines
without the need for escape characters.
2. String Interpolation: You can easily embed variables and expressions directly into the
string.
For Example:
Here’s a comparison of traditional string concatenation and template literals:
// Traditional concatenation
const greeting1 = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
557
In this example, template literals offer a more concise and readable way to construct strings
compared to traditional concatenation.
29. How does the spread operator work in JavaScript, and what are its
common use cases?
Answer:
The spread operator, represented by three dots (...), is a syntax introduced in ES6 that
allows iterable objects (like arrays and strings) to be expanded into individual elements. This
operator can be used in various contexts, including function calls, array literals, and object
literals.
For Example:
Here’s how the spread operator can be used:
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
// Merging objects
const mergedObject = { ...obj1, ...obj2 }; // Output: { a: 1, b: 3, c: 4 }
console.log(mergedArray);
console.log(mergedObject);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
558
In this example, the spread operator is used to merge arrays and objects, showcasing its
versatility and convenience.
30. What are JavaScript modules, and how do they differ from scripts?
Answer:
JavaScript modules are reusable pieces of code that can be exported from one file and
imported into another. They provide a way to encapsulate functionality and manage
dependencies between different parts of an application, promoting better organization and
maintainability.
For Example:
Here’s how to define and use a module:
// math.js
export function add(x, y) {
return x + y;
}
// main.js
import { add } from './math.js';
In this example, the add function is defined in a module and imported into another file,
demonstrating how modules improve code organization and reusability.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
559
Answer:
Error handling in asynchronous JavaScript is crucial for maintaining application stability and
providing a good user experience. There are several best practices for handling errors
effectively:
1. Use try/catch with async/await: When using async/await, wrap your code in a
try/catch block to handle rejected promises easily.
2. Use .catch() for Promises: When working with promises, always attach a .catch()
method to handle any potential errors that may arise during the asynchronous
operation.
3. Graceful Degradation: Ensure that your application can handle errors gracefully,
providing feedback to users without crashing.
4. Logging: Implement logging mechanisms to capture and record errors for
debugging purposes.
For Example:
Here’s how to handle errors with async/await and promises:
fetchData('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error('Promise error:', error));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
560
In this example, both async/await and promise error handling are demonstrated, showcasing
how to manage errors effectively in asynchronous operations.
32. What are the new features introduced in ES2020 (ES11), and how do
they enhance JavaScript development?
Answer:
ES2020 introduced several new features that enhance JavaScript development, making code
more concise, efficient, and easier to manage. Some notable features include:
1. Optional Chaining (?.): This feature allows safe access to deeply nested properties
without having to check for the existence of each level, preventing runtime errors.
2. Nullish Coalescing Operator (??): This operator provides a way to set default values
only when the left-hand side is null or undefined, improving how defaults are
assigned.
3. BigInt: A new primitive type for representing integers larger than 2^53 - 1, which
allows for precise arithmetic on very large numbers.
4. Dynamic import(): This enables loading modules dynamically, making it possible to
load code on demand.
For Example:
Here’s how to use optional chaining and nullish coalescing:
const user = {
name: 'Alice',
address: {
street: '123 Main St',
city: 'Wonderland',
},
};
In this example, optional chaining prevents errors when accessing nested properties, and the
nullish coalescing operator provides a default value only when necessary.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
561
Answer:
Functional programming is a programming paradigm that treats computation as the
evaluation of mathematical functions and avoids changing state and mutable data. In
JavaScript, functional programming is widely used and encourages the following core
principles:
For Example:
Here’s an example illustrating functional programming concepts:
// Higher-order function
const map = (arr, fn) => arr.map(fn);
// Pure function
const square = (x) => x * x;
In this example, map is a higher-order function that takes another function (square) as an
argument, demonstrating functional programming principles in action.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
562
Answer:
Immutability refers to the idea that data cannot be changed after it has been created. In
JavaScript, immutability can lead to more predictable and maintainable code, especially in
applications where state management is crucial (e.g., in React applications).
For Example:
Here’s how to achieve immutability using the spread operator:
const originalObject = { a: 1, b: 2 };
const newObject = { ...originalObject, c: 3 }; // Create a new object
console.log(originalObject); // Output: { a: 1, b: 2 }
console.log(newObject); // Output: { a: 1, b: 2, c: 3 }
In this example, the original array and object remain unchanged, demonstrating how to
implement immutability in JavaScript.
35. How do you create and use decorators in JavaScript, and what are their
common use cases?
Answer:
Decorators in JavaScript are a special kind of declaration that can be attached to a class or
method to modify its behavior. While decorators are not yet part of the official JavaScript
specification, they can be implemented using a proposal in environments that support them
(such as TypeScript or Babel).
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
563
1. Logging: Adding logging capabilities to class methods for tracking calls and
performance.
2. Validation: Implementing input validation for method parameters.
3. Access Control: Restricting access to certain methods based on user roles.
For Example:
Here’s a simple example of a method decorator:
descriptor.value = function(...args) {
console.log(`Calling ${key} with arguments: ${args}`);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
In this example, the log decorator enhances the add method to log its calls, illustrating how
decorators can modify method behavior.
36. What are the differences between shallow copy and deep copy in
JavaScript?
Answer:
In JavaScript, copying an object can be done in two ways: shallow copy and deep copy.
Understanding these concepts is essential for managing data structures effectively.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
564
1. Shallow Copy: A shallow copy creates a new object that has the same properties as
the original object, but it only copies the references of nested objects. This means that
changes to nested objects will affect both the original and the copied object.
2. Deep Copy: A deep copy creates a new object and recursively copies all nested
objects, ensuring that the original and copied objects are completely independent.
Changes to nested objects in the copied object do not affect the original object.
For Example:
Here’s how to create shallow and deep copies:
const original = { a: 1, b: { c: 2 } };
console.log(original.b.c); // Output: 3
console.log(original.b.c); // Output: 3
In this example, the shallow copy modifies the original object due to shared references, while
the deep copy remains independent.
37. What is a service worker, and how does it enhance web application
performance?
Answer:
A service worker is a script that runs in the background of a web application, separate from
the main browser thread. It acts as a proxy between the web application and the network,
enabling features like caching, background sync, and push notifications.
1. Offline Support: They allow applications to work offline by caching assets and
resources.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
565
For Example:
Here’s a basic implementation of a service worker:
In this example, the service worker caches specified files during installation and serves
cached responses when possible, improving the application’s performance and offline
capabilities.
38. Explain how to manage state in React applications and the role of
hooks like useState and useReducer.
Answer:
Managing state in React applications is essential for creating dynamic and interactive user
interfaces. React provides several tools for state management, including hooks like useState
and useReducer.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
566
1. useState: This hook allows functional components to manage local state. It returns a
state variable and a function to update that state, enabling components to respond to
user interactions and other changes.
2. useReducer: This hook is useful for managing more complex state logic. It allows you
to define a reducer function that specifies how the state changes in response to
actions. This approach is similar to Redux and is beneficial for applications with
intricate state management needs.
For Example:
Here’s how to use useState and useReducer:
// Using useState
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Using useReducer
import React, { useReducer } from 'react';
function CounterWithReducer() {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
567
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment'
})}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement'
})}>Decrement</button>
</div>
);
}
In these examples, useState is used for simple state management, while useReducer
provides a structured way to handle more complex state transitions.
39. What are web components, and how do they enhance modularity in
web development?
Answer:
Web components are a set of web platform APIs that allow developers to create reusable
custom elements with encapsulated functionality and styling. They consist of three main
technologies:
For Example:
Here’s a simple web component definition:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
568
super();
const shadow = this.attachShadow({ mode: 'open' });
const button = document.createElement('button');
button.textContent = 'Click me';
button.addEventListener('click', () => {
alert('Button clicked!');
});
shadow.appendChild(button);
}
}
customElements.define('my-button', MyButton);
In this example, a custom button element is defined using the Web Components API,
allowing it to be used anywhere in the HTML.
Answer:
Handling performance optimization for large-scale JavaScript applications involves several
strategies to ensure responsiveness and efficient resource management:
1. Code Splitting: Use dynamic imports and tools like Webpack to split the codebase
into smaller chunks, loading only what is necessary.
2. Lazy Loading: Implement lazy loading for images and other assets, ensuring they are
only loaded when they enter the viewport.
3. Memoization: Use memoization techniques to cache expensive function calls and
avoid redundant computations.
4. Debouncing and Throttling: Limit the rate of function executions for events like
scrolling or resizing to reduce workload during high-frequency events.
5. Reduce Repaints and Reflows: Minimize direct DOM manipulations and batch
updates to prevent excessive layout recalculations.
For Example:
Here’s an example of memoization using a simple function:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
569
return function(...args) {
const key = JSON.stringify(args);
if (!cache[key]) {
cache[key] = fn(...args);
}
return cache[key];
};
};
SCENARIO QUESTIONS
Scenario 41: Implementing Serverless Functions
You are tasked with building a simple serverless function to handle user sign-ups for a web
application using AWS Lambda. The function should receive user data, save it to a database,
and return a success message.
Question: How would you structure the AWS Lambda function to handle user sign-ups?
What considerations would you have for error handling?
Answer:
To implement a serverless function for user sign-ups using AWS Lambda, I would first set up
the Lambda function to handle incoming events, likely from an API Gateway. The function
should validate the incoming user data, save it to a database (like DynamoDB), and return a
response.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
570
Error handling is crucial in this setup. I would use a try-catch block to catch any errors during
the database operation, ensuring the function returns a meaningful error message without
exposing sensitive information.
For Example:
Here’s how the AWS Lambda function could be structured:
try {
await dynamoDB.put(params).promise();
return {
statusCode: 200,
body: JSON.stringify({ message: 'User signed up successfully!' }),
};
} catch (error) {
console.error('Error saving user:', error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'User sign-up failed.' }),
};
}
};
In this function, I handle potential errors and provide appropriate responses based on the
operation's success or failure.
Your company is exploring edge computing to reduce latency for users in different
geographic locations. You decide to implement a feature using Cloudflare Workers to cache
API responses.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
571
Question: How would you set up a Cloudflare Worker to cache responses, and what benefits
would this bring?
Answer:
Setting up a Cloudflare Worker to cache API responses involves creating a worker script that
intercepts requests, fetches data from the original API, and stores it in the cache. This
reduces latency and improves response times for users accessing the data from various
locations.
The worker should check the cache before fetching the API data and store the response for
subsequent requests. This caching strategy can significantly enhance performance and
reduce load on the original API, especially for frequently requested data.
For Example:
Here’s a basic implementation of a Cloudflare Worker to cache API responses:
// Check cache
let response = await cache.match(cacheKey);
if (!response) {
response = await fetch(request);
// Cache the response for future requests
event.waitUntil(cache.put(cacheKey, response.clone()));
}
return response;
}
In this example, the worker checks the cache first. If the response is not cached, it fetches it
from the API, caches it, and then returns it to the client.
You are working on an IoT project where you need to collect data from various sensors and
send it to a central server using JavaScript.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
572
Question: What technologies or libraries would you use for this IoT project, and how would
you implement data transmission?
Answer:
For an IoT project collecting data from sensors and transmitting it to a server, I would use the
Johnny-Five library, which is a JavaScript framework for robotics and IoT. It allows you to
easily interface with various hardware components.
To implement data transmission, I would use the HTTP module or a WebSocket connection
to send sensor data to a central server. WebSockets are particularly useful for real-time
communication, allowing continuous data flow without the overhead of repeated HTTP
requests.
For Example:
Here’s how you might set up a simple IoT device using Johnny-Five to collect data and send
it to a server:
board.on('ready', () => {
const temperatureSensor = new Sensor('A0');
In this example, the temperature sensor data is collected and sent to a server endpoint using
Axios.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
573
You are developing a web application that requires image classification capabilities using
JavaScript. You decide to use TensorFlow.js.
Question: How would you implement image classification in your application using
TensorFlow.js?
Answer:
To implement image classification using TensorFlow.js, I would first load a pre-trained model,
such as MobileNet, which is suitable for image classification tasks. This allows the application
to leverage existing models without needing to train one from scratch.
The workflow would include capturing the image input from the user, processing the image
to fit the model's expected input shape, and then passing the image to the model for
classification. Finally, I would display the classification results to the user.
For Example:
Here’s a basic implementation of image classification using TensorFlow.js:
// Usage
const img = document.getElementById('myImage');
classifyImage(img);
In this example, the classifyImage function loads the MobileNet model, classifies the
provided image element, and logs the predictions.
You are tasked with optimizing a web application by integrating a WebAssembly module to
perform heavy computations.
Question: How would you create and use a WebAssembly module in your JavaScript
application?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
574
Answer:
To create and use a WebAssembly (Wasm) module, I would write the computationally
intensive code in a language that compiles to WebAssembly, such as C or Rust. After
compiling the code, I would load the Wasm module in my JavaScript application and invoke
its functions to perform the required computations.
This integration can lead to significant performance improvements, especially for tasks
involving large data processing or complex calculations, as WebAssembly executes at near-
native speed.
For Example:
Here’s how to load and use a WebAssembly module in JavaScript:
loadWasmModule();
In this example, the WebAssembly module is loaded, and a function is called, showcasing
how to leverage Wasm in a JavaScript application.
You are developing a Progressive Web App (PWA) that should work offline and provide a
smooth user experience.
Question: What steps would you take to ensure your web application behaves like a PWA,
and how would you implement a service worker?
Answer:
To ensure my web application behaves like a Progressive Web App, I would follow these
steps:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
575
3. Implement a Service Worker: The service worker manages caching and enables
offline functionality by intercepting network requests.
The service worker should cache essential assets during the installation phase and serve
cached content when the user is offline.
For Example:
Here’s how to implement a service worker for a PWA:
In this example, the service worker caches the specified files and serves them when offline,
providing a seamless experience for the user.
You are building a new React application and want to improve your development experience
by using modern tools.
Question: What tools would you choose for your frontend development setup, and how
would they benefit your workflow?
Answer:
For building a new React application, I would choose tools like Vite, Next.js, and TurboRepo.
Each tool provides specific advantages that enhance the development workflow:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
576
1. Vite: This build tool offers fast development and hot module replacement (HMR),
making it ideal for React development. Its optimized build process improves initial
load times.
2. Next.js: It provides built-in server-side rendering (SSR) capabilities, which enhances
SEO and performance. It also simplifies routing and static site generation.
3. TurboRepo: This tool helps manage monorepos effectively, allowing for efficient
builds and shared code across multiple projects.
For Example:
When creating a new project with Vite, I would run:
This command sets up a new React project with Vite, optimizing my development
experience right from the start.
You are tasked with creating a simple neural network to predict user behavior on your
website using Brain.js.
Question: How would you set up a basic neural network with Brain.js for this task?
Answer:
To create a simple neural network with Brain.js, I would first install the Brain.js library and
then define the architecture of the network based on the input data I want to train it on. This
could include user interaction metrics like time spent on the site, click patterns, etc.
The network can be trained using historical data, and after training, it can be used to make
predictions about future user behavior based on new input data.
For Example:
Here’s how to set up a basic neural network using Brain.js:
net.train([
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
577
In this example, the neural network is trained on a simple XOR pattern, and the trained
model can be used for making predictions.
You have a performance-critical algorithm written in C that needs to be integrated into your
JavaScript application using WebAssembly.
Question: What steps would you take to compile the C code to WebAssembly, and how
would you use it in your JavaScript application?
Answer:
To compile C code to WebAssembly, I would use a tool like Emscripten, which allows C/C++
code to be compiled into WebAssembly. The steps involved are:
For Example:
Here’s how to load and use the compiled WebAssembly module:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
578
loadWasm();
In this example, the WebAssembly module is loaded, and a function from the compiled C
code is called in the JavaScript application.
You are developing a large-scale web application and need to optimize its performance to
handle a significant number of users efficiently.
Question: What strategies would you implement to optimize the performance of your large-
scale application?
Answer:
To optimize the performance of a large-scale web application, I would implement the
following strategies:
1. Code Splitting: Use dynamic imports and techniques like lazy loading to reduce initial
load times and only load necessary code for the user’s current view.
2. Caching: Implement caching strategies both on the client-side (using service
workers) and server-side to reduce server load and improve response times.
3. Optimize Images and Assets: Use modern formats (like WebP) and responsive image
techniques to minimize file sizes without sacrificing quality.
4. Performance Monitoring: Utilize tools like Google Lighthouse and performance APIs
to continuously monitor and identify bottlenecks in the application.
5. CDN Usage: Distribute static assets through a Content Delivery Network (CDN) to
serve users from the nearest geographic location.
For Example:
Here’s how to implement code splitting in a React application:
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
579
<OtherComponent />
</React.Suspense>
);
}
You are developing a web form that collects user information. When the form is submitted,
you need to validate the input and display an error message if any field is empty.
Answer:
To implement form validation in JavaScript, I would create an event listener for the form's
submit event. In this listener, I would check if the required fields are filled out. If any fields are
empty, I would prevent the form from being submitted and display an error message to the
user.
For Example:
Here’s how the validation could be set up:
document.getElementById('myForm').addEventListener('submit', function(event) {
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
if (!name || !email) {
event.preventDefault(); // Prevent form submission
alert('Please fill in all fields.');
}
});
In this example, the form submission is prevented if any fields are empty, ensuring that the
user provides all necessary information.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
580
You need to load user data dynamically into a web page after it has been rendered without
refreshing the page.
Question: What approach would you take to fetch and display this data using JavaScript?
Answer:
To load user data dynamically without refreshing the page, I would use the Fetch API to
make an asynchronous request to the server. Once the data is retrieved, I would manipulate
the DOM to display the data in the appropriate section of the web page.
For Example:
Here’s how this could be implemented:
In this example, user data is fetched from an API, and the results are displayed in the
specified container on the page.
You are working on a feature that fetches data from an external API. You need to ensure that
the application handles both successful and unsuccessful responses appropriately.
Question: How would you manage API responses in your application using JavaScript?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
581
Answer:
To manage API responses effectively, I would use the Fetch API along with promise chaining
to handle both success and error cases. Upon receiving a response, I would check the HTTP
status code to determine whether the request was successful or if there was an error. Based
on the result, I would update the UI accordingly.
For Example:
Here’s an example of handling API responses:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => {
console.log('Data received:', data);
// Update the UI with the data
})
.catch(error => {
console.error('Error fetching data:', error);
// Display error message to the user
});
In this example, the application checks the response status and handles errors gracefully
while updating the UI with the received data.
You are building a web application that requires a simple timer to track how long users
spend on a specific page.
Question: How would you implement a timer using JavaScript that updates every second?
Answer:
To implement a simple timer, I would use setInterval() to create a timer that updates
every second. The timer would keep track of the elapsed time in seconds and update the
displayed time in the UI.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
582
For Example:
Here’s how this could be implemented:
let seconds = 0;
const timerDisplay = document.getElementById('timerDisplay');
// To stop the timer after a certain condition, you can call clearInterval(timer);
In this example, the timer increments every second and updates the display to show the
elapsed time.
You have a list of items displayed on a web page, and you want to implement a search
feature that filters the list based on user input.
Question: How would you create a filter function using JavaScript to search through the list?
Answer:
To create a filter function for searching through a list, I would attach an event listener to the
search input field. This listener would trigger a function that filters the displayed items based
on the user’s input and updates the visible list accordingly.
For Example:
Here’s how to implement this filter functionality:
function filterItems() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
const filteredItems = items.filter(item =>
item.toLowerCase().includes(searchTerm));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
583
document.getElementById('searchInput').addEventListener('input', filterItems);
In this example, the list of items is filtered in real-time based on the user’s input, providing a
dynamic search experience.
You are developing a user registration form that requires specific validation rules for email
and password fields.
Question: What approach would you take to validate these fields using JavaScript?
Answer:
To validate the email and password fields, I would create a function that checks if the email
format is correct using a regular expression. Additionally, I would check that the password
meets specific criteria, such as length and character requirements. Upon submission, I would
prevent the form from being submitted if validation fails and display error messages.
For Example:
Here’s how you might implement the validation:
document.getElementById('registrationForm').addEventListener('submit',
function(event) {
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
valid = false;
messages.push('Please enter a valid email address.');
}
if (password.length < 8) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
584
valid = false;
messages.push('Password must be at least 8 characters long.');
}
if (!valid) {
event.preventDefault(); // Prevent form submission
alert(messages.join('\n'));
}
});
In this example, validation checks are performed on the email and password fields, with
relevant error messages displayed if the input is invalid.
You are implementing a search feature that triggers a search function as the user types.
However, you want to prevent the search function from being called too frequently.
Question: How would you implement debouncing in this scenario using JavaScript?
Answer:
To implement debouncing, I would create a debounce function that limits how often the
search function is called as the user types. The debounce function would delay the execution
of the search function until the user has stopped typing for a specified period.
For Example:
Here’s how to implement debouncing for the search input:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
585
}, 300);
document.getElementById('searchInput').addEventListener('input', performSearch);
In this example, the search function is called only after the user has stopped typing for 300
milliseconds, improving performance and reducing unnecessary calls.
You need to fetch user data from an API and display it on the web page. You want to handle
the asynchronous nature of this operation using promises.
Question: How would you use promises to manage this asynchronous operation?
Answer:
To manage the asynchronous operation of fetching user data using promises, I would use the
Fetch API to request the data and handle the response using .then() and .catch() for
success and error cases. This approach keeps the code organized and manageable.
For Example:
Here’s how this could be implemented:
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('User data:', data);
// Update the UI with user data
})
.catch(error => {
console.error('Error fetching user data:', error);
// Display error message to the user
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
586
In this example, promises are used to handle the fetching of user data, providing clear paths
for success and error handling.
You need to create a modal dialog that opens when a button is clicked and closes when the
user clicks outside the modal.
Question: How would you implement this modal functionality using JavaScript?
Answer:
To implement a modal dialog, I would create HTML elements for the modal and overlay, style
them with CSS, and use JavaScript to control their visibility. An event listener on the button
would open the modal, and a click event listener on the overlay would close it.
For Example:
Here’s how this could be set up:
btn.addEventListener('click', () => {
modal.style.display = 'block';
overlay.style.display = 'block';
});
overlay.addEventListener('click', () => {
modal.style.display = 'none';
overlay.style.display = 'none';
});
In this example, clicking the button opens the modal and overlay, while clicking outside the
modal (on the overlay) closes both.
You are tasked with creating a simple todo list application where users can add and remove
tasks.
Question: How would you implement this todo list functionality using JavaScript?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
587
Answer:
To implement a simple todo list application, I would create an HTML form for adding tasks
and a section to display the list of tasks. Using JavaScript, I would manage the list of tasks in
an array and update the DOM dynamically when tasks are added or removed.
For Example:
Here’s how the todo list functionality could be set up:
document.getElementById('addTaskButton').addEventListener('click', () => {
const task = taskInput.value;
if (task) {
tasks.push(task);
updateTaskList();
taskInput.value = ''; // Clear the input field
}
});
function updateTaskList() {
taskList.innerHTML = tasks.map((task, index) =>
`<li>${task} <button onclick="removeTask(${index})">Remove</button></li>`
).join('');
}
function removeTask(index) {
tasks.splice(index, 1);
updateTaskList();
}
In this example, users can add tasks to the list, and each task includes a button to remove it,
demonstrating basic CRUD functionality with a todo list.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
588
You are developing a web application that displays a large list of items, and you need to
implement pagination to improve usability.
Question: How would you implement a pagination system in JavaScript, and what
considerations would you have for performance?
Answer:
To implement a pagination system in JavaScript, I would first determine the total number of
items and the number of items to display per page. Based on this information, I would
calculate the total number of pages and create a mechanism to navigate between them
(e.g., "Previous" and "Next" buttons).
For performance considerations, I would only fetch and render the items needed for the
current page instead of loading all items at once. This can be achieved through API calls that
accept pagination parameters, reducing load times and improving responsiveness.
For Example:
Here’s a basic implementation of a pagination system:
function renderItems(items) {
const itemList = document.getElementById('itemList');
itemList.innerHTML = items.map(item => `<li>${item.name}</li>`).join('');
}
document.getElementById('nextButton').addEventListener('click', () => {
currentPage++;
loadPage(currentPage);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
589
});
document.getElementById('prevButton').addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
loadPage(currentPage);
}
});
// Initial load
loadPage(currentPage);
In this example, the application fetches items for the current page, rendering only what’s
necessary and minimizing network requests.
You are building a React application that requires managing complex state with multiple
actions, such as incrementing, decrementing, and resetting a counter.
Question: How would you use the useReducer hook to manage this complex state
effectively?
Answer:
To manage complex state in a React application, I would use the useReducer hook, which is
suited for handling state transitions based on different actions. I would define an initial state
and a reducer function that specifies how the state should change in response to dispatched
actions.
Using useReducer provides clarity and structure, especially when dealing with multiple state
updates that depend on each other.
For Example:
Here’s how to set this up using useReducer:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
590
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment'
})}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement'
})}>Decrement</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
In this example, the useReducer hook effectively manages the counter's state based on the
dispatched actions, providing clear and maintainable code.
You are building an application that requires user authentication. You decide to use JSON
Web Tokens (JWT) for managing user sessions.
Question: How would you implement JWT authentication in your application, and what
security measures would you take?
Answer:
To implement JWT authentication, I would first create an API endpoint for user login that
validates the user’s credentials. Upon successful authentication, the server would generate a
JWT and send it back to the client. The client would store the JWT (usually in local storage or
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
591
For Example:
Here’s a simplified implementation of JWT authentication:
In this example, the server generates a JWT upon successful login, and the client uses the
token to access protected resources.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
592
You need to manage form inputs across multiple components in a React application. To
improve reusability, you decide to create a custom hook.
Question: How would you implement a custom hook for form handling in your application?
Answer:
To create a custom hook for form handling, I would define a hook that manages the form
state and provides handlers for input changes. This hook would return the current form
values and the functions to handle input updates, making it easy to reuse across different
components.
For Example:
Here’s how to implement a custom hook for managing form inputs:
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
return {
values,
handleChange,
};
}
// Usage in a component
function MyForm() {
const { values, handleChange } = useForm({ username: '', email: '' });
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
593
return (
<form onSubmit={handleSubmit}>
<input name="username" value={values.username} onChange={handleChange}
/>
<input name="email" value={values.email} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
In this example, the useForm custom hook manages form state and simplifies handling input
changes, improving code reusability.
You are developing a React application where some components may throw errors during
rendering. You want to ensure the application remains stable and provides feedback to users.
Question: How would you implement error boundaries in your React application?
Answer:
To implement error boundaries in a React application, I would create a higher-order
component (HOC) or a class component that utilizes the componentDidCatch lifecycle
method. This component would catch JavaScript errors in its child component tree, log the
error, and render a fallback UI instead of crashing the whole application.
For Example:
Here’s how to create an error boundary:
static getDerivedStateFromError(error) {
return { hasError: true }; // Update state to indicate an error occurred
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
594
componentDidCatch(error, info) {
console.error('Error caught by ErrorBoundary:', error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong. Please try again later.</h1>;
}
// Usage
function App() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
In this example, the ErrorBoundary component catches errors in its child components and
provides a user-friendly error message, improving the application's robustness.
You have a React application with a large number of components that re-render frequently.
You want to optimize the performance by preventing unnecessary re-renders.
Question: How would you use React.memo to improve the performance of your components?
Answer:
To optimize performance in a React application, I would use React.memo, a higher-order
component that prevents functional components from re-rendering when their props do not
change. This is especially useful for components that receive complex objects or arrays as
props.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
595
By wrapping components with React.memo, I can ensure they only re-render when their
relevant props change, reducing the rendering workload and improving application
responsiveness.
For Example:
Here’s how to implement React.memo:
// Parent component
function Parent() {
const [count, setCount] = useState(0);
const data = "Hello, World!";
return (
<div>
<MyComponent data={data} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In this example, MyComponent will only re-render when its data prop changes, allowing the
parent component to update without affecting the child unnecessarily.
You need to manage global state across multiple components in your React application. You
decide to use the Context API instead of a state management library.
Question: How would you implement state management using the Context API in your
application?
Answer:
To implement state management using the Context API in React, I would create a context
using React.createContext() and provide a state and updater function through the
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
596
context provider. This allows any nested components to access and update the global state
without prop drilling.
I would create a context provider component that wraps the application, allowing all child
components to consume the context values.
For Example:
Here’s how to set up the Context API for state management:
return (
<MyContext.Provider value={{ state, setState }}>
{children}
</MyContext.Provider>
);
}
// Usage in a component
function UserProfile() {
const { state, setState } = useContext(MyContext);
return (
<div>
<p>User: {state.user ? state.user.name : 'Not logged in'}</p>
<button onClick={login}>Login</button>
</div>
);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
597
</MyProvider>
);
}
In this example, the Context API allows for global state management without prop drilling,
making it easy for components to access and modify shared state.
You are developing a web application that requires user preferences to be saved even after
the browser is closed.
Question: How would you use Local Storage in your application to store and retrieve user
preferences?
Answer:
To use Local Storage for data persistence in a web application, I would utilize the
localStorage API, which allows storing key-value pairs in the browser. I would save user
preferences (like theme or language) whenever they change, and retrieve these preferences
on page load to set the initial state.
Local Storage persists data even after the browser is closed, making it ideal for storing user
settings.
For Example:
Here’s how to implement Local Storage for user preferences:
// Save preference
function savePreference(preference) {
localStorage.setItem('userPreference', JSON.stringify(preference));
}
// Usage
const userPreference = loadPreference();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
598
if (userPreference) {
console.log('Loaded user preference:', userPreference);
}
In this example, user preferences are saved to Local Storage and retrieved on page load,
allowing for persistence across sessions.
You are building a feature that triggers actions based on the user's scroll position, but you
want to limit how often these actions are executed to improve performance.
Question: How would you implement throttling for scroll events in your application?
Answer:
To implement throttling for scroll events, I would create a throttle function that limits how
often the event handler can be executed. This can be accomplished by tracking the time of
the last execution and only allowing the handler to run if a certain interval has passed.
Throttling ensures that the scroll event handler is executed at most once every specified
interval, preventing excessive calls and improving performance.
For Example:
Here’s how to implement throttling:
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
599
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
In this example, the scroll event is throttled to execute at most once every second, improving
the efficiency of the scroll handling logic.
You are tasked with creating a responsive navigation menu that collapses into a hamburger
menu on smaller screens.
Question: How would you implement this responsive navigation using JavaScript and CSS?
Answer:
To create a responsive navigation menu, I would use CSS media queries to style the menu for
different screen sizes. JavaScript would be used to toggle the visibility of the menu when the
hamburger icon is clicked.
The implementation involves setting up the HTML structure for the navigation menu and
adding event listeners for the toggle functionality.
For Example:
Here’s how this could be implemented:
<nav>
<div class="hamburger" id="hamburger">☰</div>
<ul class="nav-links" id="navLinks">
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
600
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<style>
.nav-links {
display: flex;
}
.hamburger {
display: none;
}
<script>
document.getElementById('hamburger').addEventListener('click', () => {
const navLinks = document.getElementById('navLinks');
navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex';
});
</script>
In this example, the navigation menu adapts to screen size, and the hamburger icon toggles
the visibility of the menu items, providing a responsive design that enhances user
experience.
You are building a dashboard that displays data from multiple APIs. Users can filter the data
based on different criteria, which results in multiple API calls.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
601
Question: How would you optimize API calls to improve performance and reduce
unnecessary network requests?
Answer:
To optimize API calls in a dashboard application, I would implement a strategy that includes
batching requests, caching responses, and utilizing debouncing techniques for input
changes. Batching allows me to combine multiple API requests into a single call whenever
possible.
Caching responses using local storage or in-memory data structures would prevent
redundant calls for frequently requested data. Additionally, I would debounce the input fields
to delay the API call until the user has stopped typing for a specified duration, reducing the
number of requests sent to the server.
For Example:
Here’s how you might implement debouncing for API calls in a search feature:
let timeout;
function fetchData(query) {
// Assume fetchDataFromAPI is a function that fetches data based on the query
fetchDataFromAPI(query).then(data => {
console.log(data);
});
}
document.getElementById('searchInput').addEventListener('input', handleSearch);
In this example, the API call is delayed until the user stops typing, optimizing network usage
and improving performance.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
602
You need to implement infinite scrolling on a webpage that loads additional content as the
user scrolls down.
Question: How would you set up infinite scrolling using JavaScript, and what considerations
should you have for performance?
Answer:
To implement infinite scrolling, I would listen for the scroll event on the window and check if
the user has scrolled near the bottom of the page. When the bottom is reached, I would fetch
additional data from the server and append it to the existing content.
Performance considerations include debouncing the scroll event listener to limit the
frequency of function calls and handling loading states to prevent multiple simultaneous
requests.
For Example:
Here’s how you might implement infinite scrolling:
let currentPage = 1;
const loading = false;
currentPage++;
loading = false;
}
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
603
In this example, additional items are loaded as the user scrolls down, enhancing the user
experience by seamlessly loading more content.
You are developing a React component that fetches data and updates the document title
based on the data retrieved. You need to ensure these side effects are managed properly.
Question: How would you handle multiple side effects in a React component using hooks?
Answer:
To manage multiple side effects in a React component, I would use the useEffect hook to
perform the necessary actions such as data fetching and updating the document title. By
structuring the useEffect calls appropriately, I can ensure that each side effect is managed
independently.
I would also make sure to handle cleanup if necessary, especially when working with
subscriptions or event listeners, to prevent memory leaks.
For Example:
Here’s how to implement this:
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
};
fetchData();
}, []); // Empty dependency array to run once on mount
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
604
useEffect(() => {
if (data) {
document.title = data.title; // Update document title based on data
}
}, [data]); // Run whenever data changes
In this example, two separate useEffect hooks manage fetching data and updating the
document title, ensuring clear and effective side effect management.
You are tasked with implementing a notification system that displays alerts to users based
on certain actions within the application.
Question: How would you design and implement this notification system in JavaScript?
Answer:
To build a notification system, I would create a central notification manager that maintains
the state of notifications. This manager would provide methods for adding and removing
notifications, as well as rendering them in the UI.
For Example:
Here’s a simple implementation of a notification system:
function addNotification(message) {
notifications.push(message);
renderNotifications();
}
function removeNotification(index) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
605
notifications.splice(index, 1);
renderNotifications();
}
function renderNotifications() {
const notificationContainer = document.getElementById('notificationContainer');
notificationContainer.innerHTML = notifications.map((msg, index) =>
`<div class="notification">${msg} <button
onclick="removeNotification(${index})">Dismiss</button></div>`
).join('');
}
// Usage
addNotification('You have a new message!');
In this example, notifications are managed through an array, and the UI is updated to display
the current notifications. Users can dismiss notifications with a button.
You have a web application that requires heavy computations without blocking the UI
thread.
Question: How would you utilize Web Workers to handle background processing in your
application?
Answer:
To use Web Workers for background processing, I would create a separate JavaScript file for
the worker and initialize it in the main application file. Web Workers run in a separate thread,
allowing for heavy computations to be performed without freezing the UI.
I would communicate with the worker using the postMessage method to send data and
listen for results with the onmessage event handler.
For Example:
Here’s how to set up a Web Worker:
worker.js
self.onmessage = function(event) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
606
function heavyComputation(data) {
// Perform heavy computation
return data * 2; // Example computation
}
main.js
worker.onmessage = function(event) {
console.log('Result from worker:', event.data);
};
In this example, heavy computations are offloaded to a Web Worker, improving the
performance and responsiveness of the main application.
You need to implement a drag-and-drop feature in your web application that allows users to
rearrange items in a list.
Question: How would you implement this drag-and-drop functionality using JavaScript?
Answer:
To implement drag-and-drop functionality, I would use the HTML5 Drag and Drop API. I
would attach event listeners for drag events (such as dragstart, dragover, and drop) to the
list items.
During the drag operation, I would maintain the state of the item being dragged and update
the list order upon dropping the item in a new position.
For Example:
Here’s a basic implementation of drag-and-drop:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
607
<ul id="draggableList">
<li draggable="true">Item 1</li>
<li draggable="true">Item 2</li>
<li draggable="true">Item 3</li>
</ul>
<script>
const listItems = document.querySelectorAll('#draggableList li');
let draggedItem;
listItems.forEach(item => {
item.addEventListener('dragstart', () => {
draggedItem = item;
});
item.addEventListener('drop', () => {
if (item !== draggedItem) {
const list = document.getElementById('draggableList');
const draggedIndex = Array.from(listItems).indexOf(draggedItem);
const targetIndex = Array.from(listItems).indexOf(item);
In this example, users can drag and drop items in a list, and the order is updated accordingly.
You are tasked with allowing users to toggle between light and dark themes in your
application.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
608
Question: How would you implement theming using CSS variables in JavaScript?
Answer:
To implement theming using CSS variables, I would define the color schemes for both light
and dark themes in the CSS. Upon toggling the theme, I would update the CSS variables in
the :root selector through JavaScript.
This approach allows for dynamic theme changes without reloading the page, providing a
smooth user experience.
For Example:
Here’s how this could be implemented:
styles.css
:root {
--background-color: white;
--text-color: black;
}
body {
background-color: var(--background-color);
color: var(--text-color);
}
.dark-theme {
--background-color: black;
--text-color: white;
}
script.js
document.getElementById('themeToggle').addEventListener('click', () => {
document.body.classList.toggle('dark-theme');
});
In this example, toggling the theme class on the body element updates the CSS variables,
changing the appearance of the application.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
609
You are building a product review system where users can rate products using stars.
Question: How would you implement this rating system using JavaScript?
Answer:
To implement a rating system using stars, I would create an interactive UI with star icons that
users can click on to select their rating. I would use event listeners to handle clicks and
update the state based on the user's selection.
The selected rating would be stored in a variable, and I would visually indicate the current
rating by filling the stars.
For Example:
Here’s how to set up a simple rating system:
<div id="rating">
<span class="star" data-value="1">★</span>
<span class="star" data-value="2">★</span>
<span class="star" data-value="3">★</span>
<span class="star" data-value="4">★</span>
<span class="star" data-value="5">★</span>
</div>
<script>
const stars = document.querySelectorAll('.star');
stars.forEach(star => {
star.addEventListener('click', () => {
const rating = star.getAttribute('data-value');
stars.forEach(s => s.classList.remove('selected'));
for (let i = 0; i < rating; i++) {
stars[i].classList.add('selected'); // Fill stars based on rating
}
console.log(`User rated: ${rating} stars`);
});
});
</script>
<style>
.star {
cursor: pointer;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
610
font-size: 30px;
}
.star.selected {
color: gold; // Filled star color
}
</style>
In this example, clicking on a star updates the visual representation of the rating and logs the
user's choice.
You need to create a custom dropdown menu that enhances the standard HTML <select>
element with better styling and functionality.
Question: How would you implement a custom select dropdown using JavaScript?
Answer:
To create a custom select dropdown, I would use a combination of HTML, CSS, and
JavaScript. I would hide the standard <select> element and create a styled dropdown using
<div> and <ul> elements. Event listeners would handle opening and closing the dropdown
and selecting an item.
This approach provides greater flexibility for styling and behavior compared to native select
elements.
For Example:
Here’s how to implement a custom dropdown:
<div class="custom-select">
<div class="select-selected">Select an option</div>
<div class="select-items select-hide">
<div>Option 1</div>
<div>Option 2</div>
<div>Option 3</div>
</div>
</div>
<style>
.custom-select {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
611
position: relative;
display: inline-block;
}
.select-selected {
background-color: #f1f1f1;
cursor: pointer;
}
.select-items {
position: absolute;
background-color: white;
display: none;
border: 1px solid #ccc;
}
.select-items div {
padding: 8px;
cursor: pointer;
}
.select-items div:hover {
background-color: #ddd;
}
.select-hide {
display: none;
}
</style>
<script>
document.querySelector('.select-selected').addEventListener('click', function() {
this.nextElementSibling.classList.toggle('select-hide');
});
In this example, the custom dropdown menu allows users to select an option while providing
a more styled and interactive experience compared to standard dropdowns.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
612
You are tasked with developing a simple chat application where users can send and receive
messages in real-time.
Question: How would you implement this chat application using JavaScript and
WebSockets?
Answer:
To implement a simple chat application, I would use WebSockets to enable real-time
communication between the client and server. The server would handle incoming messages
and broadcast them to all connected clients.
On the client side, I would establish a WebSocket connection, handle sending messages, and
display received messages dynamically.
For Example:
Here’s a basic implementation using WebSockets:
wss.on('connection', ws => {
ws.on('message', message => {
// Broadcast the received message to all clients
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
});
socket.onmessage = function(event) {
const message = document.createElement('div');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
613
document.getElementById('sendButton').addEventListener('click', () => {
const input = document.getElementById('messageInput').value;
socket.send(input); // Send message to server
});
In this example, the WebSocket server broadcasts incoming messages to all connected
clients, enabling real-time chat functionality. The client-side code manages sending and
receiving messages, providing a simple chat experience.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
614
THEORETICAL QUESTIONS
1. What is Cross-Site Scripting (XSS) in JavaScript?
Answer:
Cross-Site Scripting (XSS) is a vulnerability that allows attackers to inject malicious scripts into
a trusted website, which are then executed in the browser of an unsuspecting user. This
exploit happens when a web application includes untrusted data without proper validation
or escaping. XSS allows attackers to steal session cookies, capture keystrokes, redirect users
to malicious sites, or perform actions on behalf of the victim. XSS is common in web
applications that dynamically generate HTML content from user inputs.
For Example:
In the example above, using innerHTML renders the malicious script, leading to an XSS
attack. Using textContent ensures that input is treated as text and not executable code.
Answer:
Cross-Site Request Forgery (CSRF) tricks an authenticated user into unknowingly executing
actions on a website where they are logged in. Since web browsers automatically send
cookies along with requests, an attacker can exploit this behavior to make unauthorized
requests, such as changing passwords, transferring money, or modifying user data.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
615
For instance, a CSRF attack could occur if a bank allows sensitive transactions through HTTP
POST requests without additional authentication or a CSRF token.
For Example:
In this example, using a CSRF token ensures the form submission is legitimate. The token
must be validated by the server before processing the request.
Answer:
SQL Injection occurs when an attacker injects malicious SQL code into a query, often through
input fields, compromising the database's security. While SQL Injection primarily affects
backend systems, JavaScript-based frontend applications interacting with backend APIs or
databases can expose vulnerabilities if user inputs are not properly validated.
SQL Injection can lead to unauthorized access, data leakage, or even full system compromise.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
616
Using parameterized queries ensures that user input is treated as data and not as part of the
SQL command, preventing SQL Injection attacks.
4. What is JWT (JSON Web Token), and how is it used for securing APIs?
Answer:
JWT (JSON Web Token) is a compact, URL-safe token used for securely transmitting
information between parties. JWTs contain three parts: a header, payload, and signature.
They are commonly used for API authentication and session management. JWTs can store
user information, and once verified, the server can grant access without querying the
database.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
617
JWTs are sent as part of the HTTP headers (e.g., Authorization: Bearer <token>). The
server can use the token to validate user identity without needing to manage sessions.
Answer:
OAuth 2.0 is an authorization framework that allows third-party applications to access user
resources on another service (e.g., Google, GitHub) without sharing the user’s credentials.
OAuth issues an access token after user authentication, which can be used to access the
protected APIs. This framework ensures secure delegation of access.
For Example:
With OAuth, users authenticate via a third party, and the token limits the application’s access
to only the authorized resources.
Answer:
The three primary types of XSS attacks are:
1. Stored XSS: The malicious script is stored on the server and executed when a user
views the page.
2. Reflected XSS: The attack is reflected off the server in the response, typically via a
query parameter.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
618
3. DOM-based XSS: The vulnerability lies in client-side JavaScript code that directly
manipulates the DOM based on user input.
For Example:
Stored and reflected XSS can be prevented by sanitizing input on both the server and client
sides.
Answer:
To prevent XSS attacks:
1. Sanitize inputs: Ensure all user inputs are validated and sanitized.
2. Escape outputs: Convert HTML characters to safe representations when displaying
user content.
3. Use Content Security Policy (CSP): Restrict the types of scripts allowed to run.
4. Avoid eval() and similar dangerous functions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
619
Using a CSP ensures that only scripts from trusted sources are executed.
Answer:
CSRF tokens prevent attackers from executing unauthorized actions by associating a unique,
unpredictable token with each session or request. The server validates the token with every
sensitive request to confirm it originated from the legitimate user.
For Example:
Tokens are checked on the server before processing, ensuring requests are authentic.
Answer:
OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0. It enables secure user
authentication and identity verification. While OAuth focuses on authorization, OIDC
provides authentication services, returning an ID token that contains user information in a
JWT format.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
620
OIDC is widely used in social login systems like Google and Facebook.
10. What are some best practices for securing JavaScript applications?
Answer:
Best practices for securing JavaScript applications include:
For Example:
Following these practices ensures your application remains resilient against common
vulnerabilities.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
621
11. What is the difference between stored XSS and reflected XSS?
Answer:
Stored XSS occurs when malicious scripts are saved on the server’s database or storage, such
as in comment sections, forums, or user profiles. Every time a user visits the affected page,
the script is executed in the victim's browser. This type of XSS is persistent and can affect
multiple users over time.
Reflected XSS is non-persistent and occurs when the server includes user-provided input
directly in its response. It typically happens when malicious code is embedded in a URL, and
the user is tricked into clicking it. The injected script is then executed in the user's browser
without being stored on the server.
For Example:
Stored XSS is more dangerous because it affects all visitors to the page, while reflected XSS
only works if the attacker tricks the user into visiting a specially crafted URL.
12. What is DOM-based XSS, and how does it differ from other types of XSS?
Answer:
DOM-based XSS occurs entirely on the client side, where the malicious payload changes the
DOM structure using JavaScript. It does not rely on the server's response to deliver the
payload. Instead, it takes advantage of insecure DOM manipulations in the browser. This
makes DOM-based XSS harder to detect because it does not involve server interactions, and
the vulnerability exists only in the frontend code.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
622
Unlike stored or reflected XSS, where the server sends the malicious payload, DOM-based
XSS exploits client-side logic.
For Example:
This example demonstrates how manipulating the DOM using innerHTML can open the door
for XSS attacks, while using textContent mitigates the risk.
Answer:
Content encoding ensures that user inputs are treated as plain text rather than executable
code. This encoding technique converts special HTML characters such as < and > into their
encoded equivalents (< and >). By encoding the content, browsers render the input as
text rather than interpreting it as code.
For Example:
// Usage
const userInput = "<script>alert('XSS');</script>";
document.getElementById("output").innerHTML = encodeHTML(userInput);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
623
By using content encoding, we ensure that even if a malicious script is entered, it will not
execute but instead be displayed as text.
14. How does using the eval() function introduce security risks?
Answer:
The eval() function evaluates a string as JavaScript code, making it very dangerous if used
improperly. If an attacker can inject code into the string passed to eval(), they can run
malicious scripts within the application's context, leading to severe security vulnerabilities,
such as stealing cookies, defacing the UI, or performing unintended actions.
For Example:
Avoid using eval() and opt for safer alternatives like JSON.parse() or template literals
whenever possible.
15. What is a Content Security Policy (CSP), and how does it help prevent
XSS?
Answer:
A Content Security Policy (CSP) is an HTTP response header that defines the allowed sources
for scripts, styles, images, and other resources on a web page. By limiting the origins from
which content can be loaded, CSP reduces the risk of XSS attacks by preventing the
execution of unauthorized scripts.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
624
For Example:
In this example, only scripts from the same origin (self) are allowed, and any inline or
external scripts from other sources will be blocked.
16. What are secure cookies, and how do they improve web security?
Answer:
A secure cookie is transmitted only over HTTPS, ensuring that sensitive data, such as session
tokens, is encrypted in transit. Additionally, setting the HttpOnly flag prevents the cookie
from being accessed via JavaScript, mitigating the risk of XSS attacks.
For Example:
This configuration ensures that the cookie can only be sent over HTTPS and is inaccessible via
JavaScript, providing enhanced security.
17. How does the SameSite attribute help prevent CSRF attacks?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
625
Answer:
The SameSite cookie attribute prevents cookies from being sent with cross-site requests.
This reduces the risk of CSRF attacks by ensuring that cookies are only sent with requests
originating from the same origin.
The Lax setting allows cookies with top-level navigations (e.g., clicking a link), while Strict
ensures cookies are sent only with requests from the same site, providing maximum security.
For Example:
With SameSite set to strict, the cookie will not be sent if the request originates from
another site, preventing CSRF attacks.
18. How does token-based authentication work, and how is it different from
session-based authentication?
Answer:
In token-based authentication, a server issues a token (e.g., JWT) to the client after
successful login. The client stores the token and sends it with each subsequent request in the
Authorization header. Since the server does not store session data, this approach is stateless.
Session-based authentication stores session data on the server, requiring the server to
maintain session state. Token-based authentication scales better for APIs and microservices
because it offloads state management to the client.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
626
});
Answer:
OAuth 2.0 is primarily an authorization protocol that allows applications to access resources
on behalf of a user without sharing their credentials. For example, OAuth enables a third-
party app to access your Google Calendar data with your permission.
OpenID Connect (OIDC) extends OAuth 2.0 to provide authentication services, allowing the
client to verify the user's identity and retrieve profile information in the form of a JWT token.
For Example:
20. What are JSON Web Tokens (JWTs) and how do they enhance security?
Answer:
JWTs (JSON Web Tokens) are compact, self-contained tokens used to securely transmit
information between two parties. JWTs consist of three parts: header, payload, and
signature. The payload contains claims about the user, while the signature ensures the
integrity of the token.
JWTs are commonly used for stateless authentication, meaning the server does not need to
store session data. Instead, the client sends the token with each request.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
627
JWTs ensure each request is validated, enhancing security while minimizing server-side
storage.
21. How can an attacker exploit JSONP for XSS, and how can you mitigate
it?
Answer:
JSONP (JSON with Padding) is a technique used to load cross-domain data by embedding it
in a <script> tag. It allows websites to bypass the same-origin policy, which restricts how
scripts from one origin can interact with resources from another. However, JSONP can
introduce vulnerabilities if the server does not properly control or sanitize the callback
function provided in the request.
Exploitation:
Attackers can modify the callback parameter in the URL to inject malicious scripts. When
the browser loads the <script> tag with this payload, the malicious code runs in the context
of the vulnerable site.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
628
alert({"data":"sensitive data"});
The browser executes the alert() function, showing that an attacker could run arbitrary
code.
Mitigation Strategies:
● Avoid using JSONP and adopt CORS policies for cross-origin requests.
● If JSONP must be used, validate the callback function name against a whitelist.
● Limit the domains allowed to access sensitive endpoints.
22. How does OAuth 2.0 handle token expiration and refresh tokens?
Answer:
OAuth 2.0 issues access tokens that have a short lifespan to reduce the risk of token leakage.
When an access token expires, the client application uses a refresh token to request a new
access token without requiring the user to log in again.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
629
The refresh token allows the user to stay authenticated without re-entering credentials,
enhancing user experience while maintaining security.
Answer:
Clickjacking is an attack where a malicious website overlays or embeds another site (e.g., a
banking portal) in an iframe. The victim interacts with the embedded site unknowingly,
leading to unintended actions such as money transfers or changing account settings.
Mitigation:
For Example:
Using both X-Frame-Options and CSP provides a robust defense against clickjacking.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
630
Answer:
Timing attacks exploit differences in the time taken to perform operations. For example, if a
system compares passwords character-by-character and returns results faster for correct
characters, an attacker can infer the correct password by measuring response times.
Mitigation:
Implement constant-time comparison to ensure that the operation takes the same time
regardless of the input values.
For Example:
In this example, even if some characters match, the comparison takes the same time,
mitigating timing attacks.
25. What is Subresource Integrity (SRI), and how does it protect against
compromised scripts?
Answer:
Subresource Integrity (SRI) ensures that resources loaded from external sources (e.g., CDNs)
have not been tampered with. SRI works by including a cryptographic hash of the resource
in the <script> tag. If the content of the resource changes, the browser will block the script.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
631
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxvT9dRkIY="
crossorigin="anonymous"></script>
SRI ensures that only the expected version of the resource is loaded, protecting users from
compromised third-party libraries.
Answer:
API rate limiting prevents abuse by restricting the number of requests a client can make
within a certain time. If the limit is reached, the server responds with a 429 Too Many
Requests status and may include a Retry-After header indicating when the client can try
again.
For Example:
This implementation ensures the application handles rate limits gracefully by retrying the
request after the recommended time.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
632
27. What is HTTP Strict Transport Security (HSTS), and why is it important?
Answer:
HSTS ensures that browsers always connect to a site over HTTPS, even if the user tries to
access it using HTTP. This prevents protocol downgrade attacks and man-in-the-middle
(MITM) attacks where attackers intercept unencrypted traffic.
For Example:
The max-age attribute specifies how long (in seconds) the browser should enforce HTTPS, and
includeSubDomains ensures that all subdomains are also covered by HSTS.
28. How does Cross-Origin Resource Sharing (CORS) work, and how can it
be configured securely?
Answer:
CORS allows a server to specify which domains can access its resources. Without CORS,
browsers block cross-origin requests by default for security reasons. By configuring CORS
headers, the server can selectively allow or deny access to resources from specific origins.
For Example:
app.use(cors({
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
633
This setup ensures that only trusted origins can access the server’s resources.
Answer:
In symmetric encryption, the same key is used for both encryption and decryption. It is fast
but requires secure key management. In asymmetric encryption, a public key encrypts the
data, and a private key decrypts it. Asymmetric encryption is slower but more secure for
exchanging keys.
For Example:
function encrypt(text) {
const cipher = crypto.createCipheriv(algorithm, key, iv);
return cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
}
console.log(encrypt('Sensitive data'));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
634
Answer:
Hashing is a one-way operation that converts data into a fixed-size hash value. It is primarily
used for storing passwords securely and verifying data integrity. JavaScript provides hashing
functions through the crypto module.
For Example:
Hashing ensures that even if the database is compromised, the original data (e.g., passwords)
cannot be easily recovered from the hashes.
31. How do CSRF attacks differ from XSS attacks, and how can both be
mitigated?
Answer:
CSRF (Cross-Site Request Forgery) and XSS (Cross-Site Scripting) differ in the way they
compromise a system:
Mitigation Techniques:
● CSRF Mitigation:
1. CSRF Tokens: Unique tokens embedded in forms and validated by the server
on submission.
2. SameSite Cookies: Restrict sending cookies only on same-origin requests.
3. Referer/Origin Header Validation: Ensure requests originate from trusted
domains.
● XSS Mitigation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
635
Answer:
localStorage and sessionStorage are browser-based storage mechanisms that allow
developers to store key-value pairs. However, both are vulnerable to XSS attacks since
malicious scripts running on the page can access them.
● localStorage: Persistent storage that remains available even after the browser is
closed.
● sessionStorage: Data that is available only within the current session and is cleared
when the browser tab is closed.
Security Risks:
● XSS Vulnerability: Malicious scripts can access and exfiltrate stored tokens.
● Sensitive Data Exposure: Storing authentication tokens in localStorage or
sessionStorage may expose users to security risks.
Mitigation Strategies:
For Example:
// Setting sessionStorage
sessionStorage.setItem('user', JSON.stringify({ username: 'Alice' }));
// Retrieving sessionStorage
const user = JSON.parse(sessionStorage.getItem('user'));
console.log(user.username); // "Alice"
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
636
33. What is key stretching, and why is it important for password hashing?
Answer:
Key stretching strengthens passwords by applying a hashing function multiple times to slow
down brute-force attempts. Without key stretching, weak passwords could be cracked
quickly by attackers using powerful hardware. Algorithms like PBKDF2, bcrypt, and argon2
implement key stretching.
How It Works:
For Example:
Answer:
Rate-limiting controls how frequently a user or an IP can attempt login within a given
timeframe. This is crucial for preventing brute-force attacks, where attackers attempt to
guess passwords by rapidly submitting login requests.
● After a certain number of failed attempts, the server temporarily blocks further
attempts.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
637
● The server may respond with a 429 Too Many Requests status or implement a
backoff strategy.
For Example:
35. How can you securely store JWTs in a client-side JavaScript application?
Answer:
JWTs (JSON Web Tokens) are used to authenticate users and authorize access to resources.
Storing JWTs insecurely can expose users to XSS attacks, where attackers can steal tokens
and hijack user sessions.
1. HttpOnly Cookies: Store JWTs in cookies that cannot be accessed via JavaScript.
2. Secure and SameSite Cookies: Prevent cross-site usage and transmission over non-
HTTPS connections.
3. Token Rotation: Issue new tokens frequently to minimize the risk of token theft.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
638
secure: true,
sameSite: 'strict'
});
next();
});
Answer:
● Encryption: A reversible process that converts plaintext into ciphertext using a key.
With the correct key, the data can be decrypted back to its original form. Encryption
ensures data confidentiality.
● Hashing: A one-way operation that converts data into a fixed-length hash. Hashes are
used to verify data integrity and securely store passwords, as they cannot be reversed.
function encrypt(text) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
return cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
}
console.log(encrypt('Sensitive Data'));
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
639
Answer:
WebSockets provide a persistent, two-way communication channel between a client and
server. However, WebSocket connections can be vulnerable to man-in-the-middle (MITM)
attacks and cross-site WebSocket hijacking.
Mitigation Strategies:
For Example:
38. What is HTTP/2, and how does it improve security and performance?
Answer:
HTTP/2 improves upon HTTP/1.1 by introducing features like multiplexing, header
compression, and server push, all of which enhance performance. HTTP/2 also requires TLS
encryption, which makes it more secure by default.
Benefits:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
640
39. How do CSP violations get reported, and how can you use them for
security monitoring?
Answer:
A Content Security Policy (CSP) can be configured to report violations to a monitoring
endpoint. This allows administrators to detect unauthorized scripts and assess potential XSS
vulnerabilities.
For Example:
Answer:
MFA (Multi-Factor Authentication) adds an extra layer of security by requiring users to
provide an additional verification factor, such as a one-time password (OTP) or a push
notification from an authenticator app.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
641
};
MFA ensures that even if a password is compromised, the attacker cannot log in without the
additional factor.
SCENARIO QUESTIONS
41. Scenario: A user submits a malicious script in a comment box on a social
media platform, which executes when other users view the comment.
Question: How can the platform prevent such Cross-Site Scripting (XSS) attacks?
Answer:
This is a Stored XSS attack. The attacker injects malicious scripts into user-generated content
stored on the server. When another user loads the page, the script runs in the context of the
victim’s browser, potentially stealing session cookies or personal information.
To mitigate this:
1. Sanitize user inputs by removing or escaping dangerous characters like <, >, and "
from user submissions.
2. Use content encoding to convert HTML-sensitive characters into safe text.
3. Implement Content Security Policy (CSP) to restrict resource loading.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
642
By sanitizing the input, the platform ensures that scripts embedded within comments are
rendered as plain text.
Answer:
This is a CSRF attack, where the attacker leverages the user’s authenticated session to
perform unauthorized actions. This usually happens when session cookies are sent with all
requests.
To prevent CSRF:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
643
43. Scenario: An attacker inputs SQL queries into a search field to extract
sensitive data from the database.
Answer:
SQL Injection occurs when untrusted user inputs are directly embedded into SQL queries,
allowing attackers to manipulate the database. This can lead to unauthorized data access or
deletion.
For Example:
Question: How can JWT (JSON Web Token) be used to secure API endpoints?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
644
Answer:
JWT is commonly used to authenticate users and authorize access to resources. After login,
the server issues a JWT, which the client stores and sends with subsequent requests.
For Example:
Answer:
OAuth 2.0 is an authorization framework that allows a client to access user resources on
another service without sharing credentials. It works by issuing access tokens.
Steps:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
645
For Example:
46. Scenario: A web application needs to verify a user's identity and retrieve
profile information from a third-party service.
Answer:
OpenID Connect (OIDC) is an authentication layer on top of OAuth 2.0. It allows clients to
verify a user’s identity by receiving an ID token in addition to the access token.
Steps:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
646
47. Scenario: A developer must restrict which external domains can access
their API to prevent unauthorized cross-origin requests.
Answer:
CORS allows servers to specify which origins can access their resources. Without CORS,
browsers block cross-origin requests by default.
For Example:
Answer:
HSTS ensures that browsers only connect to the server over HTTPS, even if the user types
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
647
http:// in the address bar. This prevents man-in-the-middle (MITM) attacks and protocol
downgrades.
For Example:
Question: How can CSP (Content Security Policy) reports help detect potential
attacks?
Answer:
CSP can be configured to send violation reports whenever unauthorized scripts or resources
are blocked. This helps in detecting XSS attacks and other suspicious activities.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
648
Answer:
Rate-limiting helps prevent brute-force attacks by restricting the number of login attempts
allowed from a single IP or user within a specific time window.
For Example:
51. Scenario: A user enters input into a search field, and the application
dynamically updates the page content using innerHTML.
Question: How can this approach lead to a security issue, and what are safer
alternatives?
Answer:
Using innerHTML can introduce DOM-based XSS if user input is directly injected into the
DOM without validation. If an attacker provides a malicious script as input, the script will
execute in the user's browser, potentially stealing sensitive data or hijacking the session.
Safer Alternatives:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
649
For Example:
Answer:
Storing JWTs in localStorage or sessionStorage exposes them to XSS attacks since
malicious scripts running on the site can access these storage mechanisms. If a token is
stolen, an attacker can use it to impersonate the user.
Best Practices:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
650
Answer:
Improper input validation can lead to security issues like SQL Injection, where attackers can
manipulate database queries. This occurs when input is directly embedded into SQL queries
without sanitization, allowing attackers to gain unauthorized access to the database.
Solution: Use parameterized queries or ORMs to ensure that inputs are treated as data, not
code.
For Example:
54. Scenario: A web application handles sensitive user data and needs
secure communication.
Answer:
HTTPS encrypts data transmitted between the client and server, preventing man-in-the-
middle (MITM) attacks. Without HTTPS, attackers can intercept data, including passwords
and personal information, sent over the network.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
651
55. Scenario: A developer needs to ensure that only authorized users can
access specific routes in an API.
Answer:
Middleware can intercept requests and verify JWTs or other authentication tokens before
allowing access to protected routes. If the token is missing or invalid, the middleware can
block the request.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
652
Question: What security risks are involved, and how can they be mitigated?
Answer:
Allowing users to upload files can expose the application to malware and script injection
attacks if the files are not properly validated.
Mitigation:
For Example:
57. Scenario: A user logs out of an application but their session cookie
remains valid.
Answer:
To ensure security, session cookies must be invalidated upon logout to prevent session
hijacking. This can be achieved by deleting the session data on the server and clearing the
cookie.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
653
58. Scenario: A developer needs to ensure that cookies are only sent over
secure connections.
Answer:
Secure cookies are transmitted only over HTTPS connections, ensuring that session data is
not exposed to attackers monitoring the network.
For Example:
59. Scenario: An attacker tries to overload the API with a high volume of
requests.
Answer:
Rate limiting controls how many requests a client can make in a given period, protecting
against DDoS attacks and abuse. It temporarily blocks clients that exceed the allowed
threshold.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
654
app.use('/api/', apiLimiter);
Question: How can CSP (Content Security Policy) reports be used for monitoring?
Answer:
A Content Security Policy (CSP) can be configured to send reports whenever unauthorized
scripts or resources are blocked. This helps detect XSS attacks and other violations early.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
655
61. Scenario: A user logs into a single-page application (SPA) and the
developer needs to ensure that authentication persists across page reloads
without compromising security.
Question: What are the best practices for securely storing authentication tokens
in a SPA?
Answer:
In SPAs, tokens can be stored either in localStorage, sessionStorage, or HttpOnly cookies.
However, storing them in localStorage or sessionStorage is risky due to XSS
vulnerabilities. Using HttpOnly cookies is the safest way since they are inaccessible to
JavaScript.
Best Practices:
For Example:
Question: How can JavaScript ensure data is securely encrypted before storage?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
656
Answer:
To ensure data security:
1. Encrypt data at rest: Use AES or RSA encryption before storing it in a database or
local file.
2. Encrypt data in transit: Use HTTPS for communication between the client and server.
3. Use TLS: Establish encrypted connections to protect against eavesdropping.
For Example:
// AES-256 encryption
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
function encrypt(text) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
return cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
}
console.log(encrypt('Sensitive Data'));
Question: How can the application securely handle token expiration and refresh
tokens?
Answer:
Access tokens typically have short lifespans to mitigate misuse. Refresh tokens allow
applications to obtain new access tokens without requiring user re-authentication. However,
refresh tokens must be stored securely.
Best Practices:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
657
For Example:
64. Scenario: A web application handles large amounts of traffic, and the
developer needs to prevent session fixation attacks.
Answer:
Session fixation occurs when an attacker forces a user to log in with a known session ID.
Once the user logs in, the attacker uses the same session to impersonate them.
Mitigation Strategies:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
658
});
Answer:
Exponential backoff increases the delay between login attempts after each failed attempt,
reducing the effectiveness of brute-force attacks. With each failure, the wait time doubles.
For Example:
let attempts = 0;
setTimeout(() => {
const isAuthenticated = authenticate(req.body);
if (isAuthenticated) {
attempts = 0;
res.send('Login successful');
} else {
attempts++;
res.status(401).send('Login failed');
}
}, waitTime);
});
Question: How can the application ensure that it is not embedded in iframes?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
659
Answer:
Clickjacking occurs when an attacker embeds a legitimate site in an invisible iframe to trick
users into performing unintended actions.
Mitigation:
For Example:
// CSP approach
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
next();
});
Answer:
Audit logs provide a record of security-related events, such as login attempts and
unauthorized resource access. Proper logging helps in incident detection and response.
For Example:
const fs = require('fs');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
660
Answer:
A strong password policy requires users to create passwords with a mix of uppercase and
lowercase letters, numbers, and special characters. It can be enforced using regular
expressions.
For Example:
console.log(isValidPassword('StrongP@ssw0rd')); // true
console.log(isValidPassword('weakpassword')); // false
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
661
Answer:
2FA requires a second factor, such as an OTP (One-Time Password) sent via SMS, in addition
to the user’s password.
For Example:
Answer:
Token rotation ensures that tokens are frequently replaced with new ones, reducing the risk
of token misuse if a token is stolen. When a new token is issued, the old token becomes
invalid.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
662
Answer:
CORS (Cross-Origin Resource Sharing) allows a server to specify which domains can access
its resources. Misconfiguring CORS can expose APIs to unauthorized access, leading to
security risks.
Best Practices:
For Example:
const corsOptions = {
origin: 'https://trusted-site.com',
methods: ['GET', 'POST'], // Restrict methods
credentials: true, // Use only if necessary
};
app.use(cors(corsOptions));
This configuration allows only requests from a specific origin and limits the HTTP methods to
GET and POST.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
663
Answer:
OAuth provides a way for users to grant third-party applications access to their resources
without sharing credentials. The Authorization Code Flow is the most secure OAuth flow for
server-side applications.
Steps:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
664
Answer:
Account lockout temporarily disables an account after multiple failed login attempts,
mitigating brute-force attacks.
Implementation Steps:
For Example:
userAttempts[username].attempts = 0;
res.send('Login successful');
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
665
Answer:
Exposing API keys in frontend code can lead to unauthorized usage. To secure API keys:
1. Use backend proxy servers to make API calls instead of directly from the frontend.
2. Restrict API keys by origin, IP, or usage limits.
3. Rotate keys periodically to limit the damage if a key is compromised.
For Example:
Answer:
Access control ensures that users can only access resources they are authorized for. Role-
based access control (RBAC) is a common approach where users are assigned roles that
define their permissions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
666
};
Here, only users with the admin role can access the /admin route.
Answer:
Input validation ensures that only expected data is accepted by the application, mitigating
attacks such as SQL Injection, XSS, and Command Injection.
Best Practices:
For Example:
Answer:
Short-lived tokens limit the time an attacker can misuse a stolen token. After expiration,
users must request a new token using a refresh token.
For Example:
Answer:
Anomaly detection identifies unusual behavior, such as sudden changes in login location or
high-frequency requests, which could indicate an attack.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
668
}
};
Answer:
WebSocket hijacking occurs when an attacker intercepts or takes over a WebSocket
connection.
Mitigation Strategies:
For Example:
80. Scenario: A user logs into multiple devices, and the developer needs to
manage sessions efficiently.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
669
Answer:
Managing sessions across devices ensures consistent user experience and security. Token-
based sessions are recommended to handle multi-device login efficiently.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
670
THEORETICAL QUESTIONS
1. What is the role of JavaScript in DevOps?
Answer:
JavaScript plays a crucial role in DevOps, particularly through Node.js, as it allows developers
to create automation tools, manage configurations, and streamline CI/CD pipelines. With
JavaScript, teams can automate repetitive tasks, build monitoring systems, and handle
deployments efficiently.
For Example:
A Node.js script can automate the deployment of an application by triggering shell
commands or interacting with APIs to deploy code:
exec('git pull origin main && npm install && npm run build', (err, stdout, stderr)
=> {
if (err) {
console.error(`Error: ${stderr}`);
return;
}
console.log(`Deployment Success: ${stdout}`);
});
Answer:
CI/CD pipelines rely heavily on automation, and Node.js is ideal for writing scripts that
integrate with CI tools like Jenkins, GitHub Actions, and GitLab CI. Node.js scripts can
automate tasks such as running tests, building projects, and deploying code. The
asynchronous, non-blocking nature of Node.js ensures fast execution of multiple tasks.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
671
For Example:
The following Node.js script runs unit tests using the child_process module during the CI
phase:
3. What is a Command Line Interface (CLI), and how can you build one
using JavaScript?
Answer:
A Command Line Interface (CLI) allows users to interact with applications by typing
commands in the terminal. Using Node.js, developers can build lightweight CLIs for various
tasks like automating builds, deploying applications, or generating files.
For Example:
The following code creates a simple CLI tool using commander.js:
program
.version('1.0.0')
.description('A simple CLI example')
.option('-n, --name <type>', 'Enter your name')
.action((options) => {
console.log(`Hello, ${options.name}!`);
});
program.parse(process.argv);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
672
Answer:
JavaScript build tools like Webpack, Rollup, and Parcel are essential in DevOps for bundling,
optimizing, and packaging code. These tools help minimize file sizes, enable code splitting,
and support modular development.
For Example:
Here is a basic Webpack configuration:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
Answer:
npm (Node Package Manager) is used to manage dependencies in JavaScript projects and
plays a key role in DevOps automation. With npm, developers can script tasks (like builds,
tests, and deployments) and manage versions of dependencies efficiently. It integrates
seamlessly with CI/CD pipelines to automate workflows.
For Example:
The following package.json file includes an automated test script:
{
"name": "devops-project",
"version": "1.0.0",
"scripts": {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
673
Answer:
npm, Yarn, and pnpm are package managers used to install, update, and manage project
dependencies.
● npm: Default package manager for Node.js, known for easy configuration.
● Yarn: Focuses on speed and stability, offering better caching than npm.
● pnpm: Uses symlinks to store packages efficiently, saving space and time.
For Example:
Install a package using:
Answer:
Webpack helps optimize JavaScript projects by bundling modules, enabling faster build and
deployment processes in CI/CD pipelines. It supports tree-shaking to remove unused code,
which reduces the bundle size and improves performance.
For Example:
A Webpack setup for production mode can be configured as:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
674
module.exports = {
mode: 'production',
entry: './src/app.js',
output: {
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
Answer:
Rollup is a build tool focused on creating efficient JavaScript bundles, especially for libraries.
It uses ES6 modules and supports tree-shaking, ensuring only the necessary code is bundled.
Rollup is widely used to generate smaller, optimized builds.
For Example:
Here is a basic Rollup configuration:
export default {
input: 'src/index.js',
output: {
file: 'bundle.js',
format: 'cjs',
},
plugins: [resolve()],
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
675
Answer:
Parcel offers a zero-configuration setup that simplifies JavaScript build processes. It
automatically detects dependencies and optimizes code without requiring complex
configuration files. This makes it ideal for small to medium-sized projects.
For Example:
Run the following command to start a Parcel project:
parcel index.
Answer:
JavaScript, with the help of Node.js, can automate repetitive tasks like file management,
monitoring, and deployment. Using scripting, developers can build tools that perform
operations such as cleaning directories, generating reports, or syncing files between servers.
For Example:
Here is a Node.js script that deletes log files older than 7 days:
const fs = require('fs');
const path = require('path');
files.forEach((file) => {
const filePath = path.join(logDir, file);
const stats = fs.statSync(filePath);
const now = new Date().getTime();
const endTime = new Date(stats.mtime).getTime() + 7 * 24 * 60 * 60 * 1000;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
676
Answer:
Node.js supports task scheduling using libraries such as node-cron or agenda. These libraries
allow developers to automate tasks by running scripts at specific intervals, such as daily,
weekly, or monthly. Automated tasks are essential in DevOps for regular maintenance
activities like backups, log cleanup, or sending reports. node-cron offers a cron-like syntax to
specify the frequency, while agenda is useful for more complex job management and
integrates well with MongoDB.
For Example:
The following example uses node-cron to print a message every minute:
In the cron syntax, the asterisk (*) represents "every" for each unit of time (minute, hour, day,
etc.). This script runs indefinitely until stopped, useful for tasks like monitoring services.
12. What are npm scripts, and how are they useful for automation?
Answer:
npm scripts are commands defined in the package.json file to automate common tasks like
running tests, building projects, or starting servers. They simplify repetitive tasks and can
trigger multiple processes in sequence. Using npm scripts, developers can automate
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
677
workflows within CI/CD pipelines. These scripts are triggered with npm run <script_name>
and can call Node.js commands, shell commands, or other scripts.
For Example:
A sample package.json with npm scripts might look like this:
{
"scripts": {
"start": "node app.js",
"build": "webpack --mode production",
"test": "jest"
}
}
Answer:
Node.js can execute shell commands through the child_process module. This is especially
useful in DevOps for performing tasks like deploying code, starting/stopping servers, and
automating database backups. It allows JavaScript scripts to run shell commands and
process their outputs. You can use exec for simple commands or spawn for long-running
processes.
For Example:
Here is a script that lists all files in the current directory:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
678
This script runs the ls command and prints the list of files.
Answer:
Yarn is a package manager that enhances dependency management with faster installations
and a deterministic approach. It uses parallel installations and caching to improve speed.
Yarn ensures consistency across environments by generating a yarn.lock file, which locks
the exact versions of dependencies. This helps in maintaining the same behavior in
development, testing, and production.
For Example:
Here’s how to install and manage dependencies with Yarn:
Yarn’s offline cache allows installing packages even when disconnected from the internet.
15. What is the role of CI/CD pipelines in DevOps, and how does JavaScript
help?
Answer:
CI/CD pipelines automate the process of integrating code changes (CI) and deploying
applications (CD). JavaScript, particularly with Node.js, helps automate testing, code analysis,
and deployment in these pipelines. Scripts written in JavaScript can validate code, run tests,
and deploy builds automatically, ensuring quicker feedback loops and faster releases.
For Example:
A Node.js script for linting code during CI might look like this:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
679
This script will exit with an error if linting fails, ensuring only clean code is deployed.
Answer:
Webpack and Parcel are both JavaScript bundlers, but they differ in complexity and usage.
● Webpack: Best suited for large projects, offering extensive customization through
plugins and configuration. It requires a webpack.config.js file but provides
advanced features like tree-shaking, code splitting, and hot module replacement.
● Parcel: A zero-configuration bundler that works out of the box. It detects
dependencies automatically and is ideal for smaller projects or quick prototypes.
For Example:
Using Parcel for a quick build:
Answer:
A build tool automates tasks such as bundling, minifying, and transpiling code. It transforms
source code into optimized output suitable for deployment. Build tools like Webpack and
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
680
Parcel play a crucial role in improving application performance and ensuring that code is
ready for production.
For Example:
Running Webpack to bundle a project:
18. What are package.json and lock files, and how do they help in DevOps?
Answer:
The package.json file contains information about the project, including dependencies,
scripts, and metadata. Lock files, such as package-lock.json or yarn.lock, store the exact
versions of dependencies installed. This ensures that all environments (development, testing,
and production) use the same versions, preventing issues caused by version mismatches.
For Example:
Here’s a simple package.json:
{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1"
}
}
19. What are some best practices when writing JavaScript automation
scripts?
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
681
Answer:
When writing JavaScript automation scripts:
For Example:
Reading environment variables securely:
require('dotenv').config();
20. How does pnpm differ from npm and Yarn in handling dependencies?
Answer:
pnpm manages dependencies differently by using a global store with symlinks to save disk
space and reduce duplication. Unlike npm and Yarn, pnpm ensures that a single version of a
package is shared across multiple projects, making installation faster and more efficient. This
is particularly useful for monorepos and large applications.
For Example:
To add a dependency using pnpm:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
682
This command installs the package and links it, reducing duplication across projects.
Answer:
JavaScript, particularly with Node.js, plays a critical role in serverless automation by enabling
event-driven applications without needing dedicated infrastructure. Platforms such as AWS
Lambda, Azure Functions, and Google Cloud Functions support JavaScript functions that
execute in response to triggers like HTTP requests, database events, or file uploads. This
serverless model is highly cost-effective as it charges based only on function execution,
scaling automatically with demand.
For Example:
Here’s a sample AWS Lambda function written in JavaScript to send a notification:
This function runs only when triggered, making it ideal for automating workflows like file
processing or sending alerts based on events.
Answer:
JavaScript can automate cloud tasks using SDKs like aws-sdk for AWS and azure-sdk-for-
js for Azure. DevOps engineers use these libraries to script the deployment of cloud
resources, manage configurations, and automate scaling operations. Automating
infrastructure ensures consistency, reduces manual effort, and speeds up deployment
processes.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
683
For Example:
This Node.js script creates an S3 bucket using the AWS SDK:
This script creates cloud storage, demonstrating how JavaScript simplifies infrastructure
management.
Answer:
Tree-shaking is an optimization process that removes unused JavaScript code from bundles
during the build process. Webpack leverages static analysis of ES6 modules to identify and
exclude unnecessary code. This reduces bundle size, improving performance by ensuring
that only the required code is delivered to users.
For Example:
Consider the following code:
// utils.js
export const usedFunction = () => console.log('Used');
export const unusedFunction = () => console.log('Unused');
// index.js
import { usedFunction } from './utils';
usedFunction();
During the build, Webpack excludes unusedFunction() from the output bundle.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
684
24. What are some best practices for writing JavaScript-based CLIs for
automation?
Answer:
When building JavaScript CLIs, it’s important to:
1. Follow Unix conventions: Return appropriate exit codes to signal success or failure.
2. Use established libraries: Use commander for handling command-line arguments or
inquirer for interactive prompts.
3. Provide helpful error messages: This makes troubleshooting easier.
4. Implement testing: Ensure command logic works under different scenarios.
For Example:
A simple CLI using commander:
program
.version('1.0.0')
.description('Sample CLI Tool')
.option('-n, --name <name>', 'Enter your name')
.action((options) => {
console.log(`Hello, ${options.name}!`);
});
program.parse(process.argv);
This example creates a CLI that accepts a name and prints a greeting.
Answer:
Monitoring tools built with JavaScript and Node.js track metrics like CPU usage, memory
consumption, and application health. These tools can log data and integrate with services
like Prometheus or Grafana to create dashboards and send alerts. JavaScript's event-driven
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
685
model is well-suited for real-time monitoring, making it a good choice for tracking system
performance.
For Example:
A simple Node.js script that logs memory usage:
setInterval(() => {
const memoryUsage = process.memoryUsage();
console.log(`Memory Usage: ${JSON.stringify(memoryUsage)}`);
}, 5000);
This script logs memory statistics every 5 seconds, helping detect memory leaks.
26. What is hot module replacement (HMR), and how does it help in
DevOps?
Answer:
Hot Module Replacement (HMR) allows developers to update modules in a running
application without a full browser reload. It improves developer productivity by instantly
reflecting code changes, making it easier to test and debug. Webpack provides built-in HMR
support, which can be enabled in the configuration file.
For Example:
Enable HMR in Webpack:
module.exports = {
devServer: {
hot: true,
contentBase: './dist',
},
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
686
Answer:
JavaScript’s asynchronous nature enables task parallelization in CI/CD pipelines. Functions
like Promise.all() allow multiple independent tasks to run concurrently, reducing pipeline
execution time. This is especially useful for running tests, builds, or deployments
simultaneously.
For Example:
Running two tasks in parallel:
Promise.all([task1(), task2()])
.then(() => console.log('Both tasks completed.'));
28. How does JavaScript handle file system operations for automation?
Answer:
JavaScript, via the fs module in Node.js, can manage file systems by reading, writing, and
modifying files and directories. Automating file operations is common in DevOps for log
rotation, configuration updates, and backups.
For Example:
Create a log file with the following script:
const fs = require('fs');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
687
This example demonstrates how JavaScript can generate and manage files.
29. How can you implement error handling in JavaScript CI/CD automation
scripts?
Answer:
Error handling ensures that scripts do not fail silently and provides meaningful feedback to
developers. JavaScript offers try...catch blocks and error events to gracefully handle
exceptions. In CI/CD, proper error handling ensures failures are detected and logged for
troubleshooting.
For Example:
Here’s a script with error handling:
try {
const result = performCriticalTask();
console.log('Task completed successfully:', result);
} catch (error) {
console.error('Error encountered:', error.message);
process.exit(1); // Exit with failure status
}
This ensures that the pipeline reports errors instead of continuing silently.
Answer:
JavaScript package managers (npm, Yarn, pnpm) streamline dependency management by
automating installations and versioning. They ensure consistent environments by locking
dependency versions, which is essential for reproducible builds. Additionally, package
managers can run scripts for testing, building, and deploying code.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
688
For Example:
Here’s how npm can automate workflows:
This ensures dependencies are installed, tests are executed, and the project is built
automatically in CI/CD pipelines.
Answer:
JavaScript, particularly with libraries like AWS CDK (Cloud Development Kit) and Pulumi,
enables Infrastructure-as-Code (IaC) by allowing developers to define cloud resources
through code. IaC allows infrastructure configurations to be versioned, shared, and reused.
Using JavaScript-based IaC tools, DevOps teams can automate the provisioning and
management of infrastructure resources consistently across environments.
For Example:
Here’s how to create an AWS S3 bucket using the AWS CDK:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
689
32. How does JavaScript help with container automation using Docker?
Answer:
JavaScript, through Node.js scripts, can automate container management using Docker APIs
and shell commands. Automation tasks may include building, running, and stopping
containers, as well as deploying services in a containerized environment. This integration
simplifies DevOps workflows by streamlining container orchestration.
For Example:
A Node.js script to automate Docker container management:
This script builds and runs a Docker container for the application.
Answer:
While JavaScript is single-threaded by nature, Node.js supports multithreading through the
Worker Threads API. In DevOps, multithreading helps execute parallel tasks efficiently, such
as processing large datasets, file operations, or running multiple automation tasks
simultaneously.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
690
For Example:
Using worker threads for parallel execution:
34. How can JavaScript automate CI/CD pipelines with GitHub Actions?
Answer:
GitHub Actions allows developers to automate workflows directly from repositories.
JavaScript, used with Node.js and npm, is often leveraged in GitHub Actions to run tests,
build projects, and deploy applications automatically. Actions are defined in YAML files and
triggered by events like pull requests or merges.
For Example:
A GitHub Actions workflow for a Node.js project:
name: CI Pipeline
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
691
Answer:
JavaScript, along with tools like the AWS SDK, Azure CLI, or Firebase CLI, automates cloud
deployments by triggering updates in response to new code releases. This ensures seamless
continuous delivery (CD) by deploying new versions of the application without manual
intervention.
For Example:
Deploying an app using the Firebase CLI:
With JavaScript, you can integrate such commands into automation scripts to deploy
continuously as part of your CI/CD process.
Answer:
JavaScript can automate Kubernetes operations by interacting with Kubernetes APIs. Using
libraries like kubernetes-client, developers can manage pods, services, and deployments
programmatically. This is useful for automating scaling, deployments, and rollbacks in a
Kubernetes cluster.
For Example:
A Node.js script to list Kubernetes pods:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
692
k8sApi.listNamespacedPod('default').then((res) => {
console.log('Pods:', res.body.items);
});
Answer:
JavaScript-based tools like npm audit and Snyk detect vulnerabilities in dependencies and
automate security checks in CI/CD pipelines. By integrating these tools, DevOps teams can
ensure that applications remain secure throughout the development lifecycle.
For Example:
To run an automated security scan:
npm audit
This command checks for known vulnerabilities in dependencies and suggests fixes.
Answer:
Automating versioning ensures that every new build or release is correctly tagged. JavaScript
scripts can increment versions in the package.json file and commit the changes to version
control, ensuring consistent version management across releases.
For Example:
A script to automate version bumping:
const fs = require('fs');
const packageJson = require('./package.json');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
693
Answer:
Blue-green deployments minimize downtime during releases by maintaining two identical
environments—one live (blue) and one staged (green). JavaScript can automate switching
between environments using API calls or cloud management scripts, ensuring smooth
transitions.
For Example:
A Node.js script to update DNS records and switch environments:
const params = {
ChangeBatch: {
Changes: [{
Action: 'UPSERT',
ResourceRecordSet: {
Name: 'myapp.example.com',
Type: 'CNAME',
TTL: 300,
ResourceRecords: [{ Value: 'green-app.example.com' }],
},
}],
},
HostedZoneId: 'Z3M3LMPEXAMPLE',
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
694
40. How does JavaScript integrate with message queues for automation?
Answer:
Message queues like RabbitMQ, Kafka, or AWS SQS are essential for decoupling services in
distributed systems. JavaScript, using libraries such as amqplib or AWS SDK, can automate
the production and consumption of messages in these queues, enabling asynchronous
communication between microservices.
For Example:
A Node.js script to publish messages to RabbitMQ:
(async () => {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'task_queue';
console.log('Message sent');
setTimeout(() => connection.close(), 500);
})();
This script connects to RabbitMQ, sends a message, and closes the connection.
SCENARIO QUESTIONS
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
695
Question: How would you use Node.js to automate deployment in a CI/CD pipeline?
Answer:
In CI/CD pipelines, deployment tasks can be automated using Node.js to eliminate the need
for manual operations. This ensures that every release follows the same process, reducing
human errors. A Node.js deployment script integrates with tools like GitHub Actions,
Jenkins, or GitLab CI to fetch code from repositories, install dependencies, build the project,
and restart the application automatically. It also ensures that each step—like testing and
building—is executed in the right sequence.
For Example:
The following Node.js script pulls code, installs dependencies, builds the project, and restarts
the server:
exec('git pull origin main && npm install && npm run build && pm2 restart my-app',
(error, stdout, stderr) => {
if (error) {
console.error(`Error during deployment: ${stderr}`);
return;
}
console.log(`Deployment Successful: ${stdout}`);
});
This script automates code deployment and ensures that the server restarts with the latest
changes after each build.
Question: How can npm scripts be used to automate testing as part of the CI/CD process?
Answer:
npm scripts streamline the process of running tests by defining commands in the
package.json file. During CI/CD pipelines, these scripts can be triggered to execute unit
tests, integration tests, or end-to-end tests automatically. Ensuring that tests run on every
code change helps maintain code quality and reduces the chances of deploying faulty code.
This practice, known as shift-left testing, catches bugs early in the development lifecycle.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
696
For Example:
Here is a package.json file with a test script defined:
{
"scripts": {
"test": "mocha tests/**/*.js"
}
}
This ensures that the tests are executed in every build process to validate the changes before
deployment.
Question: How would you use Node.js to create a custom CLI tool for managing builds?
Answer:
A Command Line Interface (CLI) tool allows developers to perform common operations,
such as building or deploying applications, from the terminal. Using libraries like commander,
you can create a lightweight tool to abstract complex build tasks into simple commands. This
makes workflows faster and more efficient by minimizing human interaction with multiple
tools.
For Example:
Below is a simple CLI built with commander to manage builds:
program
.command('build')
.description('Build the project')
.action(() => {
console.log('Building the project...');
// Additional build logic here
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
697
program.parse(process.argv);
This triggers the build process with minimal effort from the developer.
Question: How can Webpack be used to optimize build size in a CI/CD environment?
Answer:
In a CI/CD environment, optimizing build size ensures that applications load faster and
provide better user experience. Webpack enables code splitting and tree-shaking to reduce
the size of JavaScript bundles by including only the necessary modules. In production builds,
Webpack minifies the code and removes unused imports, which results in faster
deployments and improved application performance.
For Example:
Here is a Webpack configuration that uses tree-shaking:
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
usedExports: true, // Enables tree-shaking
},
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
698
This configuration ensures only the used exports are included in the build.
Question: How would you automate dependency management using Yarn in a project?
Answer:
Yarn improves dependency management by providing faster installations and ensuring
version consistency across environments using the yarn.lock file. Automation in CI/CD
pipelines can be achieved by using Yarn to install dependencies before building and testing
the project. This ensures that developers always work with the correct versions of libraries,
avoiding dependency conflicts.
For Example:
Define a CI/CD step to install dependencies:
yarn install
This command installs all dependencies listed in package.json and ensures the correct
versions are used every time the pipeline runs.
Question: How would you use Rollup to bundle and publish a JavaScript library?
Answer:
Rollup is ideal for building JavaScript libraries because of its tree-shaking capabilities, which
ensure that only the necessary code is included in the bundle. After bundling the library, it
can be published to npm for public use or deployed privately. Rollup also supports multiple
formats like CommonJS (CJS) and ES modules (ESM), making the library compatible with
different environments.
For Example:
Here’s a Rollup configuration for bundling a library:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
699
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'cjs',
},
};
Question: How can Node.js automate file operations, such as rotating logs?
Answer:
In DevOps, log rotation prevents disk space from being consumed by large or outdated logs.
Node.js automates this task by managing log files—moving older logs to an archive folder or
deleting them. This automation ensures that only recent logs are kept while preserving old
ones for future analysis.
For Example:
The following Node.js script rotates logs by archiving them:
const fs = require('fs');
const path = require('path');
files.forEach((file) => {
const oldPath = path.join(logDir, file);
const newPath = path.join(archiveDir, file);
fs.rename(oldPath, newPath, (err) => {
if (err) throw err;
console.log(`${file} archived.`);
});
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
700
});
});
Question: How would you automate version updates and releases using Node.js?
Answer:
Automating version updates ensures that every release follows a standardized versioning
strategy, such as Semantic Versioning (semver). Node.js scripts can increment the version in
package.json before committing the changes and tagging them in version control. This
ensures that the product is always released with unique version numbers.
For Example:
A script to bump the version number:
const fs = require('fs');
const packageJson = require('./package.json');
Question: How would you implement error handling in a JavaScript automation script?
Answer:
Error handling is critical in automation scripts to ensure that failures are detected and
reported promptly. Node.js provides try...catch blocks for error handling. In CI/CD
pipelines, it’s essential to log errors and exit with appropriate status codes to prevent faulty
builds from being deployed.
For Example:
Here’s a Node.js script with proper error handling:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
701
try {
const result = performTask();
console.log('Task completed:', result);
} catch (error) {
console.error('Error occurred:', error.message);
process.exit(1); // Exit with failure status
}
Question: How would you create a simple build tool using Parcel?
Answer:
Parcel simplifies the build process by bundling assets without complex configuration. It
automatically detects the required dependencies and optimizes files for production. Parcel is
ideal for small to medium-sized projects where rapid development and quick builds are
necessary.
For Example:
Run the following commands to install Parcel and build your project:
This command generates a production-ready build with optimized JavaScript, HTML, and
CSS assets.
Question: How would you use Node.js to automate API calls in a CI/CD pipeline?
Answer:
Node.js can be used to automate interactions with external APIs, which is helpful in CI/CD
pipelines for tasks like triggering deployments, sending notifications, or updating a
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
702
dashboard. The axios library simplifies sending HTTP requests from Node.js scripts.
Automating API calls ensures that required actions are performed consistently as part of the
pipeline, such as informing a service of a new release.
For Example:
Here is a Node.js script to send a POST request to notify a deployment:
notifyDeployment();
Question: How can you use Node.js to monitor and analyze application logs?
Answer:
Node.js can monitor logs in real-time by reading log files and analyzing their contents. This is
helpful in DevOps for tracking errors, identifying performance issues, and ensuring smooth
operations. Log monitoring scripts can be integrated into dashboards or alerting systems for
proactive incident management.
For Example:
A Node.js script to watch and print new log entries:
const fs = require('fs');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
703
Answer:
Parcel is a zero-configuration bundler that makes it easy to build static websites. It handles
JavaScript, CSS, HTML, and other assets, optimizing them for production. Using Parcel,
developers can build and deploy static sites quickly.
For Example:
Bundle and optimize a static site with the following command:
Parcel automatically detects the dependencies and creates an optimized build in the dist
directory, ready for deployment.
Question: How would you schedule a task to run at specific intervals using Node.js?
Answer:
Using the node-cron library, you can schedule tasks to run at specific intervals, such as hourly
or daily. This is useful for automating routine tasks like backups, log rotations, or data
synchronization in DevOps.
For Example:
A Node.js script to run a task every minute:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
704
Question: How can you use npm to automate version checks for dependencies?
Answer:
Using npm outdated or npm-check, developers can automate the process of checking for
outdated dependencies. Keeping dependencies up-to-date helps maintain security and
ensures compatibility with the latest tools.
For Example:
Run the following command to check outdated packages:
npm outdated
Question: How does pnpm help manage multiple package versions efficiently?
Answer:
pnpm stores packages in a global content-addressable store and creates symlinks to them in
projects. This reduces disk space usage and ensures that different versions of a package are
efficiently managed without duplication.
For Example:
Install a specific version of a package with pnpm:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
705
This ensures that the specified version is linked and reused across multiple projects.
Answer:
Parcel automatically minifies CSS and JavaScript files in production builds, reducing the size
of the bundled files and improving application performance. This ensures that only optimized
code is deployed.
For Example:
Create a minified build with:
Answer:
Node.js can be used to identify and delete temporary files periodically, preventing
unnecessary disk usage. This cleanup can be scheduled as part of a DevOps routine.
For Example:
A script to delete all files from a temporary folder:
const fs = require('fs');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
706
files.forEach((file) => {
fs.unlink(path.join(tempDir, file), (err) => {
if (err) throw err;
console.log(`${file} deleted.`);
});
});
});
Answer:
Webpack bundles JavaScript files along with other assets like CSS and images, making it
ideal for large projects. Configuring Webpack allows for modular code development and
optimized production builds.
For Example:
Here’s a basic Webpack configuration:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
707
Answer:
Node.js can collect and log server metrics such as CPU usage, memory consumption, and
disk space, providing insights into server performance. These metrics can be used to trigger
alerts or generate reports, ensuring proactive monitoring.
For Example:
A script to log memory usage every 5 seconds:
setInterval(() => {
const memoryUsage = process.memoryUsage();
console.log('Memory Usage:', memoryUsage);
}, 5000);
This script logs memory usage, helping identify memory leaks or performance issues.
Question: How would you use Node.js to automate Docker container management?
Answer:
Node.js can interact with Docker through the child_process module or Docker’s APIs to
automate tasks such as building, running, stopping, and removing containers. This is
especially useful in DevOps workflows for continuous integration and deployment.
Automating container management ensures that the containers are always up-to-date and
eliminates the need for manual intervention.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
708
For Example:
Here is a script to automate Docker container creation and startup:
This script builds a Docker image and runs the container on port 3000.
Answer:
Node.js can manage Kubernetes resources programmatically using the
@kubernetes/client-node library. This allows automation of tasks such as creating,
updating, and monitoring pods, which is crucial in DevOps for scaling and maintaining
applications in a Kubernetes cluster.
For Example:
Below is a script to list all pods in the default namespace:
k8sApi.listNamespacedPod('default').then((res) => {
console.log('Pods:', res.body.items);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
709
});
Question: How would you automate the deployment of cloud functions with Node.js?
Answer:
Node.js can automate the deployment of serverless cloud functions by integrating with tools
like the AWS SDK, Firebase CLI, or Google Cloud SDK. This allows developers to deploy
functions as part of a CI/CD pipeline, ensuring rapid and consistent updates.
For Example:
Here’s a Firebase deployment command automated using Node.js:
Question: How would you automate DNS updates for blue-green deployments with Node.js?
Answer:
In blue-green deployments, DNS updates ensure traffic is smoothly redirected between the
two environments. Node.js can automate these updates by interacting with cloud DNS
services like AWS Route 53. This eliminates downtime by switching traffic seamlessly from
the old version to the new one.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
710
For Example:
Below is a Node.js script to update DNS records in AWS Route 53:
const params = {
ChangeBatch: {
Changes: [{
Action: 'UPSERT',
ResourceRecordSet: {
Name: 'myapp.example.com',
Type: 'CNAME',
TTL: 300,
ResourceRecords: [{ Value: 'new-app.example.com' }],
},
}],
},
HostedZoneId: 'Z3M3LMPEXAMPLE',
};
Question: How can you automate build and deployments using JavaScript with GitHub
Actions?
Answer:
GitHub Actions allows you to define workflows that automate build and deployment
processes. JavaScript-based actions can trigger builds, run tests, and deploy applications in
response to code changes. This ensures consistent and error-free deployments directly from
the repository.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
711
For Example:
Below is a GitHub Actions workflow for a Node.js project:
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Deploy
run: npm run deploy
Question: How can you automate version tagging using Node.js in CI/CD pipelines?
Answer:
Versioning is critical in CI/CD to ensure each release is uniquely identifiable. Node.js can
automate version increments and tagging with Git. Automating this process ensures that
every build is correctly tracked and versioned.
For Example:
Below is a Node.js script to bump the version and tag it in Git:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
712
Answer:
Multi-environment builds allow applications to be configured differently for development,
testing, and production. Webpack supports this through environment variables and
separate configurations, ensuring each build matches the environment’s requirements.
For Example:
Below is a Webpack configuration that uses environment variables:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
713
Answer:
Long-running processes in Node.js can block the event loop, impacting performance. Using
the Worker Threads API, you can offload heavy computations to separate threads, ensuring
the main thread remains responsive.
For Example:
Here is a script using worker threads for a long-running task:
Question: How can JavaScript automate security audits using npm and Snyk?
Answer:
Automated security audits ensure that applications are free from known vulnerabilities. npm
provides built-in auditing, while Snyk offers deeper insights into dependencies. These tools
can be integrated into CI/CD pipelines to block deployments with vulnerabilities.
For Example:
Run an npm security audit:
npm audit
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
714
snyk test
Question: How would you automate rollbacks using Node.js if a deployment fails?
Answer:
Automating rollbacks ensures that faulty deployments are quickly undone to maintain
service availability. Node.js can interact with version control systems and infrastructure tools
to revert to the last known stable state if an error occurs during deployment.
For Example:
Here’s a script that reverts to the previous Git commit:
exec('git reset --hard HEAD~1 && pm2 restart my-app', (error, stdout, stderr) => {
if (error) {
console.error(`Rollback Error: ${stderr}`);
return;
}
console.log('Rollback Successful:', stdout);
});
Question: How would you automate failure notifications for a CI/CD pipeline using Node.js?
Answer:
Automating notifications ensures that DevOps teams are alerted immediately when a
pipeline fails, allowing them to respond quickly. Node.js can send alerts to messaging
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
715
services like Slack, Microsoft Teams, or email using APIs. This ensures faster recovery from
pipeline issues.
For Example:
Here’s a Node.js script to send a Slack message when a failure occurs:
Question: How would you automate rollbacks in a blue-green deployment strategy using
Node.js?
Answer:
In a blue-green deployment strategy, Node.js can automate the rollback process by
switching traffic back to the stable environment if the new version fails. This ensures minimal
downtime and avoids disruptions in production services. The automation interacts with DNS
or load balancers to switch environments.
For Example:
Here’s a script that switches traffic back to the blue environment:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
716
const params = {
ChangeBatch: {
Changes: [{
Action: 'UPSERT',
ResourceRecordSet: {
Name: 'myapp.example.com',
Type: 'CNAME',
TTL: 300,
ResourceRecords: [{ Value: 'blue-app.example.com' }],
},
}],
},
HostedZoneId: 'Z3M3LMPEXAMPLE',
};
Question: How would you use Node.js to perform automated server health checks?
Answer:
Automated health checks ensure that servers and services are running as expected. Node.js
can periodically send HTTP requests to endpoints and alert the team if any service is
unresponsive. This is essential for proactive monitoring in DevOps environments.
For Example:
A script to check the health of a server:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
717
console.log('Server is healthy');
}
} catch (error) {
console.error('Server is down:', error.message);
}
};
Question: How can you automate the creation and upload of backups to a cloud storage
service using Node.js?
Answer:
Node.js can automate the backup process by compressing files and uploading them to cloud
services like AWS S3 or Google Cloud Storage. Automating backups ensures data is
consistently saved without manual intervention, reducing the risk of data loss.
For Example:
Here’s a script to upload a backup to AWS S3:
uploadBackup('./backup.zip', 'my-s3-bucket');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
718
Question: How would you automate dependency updates in a CI/CD pipeline with Node.js?
Answer:
Automating dependency updates ensures that your application stays secure and up-to-date
with the latest libraries. Node.js can automate this process by running npm outdated and
updating dependencies during the build process.
For Example:
A script to update all outdated dependencies:
Answer:
Managing configurations for multiple environments (e.g., development, staging, production)
ensures the right settings are applied for each. Node.js can use dotenv to load different
environment variables based on the deployment stage.
For Example:
Here’s a setup to load environment-specific variables:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
719
Question: How would you automate load testing for an application using Node.js?
Answer:
Automating load testing helps ensure that your application can handle high traffic. Node.js
can integrate with tools like Artillery or k6 to automate load tests, providing insights into the
system’s performance under stress.
For Example:
Install Artillery and run a load test:
Answer:
Automating Git operations (e.g., pulling, committing, and pushing changes) saves time and
reduces human error. Node.js can trigger these commands through the child_process
module, enabling seamless version control as part of automation workflows.
For Example:
Here’s a script to commit and push changes:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
720
exec('git add . && git commit -m "Automated commit" && git push',
(error, stdout, stderr) => {
if (error) {
console.error('Git Error:', stderr);
return;
}
console.log('Git Output:', stdout);
});
Question: How would you integrate ESLint into a CI/CD pipeline to automate code quality
checks?
Answer:
Automating code quality checks with ESLint ensures that coding standards are maintained
across the project. This can be integrated into CI/CD pipelines to prevent code with style
violations from being merged or deployed.
For Example:
Define an npm script to run ESLint:
{
"scripts": {
"lint": "eslint ."
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
721
80. Scenario: Automating Application Scaling with Node.js and AWS SDK
Question: How would you automate scaling operations using Node.js and the AWS SDK?
Answer:
Node.js can automate application scaling by interacting with AWS Auto Scaling groups. This
ensures that resources scale dynamically based on demand, maintaining performance
during traffic spikes.
For Example:
A script to update the desired instance count in an auto-scaling group:
const params = {
AutoScalingGroupName: 'my-auto-scaling-group',
DesiredCapacity: 5,
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
722
THEORETICAL QUESTIONS
1. What is JavaScript and how is it different from Java?
Answer:
JavaScript is a high-level, dynamic programming language used primarily for web
development. It was initially created to add interactivity to websites and is now widely used
in both frontend and backend development (via Node.js). JavaScript runs in web browsers
and has become a fundamental part of the web’s architecture alongside HTML and CSS.
Although JavaScript and Java share similar names, they are different in purpose and design.
JavaScript is dynamically typed, interpreted, and supports prototype-based inheritance. In
contrast, Java is statically typed, compiled, and follows class-based object-oriented principles.
Java is used for large-scale backend systems, Android apps, and enterprise software, while
JavaScript focuses more on web interactivity.
For Example:
In this example, the JavaScript function interacts with HTML elements to provide real-time
input validation. Java would not be suitable for this task, as it operates on servers or devices,
not directly within the browser.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
723
Answer:
JavaScript supports several data types to handle different kinds of values. These data types
are broadly categorized into primitive and non-primitive types.
For Example:
In this example, variables with different data types are created to demonstrate how
JavaScript handles them.
Answer:
A closure occurs when a function retains access to variables from its parent scope, even after
the outer function has finished executing. Closures allow inner functions to "remember" the
environment they were created in, enabling encapsulation and data privacy.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
724
Closures are used extensively in JavaScript to create private variables that are inaccessible
from outside their enclosing functions. This concept is useful for creating modular and
reusable code.
For Example:
function outerFunction(outerVariable) {
return function innerFunction(innerVariable) {
console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
};
}
Here, innerFunction forms a closure by capturing outerVariable from the outer scope.
Even though outerFunction has returned, innerFunction can still access outerVariable.
Answer:
JavaScript provides three ways to declare variables: var, let, and const. Each has different
behavior regarding scoping, hoisting, and reassignment.
1. var:
○ Scoped to the function in which it is declared or global if outside a function.
○ Can be redeclared and reassigned.
○ Hoisted to the top of its scope but initialized with undefined.
2. let:
○ Block-scoped (only accessible within the enclosing {} block).
○ Cannot be redeclared within the same scope, but can be reassigned.
○ Not initialized until the declaration is reached.
3. const:
○ Block-scoped like let.
○ Cannot be reassigned or redeclared.
○ Must be initialized during declaration.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
725
var x = 1;
if (true) {
let y = 2;
const z = 3;
console.log(y, z); // Output: 2 3
}
console.log(x); // Output: 1
In this example, y and z are block-scoped to the if block, while x is accessible globally. Trying
to access y or z outside the block will throw an error.
Answer:
An arrow function is a more concise way to define functions in JavaScript, introduced in ES6.
Unlike traditional functions, arrow functions do not have their own this context. Instead,
they inherit this from the outer scope, making them useful for callbacks and event handlers
where this needs to be preserved.
Arrow functions cannot be used as constructors and do not support the arguments object.
For Example:
// Regular function
function add(a, b) {
return a + b;
}
// Arrow function
const multiply = (a, b) => a * b;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
726
Here, the arrow function provides a shorter syntax for the same functionality as the regular
function.
Answer:
The event loop is the mechanism that allows JavaScript to perform non-blocking I/O
operations despite being single-threaded. It ensures that asynchronous tasks (e.g., timers,
API calls) do not block the main thread.
The event loop continuously checks if the call stack is empty. When it is, the event loop picks
up pending tasks from the task queue and pushes them onto the stack for execution.
For Example:
console.log("Start");
setTimeout(() => {
console.log("Inside timeout");
}, 0);
console.log("End");
// Output:
// Start
// End
// Inside timeout
Even though the timeout is set to 0, it gets queued and only runs after the current stack is
cleared.
Answer:
A promise represents the result of an asynchronous operation. It can be in one of three
states: pending, fulfilled, or rejected. Promises help in managing asynchronous code by
providing then() and catch() methods to handle the result.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
727
For Example:
This example demonstrates a simple promise that either resolves or rejects based on a
condition.
Answer:
Recursion is when a function calls itself to solve a problem in smaller steps. A recursive
function must include a base case to prevent infinite recursion and stack overflow.
For Example:
function factorial(n) {
if (n === 0) return 1; // Base case
return n * factorial(n - 1);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
728
Answer:
JavaScript objects are collections of key-value pairs, often used to represent real-world
entities.
For Example:
const person = {
name: "Shardul",
age: 25,
greet() {
console.log(`Hello, ${this.name}`);
}
};
This object stores properties (name and age) and a method (greet).
10. What is the difference between shallow copy and deep copy in
JavaScript?
Answer:
A shallow copy only duplicates the first-level properties, while a deep copy clones all levels,
ensuring independence from the original object.
For Example:
const obj1 = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj1 };
shallowCopy.b.c = 3;
console.log(obj1.b.c); // Output: 3
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
729
This demonstrates how changes to a nested object affect only the shallow copy.
Answer:
Hoisting is a JavaScript mechanism where variable and function declarations are moved to
the top of their scope before code execution. This means that even if you declare a function
or variable later in your code, it is known to the JavaScript engine at the start of execution.
However, only the declarations are hoisted, not the initializations.
● Variables declared with var are hoisted but initialized with undefined.
● Variables declared with let and const are also hoisted, but they remain in a temporal
dead zone (TDZ), meaning they cannot be accessed before their declaration is
encountered. Accessing them before initialization will result in a ReferenceError.
● Function declarations are fully hoisted, meaning you can call the function even before
the line where it is declared.
For Example:
function greet() {
return "Hello, World!";
}
In this example:
● The function greet() is hoisted and works even when called before the declaration.
● x is declared using var and hoisted, but its value is undefined until initialized.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
730
● Trying to access y (declared with let) before its declaration results in an error because
it is in the TDZ.
Answer:
Synchronous code in JavaScript runs sequentially. Each line of code is executed one after the
other, and the program waits for a task to complete before moving to the next. This can
become problematic if a task takes too long, as it will block the main thread, making the
application unresponsive.
Asynchronous code, on the other hand, allows certain tasks to run in the background.
JavaScript can continue executing other code while waiting for the asynchronous task (such
as a network request or timer) to complete. Asynchronous operations are achieved using
callbacks, promises, or async/await syntax.
For Example:
// Synchronous example
console.log("Start");
for (let i = 0; i < 1000000000; i++) {} // Long loop, blocking execution
console.log("End");
// Asynchronous example
console.log("Start");
setTimeout(() => console.log("After 2 seconds"), 2000); // Non-blocking
console.log("End");
In the asynchronous example, the setTimeout function schedules a task to run after 2
seconds but allows the rest of the code to continue running without waiting.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
731
Answer:
The == (loose equality) and === (strict equality) operators are used for comparisons in
JavaScript, but they behave differently:
● == (loose equality): Converts both values to the same type before comparing them.
This is known as type coercion.
● === (strict equality): Compares both the value and type without type conversion. If
the types are different, it immediately returns false.
For Example:
Answer:
The this keyword refers to the context in which a function is called. Its value can vary
depending on how the function is invoked. In general:
For Example:
const person = {
name: "Shardul",
greet: function () {
console.log(`Hello, ${this.name}`);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
732
}
};
In the example, calling greet() directly through the person object correctly binds this to
person. However, when the method is assigned to a variable, it loses its context.
Answer:
Template literals (introduced in ES6) are string literals enclosed by backticks (`). They allow
for string interpolation, where expressions can be embedded directly within the string using
${}. Template literals also support multi-line strings without needing special characters.
For Example:
// Multi-line string
const message = `This is a
multi-line string.`;
console.log(message);
// Output:
// Hello, my name is Shardul and I am 25 years old.
// This is a
// multi-line string.
Template literals make it easy to build strings dynamically and format them neatly.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
733
Answer:
Destructuring is a syntax introduced in ES6 that allows unpacking values from arrays or
objects into separate variables. This makes it easier to extract specific elements or properties.
For Example:
// Array destructuring
const [a, b] = [1, 2];
console.log(a, b); // Output: 1 2
// Object destructuring
const person = { name: "Shardul", age: 25 };
const { name, age } = person;
console.log(name, age); // Output: Shardul 25
Answer:
The spread operator (...) allows you to expand an array or object into individual elements.
The rest operator (...) collects multiple elements into an array. Both operators provide
flexibility when working with arrays, objects, and functions.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
734
The spread operator helps with copying or combining arrays, while the rest operator handles
dynamic arguments.
Answer:
A higher-order function is a function that either accepts another function as an argument
or returns a function. Higher-order functions enable powerful abstractions and are
commonly used in functional programming.
For Example:
function greet(name) {
return function (message) {
console.log(`${message}, ${name}`);
};
}
Here, greet returns a new function, demonstrating the concept of a higher-order function.
Answer:
A callback is a function that is passed as an argument to another function and executed after
the completion of some operation. Callbacks are frequently used in asynchronous
programming to handle tasks such as timers and network requests.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
735
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
In the example, the callback function is executed after a delay, simulating an asynchronous
operation.
Answer:
The map(), filter(), and reduce() methods are used to process arrays:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
736
Answer:
JavaScript introduced async and await in ES8 to simplify working with asynchronous code.
Before their introduction, developers used callbacks and promises to handle asynchronous
operations. These constructs could lead to complex code with multiple .then() and
.catch() calls, making the code harder to read and maintain.
● async: When a function is declared with the async keyword, it always returns a
promise, regardless of whether the function explicitly returns a value or a promise. If a
function returns a non-promise value, it is automatically wrapped in a resolved
promise.
● await: The await keyword can only be used inside an async function. It pauses the
function’s execution until the promise resolves or rejects. This makes asynchronous
code appear synchronous and easier to read.
For Example:
fetchData();
Here, the await keyword ensures the function waits for the network request and JSON
parsing to complete before continuing, improving readability by avoiding chained .then()
methods.
Answer:
JavaScript automatically manages memory using a garbage collector that frees memory
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
737
occupied by objects that are no longer needed. This process ensures that memory leaks are
minimized. The JavaScript engine typically uses two algorithms:
1. Reference Counting:
In this method, the engine keeps track of how many references point to an object. If
the reference count drops to zero (i.e., no references exist), the object is eligible for
garbage collection.
2. Mark-and-Sweep:
This is the more commonly used algorithm. The garbage collector marks all objects
that can still be accessed from the global scope or other reachable objects. After
marking, it "sweeps" away unmarked objects from memory.
For Example:
In this example, when obj is set to null, the original object is no longer referenced and can
be garbage-collected.
Answer:
Event delegation is a technique that leverages event bubbling to manage events efficiently.
When an event occurs on an element, it first triggers on the target element, then bubbles up
to its parent and ancestors. By placing an event listener on a parent element, you can listen
for events occurring on its child elements without adding individual listeners to each child.
This is especially useful when elements are dynamically added or removed, such as buttons
or list items generated at runtime.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
738
});
Here, a single event listener on the parent element handles click events for all its buttons,
even those added dynamically.
Answer:
Shallow cloning copies only the top-level properties of an object, while any nested objects
are still referenced, meaning both the original and the clone share the same nested objects.
In contrast, deep cloning creates an entirely independent copy of the object, including all
nested structures.
For Example:
// Shallow clone
const obj1 = { a: 1, b: { c: 2 } };
const shallowClone = { ...obj1 };
shallowClone.b.c = 3;
console.log(obj1.b.c); // Output: 3 (shared reference)
// Deep clone
const deepClone = JSON.parse(JSON.stringify(obj1));
deepClone.b.c = 4;
console.log(obj1.b.c); // Output: 3 (independent copy)
With shallow cloning, modifying the nested object in the clone also affects the original
object. Deep cloning avoids this by creating a new nested object.
Answer:
A generator is a special type of function in JavaScript that can pause and resume its
execution. It is defined using the function* syntax and uses the yield keyword to return
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
739
Each time the generator function is called with next(), it resumes from where it last
stopped.
For Example:
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
The yield keyword allows the function to pause execution and return a value, making
generators suitable for on-demand computations.
Answer:
Memoization is an optimization technique where the results of expensive function calls are
cached and returned when the same inputs occur again. This improves performance by
avoiding redundant calculations.
For Example:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
const result = fn(...args);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
740
cache[key] = result;
return result;
};
}
In this example, subsequent calls with the same input use the cached result, improving
performance.
Answer:
The module pattern is a design pattern used to encapsulate code within a function,
exposing only what is necessary through an API. This promotes data privacy and prevents
namespace pollution.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
741
Counter.increment(); // Output: 1
Counter.increment(); // Output: 2
Counter.reset(); // Output: 0
Here, the count variable is private, accessible only through the public methods.
Answer:
Debouncing ensures that a function is called only once after a specified delay, regardless of
how many times it is triggered during that delay. It is often used to limit the frequency of
events like typing or scrolling.
For Example:
search("JavaScript");
search("JavaScript Debounce");
Only the last input triggers the search function after a 300ms delay, reducing unnecessary
calls.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
742
Answer:
Throttling limits the number of times a function can be called within a specific period. It
ensures that the function executes at most once per interval, even if it is triggered multiple
times.
For Example:
In this example, the log function will run at most once every second, even if the scroll event
fires continuously.
30. What are weak references in JavaScript, and how are they used?
Answer:
A weak reference allows you to refer to an object without preventing it from being garbage-
collected. JavaScript provides WeakMap and WeakSet to handle weak references. These
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
743
For Example:
In this example, the object obj is removed from the WeakMap when no longer referenced,
helping prevent memory leaks.
31. What are Web Workers in JavaScript, and how do they improve
performance?
Answer:
Web Workers allow JavaScript to execute code in parallel threads, separate from the main
browser thread. This ensures that intensive tasks like complex calculations, data processing,
or image manipulation do not block the main thread, which handles UI rendering and user
interactions. Web Workers run in their own context and communicate with the main thread
through messages. However, since they operate in a different context, they do not have
access to the DOM or some browser APIs like window and document.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
744
worker.js
main.js
This example creates a dedicated Web Worker that receives data, performs a calculation,
and sends the result back to the main thread. By doing this in the background, the UI
remains responsive.
Answer:
These mechanisms store client-side data but differ in behavior and use cases.
1. localStorage:
○ Data persists even after the browser is closed.
○ Stores larger amounts of data (up to 5–10 MB).
○ Useful for storing preferences or offline data.
2. sessionStorage:
○ Data lasts only for the session (until the tab or browser is closed).
○ Typically used for temporary data, such as form inputs.
3. Cookies:
○ Sent with each HTTP request, allowing data exchange with the server.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
745
For Example:
Answer:
The Shadow DOM is part of the Web Components standard, which allows developers to
encapsulate the structure, styles, and behavior of a component within a shadow tree. This
prevents CSS styles and JavaScript code from affecting elements outside the shadow DOM
and vice versa. It is useful when building reusable and isolated components.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
746
The styles inside the shadow DOM are scoped to that component, preventing them from
interfering with the global CSS.
Answer:
call(), apply(), and bind() are methods used to explicitly set the value of this when
calling a function.
● call(): Calls a function with a specified this value and individual arguments.
● apply(): Similar to call(), but arguments are passed as an array.
● bind(): Returns a new function with this value bound to the specified object. It does
not invoke the function immediately.
For Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
The key difference is that bind() creates a new function, while call() and apply() invoke
the function immediately.
Answer:
A polyfill is code that provides modern functionality on older browsers or environments that
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
747
do not support it. Polyfills allow developers to ensure their code works across various
browsers by adding missing features.
For Example:
if (!Array.prototype.map) {
Array.prototype.map = function (callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
}
This example implements a polyfill for Array.prototype.map to ensure the method works on
older browsers.
Answer:
A Proxy is an object that wraps another object and intercepts fundamental operations like
property access, assignment, and method invocation. This allows developers to add custom
behavior to these operations.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
748
In this example, the proxy intercepts the property access and provides custom behavior for
missing properties.
37. How does the JavaScript event loop manage microtasks and
macrotasks?
Answer:
The event loop in JavaScript processes tasks in two phases:
For Example:
console.log("Start");
console.log("End");
// Output:
// Start
// End
// Microtask
// Macrotask
Here, the microtask runs before the macrotask, even though both are scheduled at the same
time.
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
749
Answer:
WeakMap and WeakSet are similar to Map and Set but hold weak references to objects. This
means the referenced objects can be garbage-collected if no other references exist.
For Example:
Answer:
Symbols are unique and immutable data types introduced in ES6. They are used as object
property keys to avoid naming conflicts, especially in situations where multiple libraries or
modules interact.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
750
Symbols guarantee that each value is unique, even with the same description.
Answer:
A custom iterator in JavaScript follows the [Symbol.iterator] protocol. It defines an object
with a next() method that returns { value, done }. Iterators allow objects to be used in
loops like for...of.
For Example:
const iterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 3) {
return { value: step, done: false };
}
return { value: undefined, done: true };
}
};
}
};
SCENARIO QUESTIONS
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
751
Question: How would you remove duplicate elements from an array efficiently?
Answer:
To remove duplicate elements from an array, the most efficient way in modern JavaScript is
by using a Set. A Set is a collection that only allows unique values, and converting the array
to a set will automatically remove any duplicates. You can then convert the set back to an
array using the spread operator (...). This approach is both concise and efficient in terms of
time complexity.
For Example:
function removeDuplicates(arr) {
return [...new Set(arr)];
}
The Set automatically removes duplicate elements, and the spread operator returns a new
array with only unique values. This solution has a time complexity of O(n), making it highly
efficient for large datasets.
For Example:
function reverseString(str) {
return str.split('').reverse().join('');
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
752
The split() method converts the string into an array of characters, reverse() reverses the
array, and join() merges it back into a single string. This approach works well for typical
strings but may need adjustments for Unicode characters.
For Example:
function factorial(n) {
if (n === 0) return 1; // Base case
return n * factorial(n - 1); // Recursive call
}
For Example:
function bubbleSort(arr) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
753
let n = arr.length;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // Swap
}
}
}
return arr;
}
The time complexity of Bubble Sort is O(n²), making it less efficient for large datasets.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
754
Binary Search has a time complexity of O(log n), making it very efficient.
For Example:
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
if (this.items.length === 0) return "Underflow";
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
755
console.log(stack.peek()); // Output: 1
This implementation supports push, pop, and peek operations on the stack.
For Example:
class Queue {
constructor() {
this.items = [];
}
enqueue(element) {
this.items.push(element);
}
dequeue() {
if (this.items.length === 0) return "Underflow";
return this.items.shift();
}
front() {
return this.items[0];
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
756
For Example:
class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}
class BinaryTree {
constructor() {
this.root = null;
}
insert(data) {
const newNode = new Node(data);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.data < node.data) {
if (node.left === null) node.left = newNode;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
757
This code creates a binary tree with insertion logic based on data comparison.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
758
In this example, useMemo prevents recalculating the sum unless the numbers array changes.
Question: How would you design a basic RESTful API using Express?
Answer:
To design a RESTful API with Express, you define routes for different CRUD operations. Use
HTTP methods like GET, POST, PUT, and DELETE to perform operations on resources.
For Example:
This API provides endpoints to retrieve and create users, following RESTful principles.
Question: How can you use Promise.all() to handle multiple promises in JavaScript?
Answer:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
759
Promise.all() is used to execute multiple promises in parallel and waits for all of them to
resolve. It returns a single promise that resolves when all input promises resolve, or rejects if
any one of the promises fails. This method ensures that you can work with the results of
multiple asynchronous operations together. It’s especially useful for fetching data from
multiple APIs or executing independent tasks that need to complete before proceeding.
For Example:
Promise.all([promise1, promise2])
.then(responses => Promise.all(responses.map(res => res.json())))
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
Question: How would you handle errors using async and await in JavaScript?
Answer:
Using async and await simplifies working with asynchronous operations. With try-catch
blocks, errors are handled just like synchronous code. This approach improves readability
and makes it easier to track errors compared to promise chains.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
760
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error.message);
}
}
fetchData('https://jsonplaceholder.typicode.com/posts/1');
Explanation:
1. await pauses the function execution until the fetch promise resolves.
2. If the fetch fails or the server returns an error, the code throws an exception.
3. The catch block handles any errors, ensuring graceful failure handling.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
761
}, 300);
search('JavaScript');
search('JavaScript Debounce'); // Only this call will execute after 300ms
Explanation:
1. Every call to search resets the timeout, ensuring only the last call is executed.
2. This reduces the number of API calls and improves performance.
Question: How would you implement throttling to optimize scroll event handling?
Answer:
Throttling ensures that a function executes at most once within a specified time, even if the
event is triggered repeatedly. This is essential for handling events like scrolling or resizing
efficiently.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
762
Explanation:
1. This code ensures the logScroll function runs at most once per second.
2. The throttling technique optimizes performance by limiting event-triggered calls.
Question: How can you use the spread operator to clone and merge objects?
Answer:
The spread operator (...) allows you to clone objects or merge multiple objects into a new
one. This approach simplifies working with objects and ensures immutability.
For Example:
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
console.log(clonedObj); // Output: { a: 1, b: 2 }
console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
763
Question: How does the rest operator simplify function arguments handling?
Answer:
The rest operator (...) collects multiple arguments into an array. This makes it easy to create
functions that can handle variable numbers of arguments.
For Example:
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
Explanation:
1. The rest operator collects all function arguments into a single array.
2. This makes the function flexible and reusable.
Question: How would you use a Map to store key-value pairs in JavaScript?
Answer:
A Map allows you to store key-value pairs, with the keys being of any type, including objects
or functions. It provides useful methods like set(), get(), has(), and delete().
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
764
Explanation:
1. Maps are more versatile than objects, as keys can be non-string values.
2. The size property directly returns the number of entries.
Question: How would you use a Set to store unique values in JavaScript?
Answer:
A Set ensures that only unique values are stored. It is useful when you need to eliminate
duplicates or manage collections of distinct elements.
For Example:
set.add(5);
set.delete(2);
console.log(set.has(2)); // Output: false
console.log(set.size); // Output: 4
Explanation:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
765
const iterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 3) return { value: step, done: false };
return { value: undefined, done: true };
}
};
}
};
Explanation:
1. The next() method returns an object with value and done properties.
2. This approach enables custom iteration logic.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
766
Explanation:
1. The WeakMap entry is automatically removed when the object is no longer referenced.
2. This ensures efficient memory management.
For Example:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}
console.log(fibonacci(10)); // Output: 55
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
767
Explanation:
1. The memoize function caches previous results to avoid redundant recursive calls.
2. This improves the time complexity of recursive functions from O(2^n) to O(n).
Question: How would you implement a graph using adjacency lists in JavaScript?
Answer:
A graph can be represented using an adjacency list, where each node points to a list of
connected nodes. This is an efficient way to store sparse graphs.
For Example:
class Graph {
constructor() {
this.adjacencyList = {};
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
}
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
}
}
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
768
For Example:
class Graph {
constructor() {
this.adjacencyList = {};
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
}
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
769
graph.addVertex('C');
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.dfs('A'); // Output: A B C
Explanation:
For Example:
class Graph {
constructor() {
this.adjacencyList = {};
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
}
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
}
bfs(start) {
const queue = [start];
const visited = new Set(queue);
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
770
Explanation:
1. BFS ensures that all vertices at the same depth are explored before going deeper.
2. The queue guarantees the correct traversal order.
For Example:
class PriorityQueue {
constructor() {
this.items = [];
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
771
enqueue(element, priority) {
const queueElement = { element, priority };
let added = false;
if (!added) this.items.push(queueElement);
}
dequeue() {
return this.items.shift();
}
}
Explanation:
1. Elements with higher priority are dequeued before lower priority ones.
2. The queue maintains an ordered insertion based on priority.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
772
function mergeSort(arr) {
if (arr.length <= 1) return arr;
Explanation:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
773
app.use(limiter);
Explanation:
For Example:
function quickSort(arr) {
if (arr.length <= 1) return arr;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
774
Question: How would you implement an LRU (Least Recently Used) cache in JavaScript?
Answer:
An LRU cache keeps the most recently accessed elements in memory and evicts the least
recently used ones.
For Example:
class LRUCache {
constructor(limit) {
this.cache = new Map();
this.limit = limit;
}
get(key) {
if (!this.cache.has(key)) return -1;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
else if (this.cache.size === this.limit)
this.cache.delete(this.cache.keys().next().value);
this.cache.set(key, value);
}
}
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
775
lru.put(2, 'B');
console.log(lru.get(1)); // Output: A
lru.put(3, 'C');
console.log(lru.get(2)); // Output: -1
Explanation:
For Example:
try {
jwt.verify(token, SECRET_KEY);
res.send('Protected resource accessed.');
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
776
} catch (err) {
res.status(401).send('Invalid token.');
}
});
Explanation:
When you call a recursive function, the function may call itself with the same arguments
multiple times, leading to redundant calculations. By storing previously computed results,
you can significantly reduce the time complexity.
For Example:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args); // Create a unique key for the arguments
if (cache[key]) return cache[key]; // Return cached result if it exists
const result = fn(...args); // Compute result
cache[key] = result; // Cache the result
return result; // Return the result
};
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
777
console.log(fibonacci(10)); // Output: 55
Explanation:
Question: How would you implement a graph using adjacency lists in JavaScript?
Answer:
A graph can be represented using an adjacency list, where each vertex (node) points to a list
of its connected vertices. This method is efficient in terms of both space and performance,
especially for sparse graphs.
For Example:
class Graph {
constructor() {
this.adjacencyList = {}; // Initialize an empty adjacency list
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = []; // Create
an array for new vertex
}
addEdge(vertex1, vertex2) {
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
778
Explanation:
1. The adjacency list is implemented as an object where each key represents a vertex
and its value is an array of adjacent vertices.
2. This structure allows efficient addition of vertices and edges.
3. It is space-efficient, particularly for sparse graphs, as it only stores existing
connections.
For Example:
class Graph {
constructor() {
this.adjacencyList = {}; // Initialize an empty adjacency list
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = []; // Create
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
779
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2); // Add edge from vertex1 to
vertex2
this.adjacencyList[vertex2].push(vertex1); // Add edge from vertex2 to
vertex1
}
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
780
For Example:
class Graph {
constructor() {
this.adjacencyList = {}; // Initialize an empty adjacency list
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = []; // Create
an array for new vertex
}
addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2); // Add edge from vertex1 to
vertex2
this.adjacencyList[vertex2].push(vertex1); // Add edge from vertex2 to
vertex1
}
bfs(start) {
const queue = [start]; // Initialize the queue with the starting node
const visited = new Set(queue); // Mark the starting node as visited
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
781
Explanation:
1. The BFS algorithm uses a queue to manage nodes that need to be visited.
2. Each node is dequeued, and its neighbors are added to the queue if they haven't
been visited.
3. This guarantees that nodes are processed level by level.
For Example:
class PriorityQueue {
constructor() {
this.items = []; // Array to hold the elements
}
enqueue(element, priority) {
const queueElement = { element, priority }; // Create an object for the
element and its priority
let added = false;
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
782
added = true;
break;
}
}
dequeue() {
return this.items.shift(); // Remove and return the highest priority
element
}
}
Explanation:
1. The enqueue method adds elements to the queue based on their priority.
2. Lower numerical priority values are dequeued first.
3. This structure efficiently handles dynamic priority-based requests.
For Example:
function mergeSort(arr) {
if (arr.length <= 1) return arr; // Base case
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
783
Explanation:
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
784
app.use(limiter);
Explanation:
1. The rate limiter limits requests from each IP to 5 requests per minute.
2. It sends an error message if the limit is exceeded, helping prevent abuse.
For Example:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
785
Explanation:
For Example:
const pool1 = mysql.createPool({ host: 'db1', user: 'root', database: 'db1' });
const pool2 = mysql.createPool({ host: 'db2', user: 'root', database: 'db2' });
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM
786
1. Each database has its own connection pool for efficient management.
2. Queries are executed concurrently across different databases.
Question: How would you implement file uploads using Multer in Node.js?
Answer:
Multer is middleware for handling multipart/form-data, which is used for file uploads in
forms. It simplifies file handling in Express.
For Example:
Explanation:
INTERVIEWNINJA.IN ECOMNOWVENTURESTM