JavaScript Scenario-Based MCQ
Scenario-based multiple choice questions covering JavaScript topics.
Related Topics
- HTML Basics
- CSS Basics
- ECMAScript 6 Basics
- JavaScript Unit Testing
- JavaScript Coding Practice
- JavaScript Design Patterns
- Data Structure in JavaScript
Table of Contents
L1: Fundamental (Entry-Level / Junior)
Focus: Syntax, basic logic, and standard data handling.
- Variables: var, let, const.
- Data Types: Primitives vs. Objects.
- Operators: Arithmetic, comparison (== vs ===), and logical.
- Numbers: parseInt, filter, Math, Infinity, NaN
- String: .toFixed(), .slice(), .Splice()
- Arrays: push(), pop(), and length.
- Control Flow: Simple if/else and for loops.
- Functions: Standard declarations and parameters.
L2: Intermediate (Junior-Mid / Developer)
Focus: ES6+ features, DOM, and common “trick” concepts.
- Scope & Closures: Understanding block scope and lexical scoping.
- Hoisting: Variable and function declarations.
- ES6 Features: Arrow functions, template literals, and destructuring.
- DOM & Events: Selecting elements, adding listeners, and event bubbling.
- Regular Expressions: Basic pattern matching.
- Error Handling: Using try…catch.
- Web Storage: localStorage and sessionStorage.
L3: Advanced (Mid-Senior / Lead)
Focus: Asynchronous operations, performance, and internal engine mechanics.
- Promises: Promise, Promise.all
- Async & Await: Handling complex asynchronous flows.
- Event Loop: Understanding the microtask vs. macrotask queue.
- this Keyword: Binding with .call(), .apply(), and .bind().
- Objects & Prototypes: Prototypal inheritance and the prototype chain.
- Functional Programming: High-order functions like map, filter, and reduce.
- Classes: Inheritance, constructors, and static methods.
- Modules: ES Modules (import/export).
- Fetch API & AJAX: Using
fetch(), response handling, POST requests, and HTTP error status handling. - Execution Context & Call Stack: JavaScript engine execution contexts, call stack behavior, and TDZ.
L4: Expert (Senior / Architect)
Focus: Scalability, security, and low-level optimization.
- Performance Optimization: Memoization, debouncing/throttling, and avoiding memory leaks.
- Design Patterns: Singleton, Factory, and Pub-Sub patterns.
- Security: Preventing XSS, CSRF, and secure data handling.
- Browser Internals: Rendering behavior, reflows, and repaints.
- Progressive Web Apps (PWA): Service workers and caching strategies.
- Complex Problem Solving: Coding simulators for real-world algorithmic tasks.
- Output-Based & Coding Patterns: Type coercion outputs, remove duplicates, and string/array manipulation patterns.
L5: Technical Lead
Focus: Code review, team standards, architectural decisions, and engineering best practices for leading a development team.
- Code Review & Standards: Identifying anti-patterns, enforcing conventions, and reviewing PRs.
- Async Strategy & Team Patterns: Choosing and enforcing async patterns across a codebase.
- Module Architecture: Organizing modules, barrel files, and dependency management.
- Error Handling Strategy: Centralized error handling and observability.
- Performance Review: Reviewing code for layout thrashing, memory leaks, and bundle size.
L6: Technical Architect
Focus: System-level design, scalability, security posture, micro-frontends, and cross-team JavaScript architecture.
- Micro-Frontend Architecture: Module federation, iframe isolation, and shared dependencies.
- Bundle & Runtime Optimization: Tree-shaking, code-splitting, and Web Workers.
- Security Architecture: CSP headers, CORS configuration, and supply chain security.
- State Management at Scale: Choosing state patterns for large distributed frontend teams.
- Migration & Evolution: Framework migrations, progressive TypeScript adoption, and API versioning.
L1: Fundamental (Entry-Level / Junior)
# 1. Variables
Q. A developer writes the following code inside a function. What is the output?
function test() {
console.log(x);
var x = 5;
console.log(x);
}
test();
- A)
ReferenceError: x is not defined,5 - B)
undefined,5 - C)
5,5 - D)
null,5
Answer & Explanation
**Answer: B) `undefined`, `5`** **Explanation:** `var` declarations are hoisted to the top of their function scope and initialized to `undefined`. So `console.log(x)` before assignment prints `undefined`, and after assignment it prints `5`.Q. A team decides to use let instead of var for all loop counters. What happens when this code runs?
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
- A)
3,3,3 - B)
0,1,2 - C)
undefined,undefined,undefined - D)
ReferenceError
Answer & Explanation
**Answer: B) `0`, `1`, `2`** **Explanation:** `let` creates a new binding for each iteration of the loop due to block scoping. Each `setTimeout` callback captures its own `i` value. With `var`, the output would be `3, 3, 3`.Q. A developer writes a configuration object that should never be reassigned. Which declaration is most appropriate and what happens if you try to modify it?
const config = { apiUrl: "https://api.example.com" };
config.apiUrl = "https://new-api.com";
console.log(config.apiUrl);
- A) Throws a
TypeError—constprevents all mutations - B) Logs
"https://new-api.com"—constonly prevents reassignment, not mutation - C) Logs
"https://api.example.com"—constsilently ignores the assignment - D) Throws a
SyntaxErrorat declaration
Answer & Explanation
**Answer: B) Logs `"https://new-api.com"` — `const` only prevents reassignment, not mutation** **Explanation:** `const` prevents rebinding (e.g., `config = {}` would throw), but the object\'s properties can still be changed. To truly freeze an object, use `Object.freeze(config)`.Q. What is logged to the console?
let a = 1;
let b = a;
b = 2;
console.log(a);
- A)
2 - B)
1 - C)
undefined - D)
null
Answer & Explanation
**Answer: B) `1`** **Explanation:** Primitive values like numbers are copied by value. Assigning `b = a` creates an independent copy, so modifying `b` has no effect on `a`.Q. What happens when you access a let variable before its declaration inside a block?
{
console.log(typeof myVar);
console.log(typeof myLet);
var myVar = 'var';
let myLet = 'let';
}
- A)
"undefined","undefined" - B)
"undefined",ReferenceError - C)
"string","string" - D)
"var","let"
Answer & Explanation
**Answer: B) `"undefined"`, `ReferenceError`** **Explanation:** `var` is hoisted and initialized to `undefined`, so `typeof myVar` safely returns `"undefined"`. `let` is hoisted but NOT initialized — it sits in the Temporal Dead Zone. Accessing it before the declaration line throws a `ReferenceError: Cannot access 'myLet' before initialization`.Q. What happens when you attempt to reassign a const variable?
const PI = 3.14;
try {
PI = 3.14159;
} catch (e) {
console.log(e instanceof TypeError);
}
console.log(PI);
- A)
false,3.14159 - B)
true,3.14 - C)
false,3.14 - D)
true,3.14159
Answer & Explanation
**Answer: B) `true`, `3.14`** **Explanation:** Attempting to reassign a `const` binding throws a `TypeError`. The `catch` block confirms `e instanceof TypeError` is `true`. `PI` remains `3.14` because the assignment failed. Remember: `const` prevents rebinding but does not make objects immutable.Q. What is the scope of var versus let in a block?
{
var blockVar = 'I am var';
let blockLet = 'I am let';
}
console.log(typeof blockVar);
console.log(typeof blockLet);
- A)
"string","string" - B)
"undefined","undefined" - C)
"string","undefined" - D)
ReferenceErroron both
Answer & Explanation
**Answer: C) `"string"`, `"undefined"`** **Explanation:** `var` is function-scoped (or global if not inside a function), so `blockVar` leaks outside the block. `let` is block-scoped — it does not exist outside `{}`. `typeof blockLet` returns `"undefined"` (not a ReferenceError) because `typeof` on an undeclared name is safe.Q. A developer chains multiple assignments. What is logged?
let x, y, z;
x = y = z = 10;
console.log(x, y, z);
z = 99;
console.log(x, y, z);
- A)
10 10 10, then99 99 99 - B)
10 10 10, then10 10 99 - C)
undefined undefined undefined, then10 10 99 - D)
10 10 10, then99 10 10
Answer & Explanation
**Answer: B) `10 10 10`, then `10 10 99`** **Explanation:** Assignment evaluates right-to-left: `z = 10`, `y = 10`, `x = 10`. All three are independently assigned the value `10`. They are not references to each other. Changing `z` to `99` does not affect `x` or `y`.Q. What happens when a variable is assigned without a declaration keyword inside a function?
function createGlobal() {
implicitGlobal = 'I am global';
}
createGlobal();
console.log(typeof implicitGlobal);
console.log(implicitGlobal);
- A)
"undefined",ReferenceError - B)
"string","I am global" - C)
ReferenceErroron both - D)
"string",undefined
Answer & Explanation
**Answer: B) `"string"`, `"I am global"`** **Explanation:** Assigning to an undeclared variable in non-strict mode creates an implicit global variable. This is a dangerous anti-pattern and a common source of bugs. In strict mode (`"use strict"`), this throws a `ReferenceError`. Always declare variables with `let`, `const`, or `var`.Q. What does variable shadowing produce in nested scopes?
const value = 'outer';
function outer() {
const value = 'middle';
function inner() {
const value = 'inner';
console.log(value);
}
inner();
console.log(value);
}
outer();
console.log(value);
- A)
"outer","outer","outer" - B)
"inner","middle","outer" - C)
"inner","inner","inner" - D)
"outer","middle","inner"
Answer & Explanation
**Answer: B) `"inner"`, `"middle"`, `"outer"`** **Explanation:** Each `const value` creates a new binding that shadows outer ones within its own scope. `inner()` logs its own `"inner"`, the `outer()` function\'s log sees `"middle"`, and the global log sees `"outer"`. Each scope resolves names by walking up the scope chain.Q. A developer uses var in a for loop with setTimeout. What is logged?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
- A)
0,1,2 - B)
3,3,3 - C)
0,0,0 - D)
undefined,undefined,undefined
Answer & Explanation
**Answer: B) `3`, `3`, `3`** **Explanation:** `var` is function-scoped — all three callbacks close over the same `i`. By the time the `setTimeout` callbacks run (after the loop completes), `i` has been incremented to `3`. Replace `var` with `let` to create a new block-scoped binding per iteration (outputs `0, 1, 2`).Q. Which of the following identifiers is valid in JavaScript?
const $price = 100; // A
const _temp = 200; // B
const 2fast = 300; // C
const über = 400; // D
- A) Only
$priceis valid - B)
$price,_temp, andüberare valid;2fastis aSyntaxError - C) Only
$priceand_tempare valid - D) Only identifiers using ASCII characters are valid
Answer & Explanation
**Answer: B) `$price`, `_temp`, and `über` are valid; `2fast` is a `SyntaxError`** **Explanation:** Identifiers must start with a Unicode letter, `_`, or `$`. Digits are not allowed as the first character. JavaScript supports Unicode identifiers, so `über` is valid. `2fast` is a `SyntaxError` because it starts with a digit. Reserved keywords (like `class`, `let`, `return`) also cannot be used as identifiers.Q. How does the typeof operator behave on undeclared variables?
console.log(typeof undeclaredVariable);
console.log(undeclaredVariable === undefined);
- A)
"undefined",true - B)
"undefined",ReferenceError - C)
ReferenceError,ReferenceError - D)
null,false
Answer & Explanation
**Answer: B) `"undefined"`, `ReferenceError`** **Explanation:** `typeof` is the only operator that does NOT throw when used on an undeclared variable — it safely returns `"undefined"`. However, directly referencing an undeclared variable (like `=== undefined`) throws a `ReferenceError`. This makes `typeof` useful for safe feature detection: `if (typeof window !== 'undefined')`.Q. A developer uses array destructuring for a variable swap. What is the output?
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b);
- A)
1 2 - B)
2 1 - C)
undefined undefined - D)
SyntaxError
Answer & Explanation
**Answer: B) `2 1`** **Explanation:** ES6 array destructuring enables an elegant variable swap without a temporary variable. The right side `[b, a]` creates a new array `[2, 1]`, which is then destructured back into `a` and `b`. This is equivalent to the classic `let temp = a; a = b; b = temp`.# 2. Data Types
Q. A developer checks data types using typeof. What does the following code output?
console.log(typeof null);
console.log(typeof undefined);
console.log(typeof NaN);
console.log(typeof function(){});
- A)
"null","undefined","NaN","function" - B)
"object","undefined","number","function" - C)
"null","undefined","number","object" - D)
"object","null","number","function"
Answer & Explanation
**Answer: B) `"object"`, `"undefined"`, `"number"`, `"function"`** **Explanation:** `typeof null === "object"` is a well-known JavaScript bug. `NaN` is of type `"number"`. `undefined` correctly returns `"undefined"`, and functions return `"function"`.Q. Two variables hold values. What does the equality check return?
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2);
- A)
true— the arrays contain the same values - B)
false— arrays are compared by reference, not value - C)
undefined - D) Throws a
TypeError
Answer & Explanation
**Answer: B) `false` — arrays are compared by reference, not value** **Explanation:** Arrays and objects are reference types. `arr1` and `arr2` are two separate objects in memory. Even though their contents are equal, `===` compares memory references, not values.Q. What is the output?
console.log(0 == false);
console.log(0 === false);
console.log("" == false);
console.log("" === false);
- A)
true,true,true,true - B)
true,false,true,false - C)
false,false,true,true - D)
true,true,false,false
Answer & Explanation
**Answer: B) `true`, `false`, `true`, `false`** **Explanation:** `==` performs type coercion: `0 == false` and `"" == false` coerce to the same numeric value (`0`). `===` checks both value and type with no coercion, so `0 === false` and `"" === false` are both `false`.Q. A developer uses Symbol as an object key. What is the output?
const id = Symbol('id');
const user = {
name: 'Alice',
[id]: 12345
};
console.log(user[id]);
console.log(Object.keys(user));
console.log(JSON.stringify(user));
- A)
12345,["name", "id"],'{"name":"Alice","id":12345}' - B)
12345,["name"],'{"name":"Alice"}' - C)
undefined,["name"],'{"name":"Alice"}' - D)
12345,["name"],'{"name":"Alice","Symbol(id)":12345}'
Answer & Explanation
**Answer: B) `12345`, `["name"]`, `'{"name":"Alice"}'`** **Explanation:** Symbol-keyed properties are not enumerable via `Object.keys()`, `for...in`, or serialized by `JSON.stringify()`. They are hidden from most reflection APIs. Use `Object.getOwnPropertySymbols(obj)` to retrieve them. This makes Symbols useful for adding non-colliding metadata to objects.Q. What does typeof null return and why is it misleading?
console.log(typeof null === 'object');
console.log(null instanceof Object);
console.log(null == undefined);
console.log(null === undefined);
- A)
true,true,true,true - B)
true,false,true,false - C)
false,false,true,false - D)
true,false,false,false
Answer & Explanation
**Answer: B) `true`, `false`, `true`, `false`** **Explanation:** `typeof null === "object"` is a well-known JavaScript historical bug. However, `null instanceof Object` is `false` because `instanceof` checks the prototype chain and `null` has none. `null == undefined` is `true` by spec (they are equal with `==`); `null === undefined` is `false` due to different types.Q. What does explicit Boolean() conversion produce for these values?
console.log(Boolean(0));
console.log(Boolean(''));
console.log(Boolean([]));
console.log(Boolean({}));
console.log(Boolean('false'));
- A)
false,false,false,false,false - B)
false,false,true,true,true - C)
false,false,false,true,true - D)
false,false,true,false,false
Answer & Explanation
**Answer: B) `false`, `false`, `true`, `true`, `true`** **Explanation:** `0` and `''` are falsy. Empty arrays `[]` and empty objects `{}` are **truthy** — they are object references, and all non-null objects are truthy. `'false'` is a non-empty string and therefore truthy. This catches many developers off guard when checking for empty collections.Q. A developer uses Number() for explicit conversions. What is the output?
console.log(Number(true));
console.log(Number(false));
console.log(Number(null));
console.log(Number(undefined));
console.log(Number(' 42 '));
- A)
1,0,0,0,42 - B)
1,0,0,NaN,42 - C)
1,0,null,NaN,NaN - D)
1,0,0,NaN,NaN
Answer & Explanation
**Answer: B) `1`, `0`, `0`, `NaN`, `42`** **Explanation:** `Number(true)` → `1`, `Number(false)` → `0`. `Number(null)` → `0`. `Number(undefined)` → `NaN`. `Number(' 42 ')` → `42` — `Number()` trims whitespace before parsing. Knowing these rules prevents bugs when doing arithmetic with mixed-type data.Q. What is the output of these string-to-number conversion methods?
console.log(String(null));
console.log(String(undefined));
console.log(String(true));
console.log((1234).toString(16));
console.log((255).toString(2));
- A)
"null","undefined","true","4d2","11111111" - B)
null,undefined,true,"4d2","11111111" - C)
"","","1","4d2","11111111" - D)
"null","undefined","true","4d2","ff"
Answer & Explanation
**Answer: A) `"null"`, `"undefined"`, `"true"`, `"4d2"`, `"11111111"`** **Explanation:** `String()` converts `null` and `undefined` to their literal string representations. `.toString(16)` converts `1234` to hexadecimal `"4d2"`. `.toString(2)` converts `255` to binary `"11111111"`. The radix argument allows base conversion from 2 to 36.Q. What does Object.is() return compared to === for these edge cases?
console.log(Object.is(NaN, NaN));
console.log(Object.is(+0, -0));
console.log(Object.is(null, null));
console.log(Object.is(undefined, undefined));
- A)
false,true,true,true - B)
true,false,true,true - C)
false,false,true,true - D)
true,true,true,true
Answer & Explanation
**Answer: B) `true`, `false`, `true`, `true`** **Explanation:** `Object.is()` uses the SameValue algorithm. Unlike `===`, it treats `NaN` as equal to itself (`true`) and distinguishes `+0` from `-0` (`false`). `null` is identical to `null`, and `undefined` is identical to `undefined`. Use `Object.is()` when you need precise equality handling.Q. A developer checks if a value is a plain object. What does this function output?
function isPlainObject(value) {
return typeof value === 'object'
&& value !== null
&& !Array.isArray(value);
}
console.log(isPlainObject({}));
console.log(isPlainObject([]));
console.log(isPlainObject(null));
console.log(isPlainObject(new Date()));
- A)
true,false,false,false - B)
true,false,false,true - C)
true,true,false,true - D)
false,false,false,false
Answer & Explanation
**Answer: B) `true`, `false`, `false`, `true`** **Explanation:** The function correctly excludes `null` and arrays, but `new Date()` also passes — it is a non-null, non-array object. For stricter plain-object detection, also check `Object.getPrototypeOf(value) === Object.prototype`. This catches class instances and built-ins.Q. What is the result of these loose equality comparisons?
console.log('' == false);
console.log(0 == '');
console.log(0 == '0');
console.log('' == '0');
- A)
true,true,true,true - B)
true,true,true,false - C)
false,true,true,false - D)
true,false,true,false
Answer & Explanation
**Answer: B) `true`, `true`, `true`, `false`** **Explanation:** `'' == false` → both coerce to `0`, so `true`. `0 == ''` → `''` coerces to `0`, so `true`. `0 == '0'` → `'0'` coerces to `0`, so `true`. `'' == '0'` → string comparison, `""` ≠`"0"`, so `false`. This non-transitivity illustrates why `===` is always preferred.Q. A developer uses parseInt and parseFloat. What is the output?
console.log(parseInt('10.9'));
console.log(parseFloat('10.9'));
console.log(parseInt('0xFF', 16));
console.log(parseInt('010'));
console.log(parseInt('10abc'));
- A)
10,10.9,255,8,10 - B)
10,10.9,255,10,10 - C)
10,10.9,255,8,NaN - D)
10,10,255,10,NaN
Answer & Explanation
**Answer: B) `10`, `10.9`, `255`, `10`, `10`** **Explanation:** `parseInt('10.9')` → `10` (truncates decimal). `parseFloat('10.9')` → `10.9`. `parseInt('0xFF', 16)` → `255`. `parseInt('010')` → `10` (ES5+ defaults to base 10 without explicit radix). `parseInt('10abc')` → `10` (parses until invalid character). `parseInt('0xFF', 16)` works as follows: 1. **Radix 16** tells `parseInt` to interpret the string as a hexadecimal number. 2. **`0x` prefix** is recognized and ignored — it's a standard hex prefix, so parsing begins at `FF`. 3. **`FF` in hex** is calculated as: $$F = 15$$ $$\text{FF} = (15 \times 16^1) + (15 \times 16^0) = 240 + 15 = 255$$ So the result is `255`.Q. What is the output of mapping an array of mixed values through typeof?
const data = [1, 'two', true, null, undefined, {}, []];
const types = data.map(item => typeof item);
console.log(types);
- A)
["number","string","boolean","null","undefined","object","array"] - B)
["number","string","boolean","object","undefined","object","object"] - C)
["number","string","boolean","null","undefined","object","object"] - D)
["number","string","boolean","object","object","object","object"]
Answer & Explanation
**Answer: B) `["number","string","boolean","object","undefined","object","object"]`** **Explanation:** `typeof null` is `"object"` (historical bug). `typeof undefined` is `"undefined"`. `typeof {}` and `typeof []` both return `"object"`. There is no `"null"` or `"array"` typeof result. Use `Array.isArray()` to distinguish arrays and `=== null` to detect null values.# 3. Operators
Q. A developer uses the nullish coalescing operator. What is printed?
let userInput = 0;
let result1 = userInput || "default";
let result2 = userInput ?? "default";
console.log(result1);
console.log(result2);
- A)
"default","default" - B)
0,0 - C)
"default",0 - D)
0,"default"
Answer & Explanation
**Answer: C) `"default"`, `0`** **Explanation:** `||` returns the right side for any falsy value (including `0`, `""`, `false`). `??` (nullish coalescing) only returns the right side when the left is `null` or `undefined`. Since `0` is not `null`/`undefined`, `??` returns `0`.Q. What is the result of the following expressions?
console.log(5 + "3");
console.log(5 - "3");
console.log(true + true);
console.log(true + false);
- A)
"53",2,2,1 - B)
8,2,2,1 - C)
"53","53",2,1 - D)
8,"53",2,0
Answer & Explanation
**Answer: A) `"53"`, `2`, `2`, `1`** **Explanation:** `+` with a string triggers concatenation: `5 + "3" = "53"`. `-` does not concatenate — it coerces `"3"` to a number: `5 - 3 = 2`. Booleans coerce to `0` or `1` in arithmetic: `true + true = 2`, `true + false = 1`.Q. What does the short-circuit evaluation return?
let a = null;
let b = a && a.name;
let c = a || "Anonymous";
console.log(b);
console.log(c);
- A)
TypeError,"Anonymous" - B)
null,"Anonymous" - C)
undefined,null - D)
null,null
Answer & Explanation
**Answer: B) `null`, `"Anonymous"`** **Explanation:** `&&` short-circuits at the first falsy value (`null`), so `b = null` without evaluating `a.name`. `||` short-circuits at the first truthy value; since `a` is `null` (falsy), it evaluates to `"Anonymous"`.Q. What does the optional chaining operator return when properties are missing?
const config = {
database: { host: 'localhost' }
};
console.log(config?.database?.host);
console.log(config?.cache?.host);
console.log(config?.cache?.host ?? 'default-host');
- A)
"localhost",null,"default-host" - B)
"localhost",undefined,"default-host" - C)
"localhost",TypeError,"default-host" - D)
"localhost",undefined,undefined
Answer & Explanation
**Answer: B) `"localhost"`, `undefined`, `"default-host"`** **Explanation:** `?.` returns `undefined` (rather than throwing) when a property access would fail due to `null` or `undefined`. `config?.cache` is `undefined`, so `?.host` is also `undefined`. The `??` operator returns `"default-host"` since `undefined` is nullish.Q. What does the logical AND assignment operator &&= do?
let a = 1;
let b = 0;
let c = null;
a &&= 99;
b &&= 99;
c &&= 99;
console.log(a, b, c);
- A)
99,99,99 - B)
99,0,null - C)
1,0,null - D)
99,0,99
Answer & Explanation
**Answer: B) `99`, `0`, `null`** **Explanation:** `&&=` only assigns the right side if the left side is **truthy**. `a = 1` (truthy) → assigned `99`. `b = 0` (falsy) → not assigned, stays `0`. `c = null` (falsy) → not assigned, stays `null`. This is shorthand for `a = a && 99`.Q. What is the result of these bitwise operations?
console.log(5 & 3);
console.log(5 | 3);
console.log(5 ^ 3);
console.log(~5);
console.log(5 << 1);
console.log(20 >> 2);
- A)
1,7,6,-6,10,5 - B)
1,7,6,-5,10,5 - C)
0,7,6,-6,10,5 - D)
1,7,6,-6,20,5
Answer & Explanation
**Answer: A) `1`, `7`, `6`, `-6`, `10`, `5`** **Explanation:** `5 & 3`: `101 & 011 = 001 = 1`. `5 | 3`: `101 | 011 = 111 = 7`. `5 ^ 3`: `101 ^ 011 = 110 = 6`. `~5`: bitwise NOT (two\'s complement) = `-(5+1) = -6`. `5 << 1` = `10`. `20 >> 2` = `5`.Q. What does the instanceof operator check?
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog);
console.log(dog instanceof Animal);
console.log(dog instanceof Object);
console.log([] instanceof Array);
- A)
true,false,false,true - B)
true,true,true,true - C)
true,true,false,true - D)
false,false,true,true
Answer & Explanation
**Answer: B) `true`, `true`, `true`, `true`** **Explanation:** `instanceof` walks the prototype chain. `dog` is an instance of `Dog`, `Animal` (through inheritance), and `Object` (all objects ultimately inherit from `Object.prototype`). `[]` is an instance of `Array`. Every object is an instance of `Object`.Q. What does the ternary operator return in this chained grading example?
function classify(score) {
return score >= 90 ? 'A'
: score >= 80 ? 'B'
: score >= 70 ? 'C'
: score >= 60 ? 'D'
: 'F';
}
console.log(classify(85));
console.log(classify(55));
- A)
'A','F' - B)
'B','D' - C)
'B','F' - D)
'A','D'
Answer & Explanation
**Answer: C) `'B'`, `'F'`** **Explanation:** Chained ternaries act like if/else if chains. `85 >= 90` is false, `85 >= 80` is true → `'B'`. For `55`: all conditions fail → `'F'`. This pattern is readable for simple grading/classification but should be avoided for complex multi-branch logic.Q. What does the delete operator do to an object property?
const obj = { a: 1, b: 2, c: 3 };
console.log(delete obj.b);
console.log(obj);
console.log(delete obj.nonExistent);
- A)
false,{ a:1, c:3 },false - B)
true,{ a:1, c:3 },true - C)
true,{ a:1, b:undefined, c:3 },true - D)
undefined,{ a:1, c:3 },undefined
Answer & Explanation
**Answer: B) `true`, `{ a:1, c:3 }`, `true`** **Explanation:** `delete` removes a property from an object and returns `true` on success. It also returns `true` when the property doesn\'t exist. It completely removes the property (does not set it to `undefined`). `delete` on `var`/`let`/`const` variables returns `false` (those cannot be deleted).Q. What is the output of the void operator?
console.log(void 0);
console.log(void 'anything');
console.log(void (2 + 2));
console.log(typeof void 0);
- A)
0,"anything",4,"number" - B)
undefined,undefined,undefined,"undefined" - C)
null,null,null,"object" - D)
0,null,undefined,"undefined"
Answer & Explanation
**Answer: B) `undefined`, `undefined`, `undefined`, `"undefined"`** **Explanation:** The `void` operator evaluates its operand expression and always returns `undefined`. `void 0` is a common idiom for getting `undefined` reliably (in older environments where `undefined` could be overwritten). `typeof void 0` returns `"undefined"`.Q. What is the output given operator precedence and associativity?
console.log(2 + 3 * 4);
console.log((2 + 3) * 4);
console.log(2 ** 3 ** 2);
console.log(true + true * 2);
- A)
14,20,512,3 - B)
20,20,64,4 - C)
14,20,64,3 - D)
14,20,512,4
Answer & Explanation
**Answer: A) `14`, `20`, `512`, `3`** **Explanation:** Multiplication before addition: `2 + (3*4) = 14`. With parentheses: `(2+3)*4 = 20`. `**` is **right-associative**: `2 ** (3 ** 2) = 2 ** 9 = 512`, NOT `8 ** 2 = 64`. `true*2 = 2`, `true + 2 = 3`.Q. What does short-circuit evaluation return in this side-effect example?
let count = 0;
const inc = () => ++count;
const a = false && inc();
const b = true || inc();
const c = null ?? inc();
console.log(count, a, b, c);
- A)
1,false,true,1 - B)
0,false,true,1 - C)
1,false,true,null - D)
2,false,true,2
Answer & Explanation
**Answer: B) `0`, `false`, `true`, `1`** **Explanation:** `false && inc()` short-circuits — `inc()` is NOT called. `true || inc()` short-circuits — `inc()` is NOT called. `null ?? inc()` — `null` IS nullish, so `inc()` IS called and `count` becomes `1`. Only one actual call occurs.Q. What does the in operator check in an object?
const car = { make: 'Toyota', model: 'Corolla' };
console.log('make' in car);
console.log('price' in car);
console.log('toString' in car);
console.log('make' in Object.create(car));
- A)
true,false,false,false - B)
true,false,true,true - C)
true,false,true,false - D)
true,true,true,true
Answer & Explanation
**Answer: B) `true`, `false`, `true`, `true`** **Explanation:** The `in` operator checks if a property exists on an object **or its prototype chain**. `"make"` is an own property → `true`. `"price"` doesn\'t exist → `false`. `"toString"` exists on `Object.prototype` → `true`. An object created with `Object.create(car)` inherits `car`\'s properties, so `"make" in child` is also `true`.# 4. Numbers
Q. A developer needs to format a price to two decimal places. What is the output?
let price = 19.5;
console.log(price.toFixed(2));
console.log(typeof price.toFixed(2));
- A)
19.50,"number" - B)
"19.50","string" - C)
19.5,"number" - D)
"19.5","string"
Answer & Explanation
**Answer: B) `"19.50"`, `"string"`** **Explanation:** `.toFixed()` returns a **string**, not a number. This is a common source of bugs when developers expect a number back and then attempt arithmetic.Q. What is the output of this floating-point operation?
console.log(0.1 + 0.2 === 0.3);
console.log(0.1 + 0.2);
- A)
true,0.3 - B)
false,0.30000000000000004 - C)
false,0.3 - D)
true,0.30000000000000004
Answer & Explanation
**Answer: B) `false`, `0.30000000000000004`** **Explanation:** JavaScript uses IEEE 754 floating-point arithmetic, which cannot represent some decimal fractions exactly. The result `0.1 + 0.2` is `0.30000000000000004`. Use `Number.EPSILON` for safe comparison: `Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON`.Q. What is Number.MAX_SAFE_INTEGER and what happens beyond it?
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2);
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER));
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1));
- A)
9007199254740991,false,true,true - B)
9007199254740991,true,true,false - C)
9007199254740992,true,true,false - D)
9007199254740991,false,true,false
Answer & Explanation
**Answer: B) `9007199254740991`, `true`, `true`, `false`** **Explanation:** `Number.MAX_SAFE_INTEGER` is `2^53 - 1 = 9007199254740991`. Beyond this value integers cannot be represented exactly. `MAX_SAFE_INTEGER + 1` equals `MAX_SAFE_INTEGER + 2` (they map to the same float), returning `true`. `Number.isSafeInteger()` returns `false` for the overflow value.Q. What do the Math rounding methods return for negative numbers?
console.log(Math.round(4.5));
console.log(Math.round(-4.5));
console.log(Math.ceil(-4.1));
console.log(Math.floor(-4.1));
console.log(Math.trunc(-4.9));
- A)
5,-4,-4,-5,-4 - B)
5,-5,-4,-5,-4 - C)
5,-4,-5,-5,-5 - D)
4,-5,-4,-4,-4
Answer & Explanation
**Answer: A) `5`, `-4`, `-4`, `-5`, `-4`** **Explanation:** `Math.round(4.5)` → `5`. `Math.round(-4.5)` → `-4` (rounds toward +∞). `Math.ceil(-4.1)` → `-4` (rounds toward +∞). `Math.floor(-4.1)` → `-5` (rounds toward -∞). `Math.trunc(-4.9)` → `-4` (removes decimal part, rounds toward zero).Q. What is the output of arithmetic with Infinity and NaN?
console.log(1 / 0);
console.log(-1 / 0);
console.log(Infinity - Infinity);
console.log(0 / 0);
console.log(isFinite(Infinity));
- A)
Infinity,-Infinity,0,NaN,false - B)
Infinity,-Infinity,NaN,NaN,false - C)
Error,-Error,0,NaN,false - D)
Infinity,-Infinity,Infinity,0,true
Answer & Explanation
**Answer: B) `Infinity`, `-Infinity`, `NaN`, `NaN`, `false`** **Explanation:** Division by zero in JavaScript produces `Infinity` (not an error). `Infinity - Infinity` is an indeterminate form → `NaN`. `0/0` → `NaN`. `isFinite(Infinity)` → `false`. JavaScript arithmetic never throws for overflow or division by zero.Q. What do Number.isInteger and Number.isFinite return compared to global equivalents?
console.log(Number.isInteger(5.0));
console.log(Number.isInteger(5.5));
console.log(Number.isFinite(1 / 0));
console.log(Number.isFinite(42));
console.log(isFinite('42'));
- A)
true,false,false,true,false - B)
true,false,false,true,true - C)
false,false,false,true,true - D)
true,false,true,true,true
Answer & Explanation
**Answer: B) `true`, `false`, `false`, `true`, `true`** **Explanation:** `Number.isInteger(5.0)` → `true` (5.0 is mathematically an integer). `Number.isFinite(Infinity)` → `false`. `Number.isFinite(42)` → `true`. The global `isFinite('42')` → `true` because it **coerces** its argument to a number first, unlike `Number.isFinite` which strictly checks the type.Q. What does parseInt return for non-decimal strings with radix?
console.log(parseInt('11', 2));
console.log(parseInt('ff', 16));
console.log(parseInt('077', 8));
console.log(parseInt('z', 36));
- A)
3,255,63,35 - B)
11,255,77,35 - C)
3,255,77,35 - D)
3,15,63,26
Answer & Explanation
**Answer: A) `3`, `255`, `63`, `35`** **Explanation:** `parseInt('11', 2)` converts binary `11` → decimal `3`. `parseInt('ff', 16)` converts hex `ff` → `255`. `parseInt('077', 8)` converts octal `077` → `63`. `parseInt('z', 36)` converts base-36 `z` → `35`. Always provide the radix parameter to avoid unexpected behavior.Q. What is the output of Math utility methods?
console.log(Math.abs(-5));
console.log(Math.max(1, 3, 2));
console.log(Math.min(...[4, 2, 7]));
console.log(Math.pow(2, 8));
console.log(Math.sqrt(144));
- A)
5,3,2,256,12 - B)
-5,3,2,256,12 - C)
5,1,7,256,12 - D)
5,3,4,256,144
Answer & Explanation
**Answer: A) `5`, `3`, `2`, `256`, `12`** **Explanation:** `Math.abs(-5)` → `5`. `Math.max(1,3,2)` → `3`. `Math.min(...[4,2,7])` → `2` (spread expands the array into individual arguments). `Math.pow(2,8) = 256` (equivalent to `2**8`). `Math.sqrt(144) = 12`.Q. What does toPrecision return versus toFixed?
const n = 123.456;
console.log(n.toFixed(2));
console.log(n.toPrecision(5));
console.log(n.toPrecision(2));
console.log(typeof n.toFixed(2));
- A)
"123.46","123.46","1.2e+2","string" - B)
"123.46","123.46","120","number" - C)
"123.46","123.46","1.2e+2","number" - D)
123.46,123.46,120,"string"
Answer & Explanation
**Answer: A) `"123.46"`, `"123.46"`, `"1.2e+2"`, `"string"`** **Explanation:** `toFixed(2)` formats to 2 decimal places → `"123.46"`. `toPrecision(5)` uses 5 significant digits → `"123.46"`. `toPrecision(2)` uses only 2 significant digits → `"1.2e+2"` (scientific notation). Both return **strings**, not numbers.Q. What is the output of NaN comparisons and detection?
const x = NaN;
console.log(x === x);
console.log(x !== x);
console.log(Number.isNaN(x));
console.log(Number.isNaN('hello'));
console.log(isNaN('hello'));
- A)
false,true,true,false,true - B)
true,false,true,false,true - C)
false,true,true,true,true - D)
false,false,true,false,false
Answer & Explanation
**Answer: A) `false`, `true`, `true`, `false`, `true`** **Explanation:** `NaN` is the only value not equal to itself. `x === x` → `false`, `x !== x` → `true` (idiomatic NaN check). `Number.isNaN(NaN)` → `true` (no coercion). `Number.isNaN('hello')` → `false` (it\'s a string, not NaN). Global `isNaN('hello')` → `true` (coerces `'hello'` to `NaN` first — misleading).Q. What does Math.random() guarantee about its output?
const values = Array.from({ length: 1000 }, () => Math.random());
const allInRange = values.every(v => v >= 0 && v < 1);
console.log(allInRange);
// To get random int 0-9:
const rand = Math.floor(Math.random() * 10);
- A)
false—Math.random()can return1 - B)
true—Math.random()returns values in[0, 1): inclusive of 0, exclusive of 1 - C)
true—Math.random()returns values in(0, 1): exclusive of both ends - D)
true—Math.random()returns values in[0, 1]: inclusive of both ends
Answer & Explanation
**Answer: B) `true` — `Math.random()` returns values in `[0, 1)`: inclusive of 0, exclusive of 1** **Explanation:** `Math.random()` returns a pseudo-random float in `[0, 1)` — zero is possible but `1` is never returned. `Math.floor(Math.random() * 10)` produces integers from `0` to `9` uniformly. For cryptographic use, use `crypto.getRandomValues()` instead.Q. A developer formats numbers for display. What is the output?
const price = 1234567.891;
console.log(price.toLocaleString('en-US'));
console.log(price.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}));
- A)
"1234567.891","USD 1234567.89" - B)
"1,234,567.891","$1,234,567.89" - C)
"1.234.567,891","$1,234,567.89" - D)
"1,234,567.891","1,234,567.89 USD"
Answer & Explanation
**Answer: B) `"1,234,567.891"`, `"$1,234,567.89"`** **Explanation:** `toLocaleString('en-US')` formats with US locale conventions (comma thousands separator, dot decimal). The `currency` style adds the `$` symbol and applies the fraction digit constraints. Output may vary by environment, but this is the standard en-US format.# 5. String
Q. A developer needs to extract a file extension. Which snippet correctly gets "pdf" from "report.pdf"?
let filename = "report.pdf";
- A)
filename.slice(-3) - B)
filename.substring(filename.lastIndexOf(".") + 1) - C)
filename.split(".")[1] - D) All of the above
Answer & Explanation
**Answer: D) All of the above** **Explanation:** All three work for `"report.pdf"`: `.slice(-3)` gets the last 3 characters; `.lastIndexOf(".") + 1` finds the extension after the last dot; `.split(".")[1]` splits on dot and gets the second part. However, `.lastIndexOf` is most robust for filenames with multiple dots.Q. What does the following string operation produce?
let str = "Hello, World!";
console.log(str.indexOf("World"));
console.log(str.includes("world"));
console.log(str.toLowerCase().includes("world"));
- A)
7,true,true - B)
7,false,true - C)
-1,false,true - D)
7,false,false
Answer & Explanation
**Answer: B) `7`, `false`, `true`** **Explanation:** `.indexOf("World")` returns `7` (the index where `"World"` starts). `.includes()` is case-sensitive, so `"world"` is not found. After `.toLowerCase()`, `"world"` is found.Q. What do padStart and padEnd return?
const num = '42';
console.log(num.padStart(5, '0'));
console.log(num.padEnd(5, '*'));
console.log('hello'.padStart(3));
- A)
"00042","42***","hello" - B)
"00042","42***","hel" - C)
"42000","***42","hello" - D)
"00042","42 ","hello"
Answer & Explanation
**Answer: A) `"00042"`, `"42***"`, `"hello"`** **Explanation:** `padStart(5, '0')` pads from the left to reach length 5 → `"00042"`. `padEnd(5, '*')` pads from the right → `"42***"`. If the string is already >= the target length, it is returned unchanged — `'hello'` has length 5, which is >= 3, so it is returned as-is.Q. What does replaceAll return compared to replace?
const text = 'cat and cat and cat';
console.log(text.replace('cat', 'dog'));
console.log(text.replaceAll('cat', 'dog'));
console.log(text.replace(/cat/g, 'dog'));
- A)
"dog and cat and cat","dog and dog and dog","dog and cat and cat" - B)
"dog and cat and cat","dog and dog and dog","dog and dog and dog" - C)
"dog and dog and dog","dog and dog and dog","dog and dog and dog" - D)
"cat and cat and cat","dog and dog and dog","dog and dog and dog"
Answer & Explanation
**Answer: B) `"dog and cat and cat"`, `"dog and dog and dog"`, `"dog and dog and dog"`** **Explanation:** `String.replace(string)` only replaces the **first** occurrence. `replaceAll(string)` replaces all occurrences (ES2021). `replace(/pattern/g)` with the global regex flag also replaces all. Both `replaceAll` and `replace` with `/g` produce the same result.Q. What does split return for these inputs?
console.log('a,b,c'.split(','));
console.log('hello'.split(''));
console.log('a,,b'.split(','));
console.log('abc'.split('', 2));
- A)
["a","b","c"],["h","e","l","l","o"],["a","b"],["a","b"] - B)
["a","b","c"],["h","e","l","l","o"],["a","","b"],["a","b"] - C)
["a","b","c"],["hello"],["a","","b"],["a","b"] - D)
["a,b,c"],["h","e","l","l","o"],["a","","b"],["a","b","c"]
Answer & Explanation
**Answer: B) `["a","b","c"]`, `["h","e","l","l","o"]`, `["a","","b"]`, `["a","b"]`** **Explanation:** `split(',')` splits on commas. `split('')` splits every character. `'a,,b'.split(',')` keeps the empty string between consecutive delimiters. The second argument to `split` is a limit on the number of results — `split('', 2)` returns only the first 2 characters.Q. Are strings mutable in JavaScript?
let str = 'hello';
str[0] = 'H';
console.log(str);
str = str.charAt(0).toUpperCase() + str.slice(1);
console.log(str);
- A)
"Hello","Hello" - B)
"hello","Hello" - C)
"Hello","hello" - D)
TypeError— strings cannot be indexed
Answer & Explanation
**Answer: B) `"hello"`, `"Hello"`** **Explanation:** Strings are **immutable** in JavaScript. Assigning to `str[0]` silently fails (in non-strict mode) or throws in strict mode — the original string is unchanged. To transform a string, use methods that return new strings (like `toUpperCase()`, `slice()`, or `replace()`).Q. What does the repeat method return?
console.log('ab'.repeat(3));
console.log('x'.repeat(0));
console.log('-'.repeat(10));
- A)
"ababab","","----------" - B)
"ababababababab","x","----------" - C)
"ababab",undefined,"----------" - D)
"ab3","","-10"
Answer & Explanation
**Answer: A) `"ababab"`, `""`, `"----------"`** **Explanation:** `repeat(n)` returns a new string with the original repeated `n` times. `repeat(0)` returns an empty string. `repeat()` is useful for creating padding strings, separators, or test data without loops.Q. What does the at() method return?
const str = 'Hello';
console.log(str.at(0));
console.log(str.at(-1));
console.log(str.at(-2));
console.log(str.at(10));
- A)
"H","o","l",undefined - B)
"H","H","e",undefined - C)
"H",undefined,"l",undefined - D)
"H","o","l",""
Answer & Explanation
**Answer: A) `"H"`, `"o"`, `"l"`, `undefined`** **Explanation:** `at(0)` returns the first character. `at(-1)` returns the last character (index from end). `at(-2)` returns the second-to-last. `at(10)` returns `undefined` for out-of-range indices. `at()` is the modern alternative to `str[str.length - 1]` for negative indexing.Q. What does startsWith and endsWith return?
const url = 'https://api.example.com/users';
console.log(url.startsWith('https'));
console.log(url.endsWith('/users'));
console.log(url.startsWith('api', 8));
console.log(url.includes('example'));
- A)
true,true,false,true - B)
true,true,true,true - C)
false,true,true,true - D)
true,false,true,true
Answer & Explanation
**Answer: B) `true`, `true`, `true`, `true`** **Explanation:** `startsWith('https')` → `true`. `endsWith('/users')` → `true`. `startsWith('api', 8)` — the second argument is the start position, so it checks from index 8 where `'api'` begins → `true`. `includes('example')` → `true`. All these methods are case-sensitive.Q. What do trimStart, trimEnd, and trim return?
const padded = ' hello world ';
console.log(padded.trim().length);
console.log(padded.trimStart().endsWith(' '));
console.log(padded.trimEnd().startsWith(' '));
- A)
11,false,false - B)
11,true,true - C)
15,true,true - D)
11,true,false
Answer & Explanation
**Answer: B) `11`, `true`, `true`** **Explanation:** `trim()` removes whitespace from both ends → `'hello world'` (length 11). `trimStart()` only removes leading whitespace → `'hello world '` (still ends with `' '` → `true`). `trimEnd()` only removes trailing whitespace → `' hello world'` (still starts with `' '` → `true`).Q. What does string comparison with localeCompare return?
console.log('apple'.localeCompare('banana'));
console.log('banana'.localeCompare('apple'));
console.log('apple'.localeCompare('apple'));
const fruits = ['banana', 'apple', 'cherry'];
console.log(fruits.sort((a, b) => a.localeCompare(b)));
- A)
-1,1,0,["apple","banana","cherry"] - B)
1,-1,0,["apple","banana","cherry"] - C)
-1,1,1,["banana","apple","cherry"] - D)
-1,1,0,["banana","apple","cherry"]
Answer & Explanation
**Answer: A) `-1`, `1`, `0`, `["apple","banana","cherry"]`** **Explanation:** `localeCompare` returns negative if the string comes before the argument alphabetically, positive if after, and `0` if equal. It\'s the recommended way to sort strings because it handles locale-specific rules (accents, special characters) correctly.Q. What does String.fromCharCode and charCodeAt return?
console.log('A'.charCodeAt(0));
console.log('a'.charCodeAt(0));
console.log(String.fromCharCode(72, 101, 108, 108, 111));
- A)
65,97,"Hello" - B)
65,97,"72101108108111" - C)
64,96,"Hello" - D)
65,97,"HELLO"
Answer & Explanation
**Answer: A) `65`, `97`, `"Hello"`** **Explanation:** `'A'.charCodeAt(0)` → `65` (Unicode code point for uppercase A). `'a'.charCodeAt(0)` → `97` (lowercase a). `String.fromCharCode(72, 101, 108, 108, 111)` converts code points back to a string: H=72, e=101, l=108, l=108, o=111 → `"Hello"`. Useful for encoding/decoding ASCII data.# 6. Array
Q. A developer manages a task queue. What is the output after these operations?
let queue = ["task1", "task2", "task3"];
queue.push("task4");
let first = queue.shift();
console.log(first);
console.log(queue.length);
- A)
"task1",3 - B)
"task4",3 - C)
"task1",4 - D)
"task4",4
Answer & Explanation
**Answer: A) `"task1"`, `3`** **Explanation:** `.push()` adds `"task4"` to the end (array is now length 4). `.shift()` removes and returns the **first** element (`"task1"`), leaving 3 elements.Q. What is the output?
let nums = [1, 2, 3, 4, 5];
let sliced = nums.slice(1, 3);
let spliced = nums.splice(1, 2);
console.log(sliced);
console.log(spliced);
console.log(nums);
- A)
[2, 3],[2, 3],[1, 4, 5] - B)
[2, 3],[2, 3],[1, 2, 3, 4, 5] - C)
[1, 2, 3],[2, 3],[1, 4, 5] - D)
[2, 3],[1, 2],[3, 4, 5]
Answer & Explanation
**Answer: A) `[2, 3]`, `[2, 3]`, `[1, 4, 5]`** **Explanation:** `.slice(1, 3)` returns elements at indices 1 and 2 (`[2, 3]`) **without** modifying the original. `.splice(1, 2)` removes 2 elements starting at index 1 (`[2, 3]`) and **modifies** the original array, leaving `[1, 4, 5]`.Q. What does Array.from return for these inputs?
console.log(Array.from('hello'));
console.log(Array.from({ length: 3 }, (_, i) => i * 2));
console.log(Array.from(new Set([1, 2, 2, 3])));
- A)
["h","e","l","l","o"],[0,2,4],[1,2,3] - B)
["hello"],[0,2,4],[1,2,2,3] - C)
["h","e","l","l","o"],[0,1,2],[1,2,3] - D)
["h","e","l","l","o"],[0,2,4],[1,2,2,3]
Answer & Explanation
**Answer: A) `["h","e","l","l","o"]`, `[0,2,4]`, `[1,2,3]`** **Explanation:** `Array.from('hello')` splits the string into characters. `Array.from({length:3}, fn)` creates an array of length 3 using the mapping function — `(_, i)` gives indices 0, 1, 2, doubled to `0, 2, 4`. `Array.from(new Set([...]))` converts a Set to an array, deduplicating values.Q. What does find return versus findIndex?
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' }
];
console.log(users.find(u => u.id === 2));
console.log(users.findIndex(u => u.id === 2));
console.log(users.find(u => u.id === 99));
console.log(users.findIndex(u => u.id === 99));
- A)
{id:2,name:"Bob"},1,null,-1 - B)
{id:2,name:"Bob"},1,undefined,-1 - C)
{id:2,name:"Bob"},2,undefined,-1 - D)
2,1,undefined,-1
Answer & Explanation
**Answer: B) `{id:2,name:"Bob"}`, `1`, `undefined`, `-1`** **Explanation:** `find()` returns the first matching element (or `undefined` if none). `findIndex()` returns the index of the first match (or `-1` if not found). Both stop iteration when a match is found. They accept a callback, unlike `indexOf()` which only checks strict equality.Q. What does every and some return?
const nums = [2, 4, 6, 8, 9];
console.log(nums.every(n => n % 2 === 0));
console.log(nums.some(n => n % 2 !== 0));
console.log([].every(n => n > 100));
console.log([].some(n => n > 100));
- A)
false,true,false,false - B)
false,true,true,false - C)
true,true,true,false - D)
false,false,true,false
Answer & Explanation
**Answer: B) `false`, `true`, `true`, `false`** **Explanation:** `every` returns `false` because `9` is odd. `some` returns `true` because `9` is odd. `[].every(fn)` returns `true` for empty arrays (vacuous truth — no elements fail the test). `[].some(fn)` returns `false` for empty arrays (no elements satisfy the condition).Q. What does flatMap return?
const sentences = ['hello world', 'foo bar'];
const words = sentences.flatMap(s => s.split(' '));
console.log(words);
console.log([1, 2, 3].flatMap(x => [x, x * 2]));
- A)
[["hello","world"],["foo","bar"]],[1,2,2,4,3,6] - B)
["hello","world","foo","bar"],[1,2,2,4,3,6] - C)
["hello world","foo bar"],[1,1,2,2,3,3] - D)
["hello","world","foo","bar"],[2,4,6]
Answer & Explanation
**Answer: B) `["hello","world","foo","bar"]`, `[1,2,2,4,3,6]`** **Explanation:** `flatMap(fn)` applies `fn` to each element and flattens the result by one level. It is equivalent to `.map(fn).flat(1)`. The first example maps each sentence to an array of words, then flattens. The second doubles each element inline.Q. What does Array.fill do?
const arr = new Array(5).fill(0);
console.log(arr);
const partial = [1, 2, 3, 4, 5];
partial.fill(0, 2, 4);
console.log(partial);
- A)
[0,0,0,0,0],[1,2,0,0,5] - B)
[0,0,0,0,0],[1,2,0,0,0] - C)
[undefined,undefined,undefined,undefined,undefined],[1,2,0,0,5] - D)
[0,0,0,0,0],[1,0,0,0,5]
Answer & Explanation
**Answer: A) `[0,0,0,0,0]`, `[1,2,0,0,5]`** **Explanation:** `new Array(5).fill(0)` creates an array of 5 zeros. `fill(value, start, end)` fills from index `start` (inclusive) to `end` (exclusive). `fill(0, 2, 4)` fills indices 2 and 3 with `0`, leaving index 4 (`5`) unchanged. `fill` mutates the original array.Q. What does includes return versus indexOf?
const arr = [1, NaN, null, undefined, 0];
console.log(arr.includes(NaN));
console.log(arr.indexOf(NaN));
console.log(arr.includes(null));
console.log(arr.indexOf(null));
- A)
true,-1,true,2 - B)
false,-1,true,2 - C)
true,-1,false,-1 - D)
true,2,true,2
Answer & Explanation
**Answer: A) `true`, `-1`, `true`, `2`** **Explanation:** `includes()` uses the SameValueZero algorithm — it correctly finds `NaN` (unlike `NaN === NaN` which is false). `indexOf()` uses strict equality (`===`), so it cannot find `NaN` → returns `-1`. Both find `null` at index 2.Q. What does array destructuring with rest produce?
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first);
console.log(second);
console.log(rest);
const [a, , b] = [10, 20, 30];
console.log(a, b);
- A)
1,2,[3,4,5],10 20 - B)
1,2,[3,4,5],10 30 - C)
1,2,3,10 30 - D)
1,2,[3,4,5],10 undefined
Answer & Explanation
**Answer: B) `1`, `2`, `[3,4,5]`, `10 30`** **Explanation:** Rest syntax `...rest` collects all remaining elements into an array. Skipping elements with `, ,` (empty slot) jumps over the value at that position — so `a = 10` and `b = 30` (index 2), skipping `20` (index 1).Q. How does sort work with numbers?
const nums = [10, 1, 21, 2];
console.log(nums.sort());
console.log([10, 1, 21, 2].sort((a, b) => a - b));
console.log([10, 1, 21, 2].sort((a, b) => b - a));
- A)
[1,2,10,21],[1,2,10,21],[21,10,2,1] - B)
[1,10,2,21],[1,2,10,21],[21,10,2,1] - C)
[10,1,21,2],[1,2,10,21],[21,10,2,1] - D)
[1,2,10,21],[1,2,10,21],[2,1,21,10]
Answer & Explanation
**Answer: B) `[1,10,2,21]`, `[1,2,10,21]`, `[21,10,2,1]`** **Explanation:** Default `sort()` converts elements to strings and sorts lexicographically — `"10" < "2"` because `"1" < "2"` as a string! Always provide a comparator for numeric sorting: `(a, b) => a - b` for ascending, `(a, b) => b - a` for descending.Q. What does reduceRight return?
const result = [[1, 2], [3, 4], [5, 6]]
.reduceRight((acc, arr) => acc.concat(arr), []);
console.log(result);
- A)
[1,2,3,4,5,6] - B)
[5,6,3,4,1,2] - C)
[6,5,4,3,2,1] - D)
[[5,6],[3,4],[1,2]]
Answer & Explanation
**Answer: B) `[5,6,3,4,1,2]`** **Explanation:** `reduceRight` processes elements from right to left. Starting with `[]`: concat `[5,6]` → `[5,6]`, concat `[3,4]` → `[5,6,3,4]`, concat `[1,2]` → `[5,6,3,4,1,2]`. Compare with `reduce` (left-to-right) which would produce `[1,2,3,4,5,6]`.Q. What does Array.of return versus new Array?
console.log(Array.of(3));
console.log(new Array(3));
console.log(Array.of(1, 2, 3));
console.log(new Array(1, 2, 3));
- A)
[3],[3],[1,2,3],[1,2,3] - B)
[3],[,,],[1,2,3],[1,2,3] - C)
[undefined,undefined,undefined],[,,],[1,2,3],[1,2,3] - D)
[3],[undefined,undefined,undefined],[1,2,3],[1,2,3]
Answer & Explanation
**Answer: B) `[3]`, `[,,]`, `[1,2,3]`, `[1,2,3]`** **Explanation:** `Array.of(3)` creates an array with one element `3`. `new Array(3)` creates a sparse array with 3 **empty** slots (a classic gotcha). `Array.of(1,2,3)` and `new Array(1,2,3)` are equivalent. Use `Array.of()` when you need reliable single-element arrays.# 7. Control Flow
Q. What is the output of this loop?
for (var i = 0; i < 3; i++) {
if (i === 1) continue;
console.log(i);
}
- A)
0,1,2 - B)
0,2 - C)
1 - D)
0
Answer & Explanation
**Answer: B) `0`, `2`** **Explanation:** `continue` skips the current iteration when `i === 1`, so `1` is never logged. The loop runs for `i = 0`, skips `i = 1`, runs for `i = 2`.Q. A developer writes a switch statement. What is logged?
let day = 2;
switch (day) {
case 1:
console.log("Monday");
case 2:
console.log("Tuesday");
case 3:
console.log("Wednesday");
break;
default:
console.log("Unknown");
}
- A)
"Tuesday" - B)
"Tuesday","Wednesday" - C)
"Tuesday","Wednesday","Unknown" - D)
"Monday","Tuesday","Wednesday"
Answer & Explanation
**Answer: B) `"Tuesday"`, `"Wednesday"`** **Explanation:** Without a `break` after `case 2`, execution "falls through" to `case 3`. The `break` in `case 3` stops further execution. This is a common JavaScript gotcha.Q. What is the output of a do...while loop?
let i = 5;
do {
console.log(i);
i--;
} while (i > 0 && i < 3);
- A)
5,4,3,2,1 - B)
5 - C) Nothing — the condition is false from the start
- D)
5,4,3
Answer & Explanation
**Answer: B) `5`** **Explanation:** A `do...while` loop **always executes at least once** before checking the condition. After printing `5`, `i` becomes `4`. The condition `4 > 0 && 4 < 3` is `false`, so the loop stops. Only `5` is logged.Q. What is the difference between for...of and for...in?
const arr = ['a', 'b', 'c'];
arr.custom = 'extra';
for (const val of arr) process.stdout.write(val + ' ');
console.log();
for (const key in arr) process.stdout.write(key + ' ');
- A)
a b c,0 1 2 - B)
a b c,0 1 2 custom - C)
0 1 2,a b c - D)
a b c extra,0 1 2
Answer & Explanation
**Answer: B) `a b c`, `0 1 2 custom`** **Explanation:** `for...of` iterates over **values** of iterable objects (strings, arrays, Maps, Sets). `for...in` iterates over **all enumerable property keys** — including non-index properties like `custom`. Never use `for...in` to iterate arrays; use `for...of` or `forEach` instead.Q. What does a labeled break do?
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) break outer;
console.log(i, j);
}
}
- A)
0 0,1 0,2 0 - B)
0 0 - C)
0 0,0 1,0 2,1 0,1 1,1 2,2 0,2 1,2 2 - D)
0 0,1 0
Answer & Explanation
**Answer: B) `0 0`** **Explanation:** `break outer` breaks out of the **outer** labeled loop entirely, not just the inner one. When `i=0, j=1`, `break outer` is triggered, immediately exiting both loops. Only `0 0` is printed before the break.Q. What is the output of for...of iterating over a string?
const emoji = 'Hi😀';
const chars = [];
for (const char of emoji) {
chars.push(char);
}
console.log(chars.length);
console.log(emoji.length);
- A)
4,4 - B)
3,4 - C)
4,5 - D)
3,5
Answer & Explanation
**Answer: B) `3`, `4`** **Explanation:** `for...of` iterates Unicode code points correctly — the emoji `😀` is a single character. `chars.length` is `3` ('H', 'i', '😀'). However, `emoji.length` is `4` because `length` counts UTF-16 code units, and the emoji takes 2 code units (a surrogate pair).Q. What does while with break and continue produce?
let i = 0;
let sum = 0;
while (i < 10) {
i++;
if (i % 2 === 0) continue;
if (i > 7) break;
sum += i;
}
console.log(sum);
- A)
25 - B)
16 - C)
9 - D)
1 + 3 + 5 + 7 = 16
Answer & Explanation
**Answer: B) `16`** **Explanation:** The loop adds odd numbers below 8. `continue` skips even numbers. `break` exits when `i > 7`. Odd numbers added: `1, 3, 5, 7` → sum = `16`. (`9` is odd but `i > 7` triggers break before adding).Q. What does a switch with multiple cases sharing a block produce?
const fruit = 'orange';
switch (fruit) {
case 'apple':
case 'pear':
console.log('pome fruit');
break;
case 'orange':
case 'lemon':
console.log('citrus fruit');
break;
default:
console.log('other');
}
- A)
"citrus fruit","other" - B)
"citrus fruit" - C)
"other" - D)
"pome fruit","citrus fruit"
Answer & Explanation
**Answer: B) `"citrus fruit"`** **Explanation:** Multiple cases can share a block by stacking them without `break`. `'orange'` matches `case 'orange'`, falls through to `case 'lemon'`\'s block (they share the same code), logs `"citrus fruit"`, then hits `break`. This is intentional fall-through for grouping.Q. What is the output of nested loops with continue?
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (i === j) continue;
console.log(i, j);
}
}
- A) 9 pairs (all combinations)
- B) 6 pairs (all except where i === j)
- C) 3 pairs (only where i === j)
- D) 0 pairs
Answer & Explanation
**Answer: B) 6 pairs (all except where i === j)** **Explanation:** `continue` skips the current iteration of the **innermost** loop when `i === j`. The pairs `(1,1)`, `(2,2)`, `(3,3)` are skipped. The remaining 6 pairs `(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)` are logged.Q. What does try...finally return in a loop?
function test() {
for (let i = 0; i < 3; i++) {
try {
if (i === 1) return 'returned at 1';
} finally {
console.log('finally:', i);
}
}
return 'done';
}
console.log(test());
- A)
"finally: 0","finally: 1","finally: 2","done" - B)
"finally: 0","finally: 1","returned at 1" - C)
"finally: 1","returned at 1" - D)
"finally: 0","returned at 1"
Answer & Explanation
**Answer: B) `"finally: 0"`, `"finally: 1"`, `"returned at 1"`** **Explanation:** `finally` always runs, even when `return` is encountered. When `i=0`: `finally` logs `"finally: 0"`, no return. When `i=1`: `return` is hit, but `finally` still runs first (`"finally: 1"`), then the function returns `"returned at 1"`.Q. What does for...in return for inherited properties?
function Base() {
this.own = 1;
}
Base.prototype.inherited = 2;
const obj = new Base();
const keys = [];
for (const key in obj) keys.push(key);
const ownKeys = Object.keys(obj);
console.log(keys, ownKeys);
- A)
["own"],["own"] - B)
["own","inherited"],["own"] - C)
["own","inherited"],["own","inherited"] - D)
["inherited"],["own"]
Answer & Explanation
**Answer: B) `["own","inherited"]`, `["own"]`** **Explanation:** `for...in` iterates over all enumerable properties including **inherited** ones. `Object.keys()` only returns the object\'s own enumerable properties. When iterating with `for...in`, use `hasOwnProperty` check (`if (obj.hasOwnProperty(key))`) to filter out inherited properties.Q. What is the output of the ternary operator used as a statement?
let x = 10;
x > 5 ? console.log('big') : console.log('small');
const result = x > 5 ? x * 2 : x / 2;
console.log(result);
- A)
"small",5 - B)
"big",20 - C)
"big",5 - D) Nothing,
20
Answer & Explanation
**Answer: B) `"big"`, `20`** **Explanation:** The ternary operator can be used as a statement (though this is generally discouraged for readability). `10 > 5` is true → `console.log('big')` is executed. The second ternary evaluates `x * 2 = 20` and assigns it to `result`.# 8. Functions
Q. A function is called before it is defined. What is the output?
console.log(greet("Alice"));
function greet(name) {
return "Hello, " + name;
}
- A)
ReferenceError: greet is not defined - B)
TypeError: greet is not a function - C)
"Hello, Alice" - D)
undefined
Answer & Explanation
**Answer: C) `"Hello, Alice"`** **Explanation:** Function declarations are fully hoisted — both the name and body. You can call them before they appear in code. This is different from function expressions (`const greet = function(){}`) which are not hoisted.Q. What is the output of this function with default parameters?
function createUser(name, role = "viewer", active = true) {
return `${name} | ${role} | ${active}`;
}
console.log(createUser("Alice"));
console.log(createUser("Bob", undefined, false));
- A)
"Alice | viewer | true","Bob | viewer | false" - B)
"Alice | undefined | undefined","Bob | undefined | false" - C)
"Alice | viewer | true","Bob | undefined | false" - D) Error on both calls
Answer & Explanation
**Answer: A) `"Alice | viewer | true"`, `"Bob | viewer | false"`** **Explanation:** When a parameter is `undefined` (or omitted), the default value is used. Passing `undefined` explicitly still triggers the default. Passing `false` explicitly overrides the default.Q. What does the arguments object contain?
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3, 4, 5));
console.log(Array.isArray(arguments));
- A)
15,true - B)
15,false— then ReferenceError on the second log - C)
NaN,false - D)
15, and the second line throwsReferenceError
Answer & Explanation
**Answer: D) `15`, and the second line throws `ReferenceError`** **Explanation:** Inside `sum()`, `arguments` works fine. Outside a function, `arguments` is not defined → `ReferenceError`. The `arguments` object is array-like (has `length`, numeric indices) but is NOT an actual array — `Array.isArray(arguments)` inside the function returns `false`. Use rest parameters (`...args`) in modern code.Q. What does an IIFE (Immediately Invoked Function Expression) do?
const counter = (function() {
let count = 0;
return {
inc: () => ++count,
get: () => count
};
})();
counter.inc();
counter.inc();
console.log(counter.get());
console.log(typeof count);
- A)
2,"number" - B)
2,"undefined" - C)
0,"undefined" - D)
ReferenceError
Answer & Explanation
**Answer: B) `2`, `"undefined"`** **Explanation:** The IIFE creates a private `count` variable inaccessible from outside. The returned object provides controlled access. `counter.inc()` increments twice → `2`. `typeof count` outside the IIFE → `"undefined"` (undeclared variable, `typeof` is safe). This is the classic module pattern.Q. What does a higher-order function return?
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5));
console.log(triple(5));
console.log(double(triple(4)));
- A)
10,15,24 - B)
10,15,24 - C)
25,15,24 - D)
10,15,12
Answer & Explanation
**Answer: A) `10`, `15`, `24`** **Explanation:** `multiplier` is a higher-order function — it returns a function. `double(5)` → `5*2 = 10`. `triple(5)` → `5*3 = 15`. `double(triple(4))` → `triple(4) = 12`, then `double(12) = 24`. Each closure captures its own `factor` value.Q. What does a recursive function return?
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
console.log(factorial(5));
console.log(factorial(0));
- A)
120,0 - B)
120,1 - C)
120,undefined - D)
24,1
Answer & Explanation
**Answer: B) `120`, `1`** **Explanation:** `factorial(5) = 5 * 4 * 3 * 2 * 1 = 120`. `factorial(0)` hits the base case `n <= 1` immediately and returns `1` (0! = 1 by mathematical convention). Recursive functions must have a base case to prevent infinite recursion and stack overflow.Q. What does currying produce?
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
};
}
const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3));
console.log(add(1, 2)(3));
console.log(add(1)(2, 3));
- A)
6,6,6 - B)
6,undefined,6 - C)
1,6,6 - D)
TypeError,6,6
Answer & Explanation
**Answer: A) `6`, `6`, `6`** **Explanation:** Currying transforms a function with multiple arguments into a sequence of functions each taking one (or more) arguments. All three call styles produce `6` because `curry` checks if enough arguments are provided (`args.length >= fn.length`). If not, it returns another partial function.Q. What does the Function.length property return?
function noParams() {}
function twoParams(a, b) {}
function withDefault(a, b = 10) {}
function withRest(a, ...rest) {}
console.log(noParams.length, twoParams.length, withDefault.length, withRest.length);
- A)
0,2,2,2 - B)
0,2,1,1 - C)
0,2,2,1 - D)
0,2,1,2
Answer & Explanation
**Answer: B) `0`, `2`, `1`, `1`** **Explanation:** `Function.length` counts the number of **expected parameters**, excluding parameters with default values and rest parameters. `noParams.length = 0`, `twoParams.length = 2`, `withDefault.length = 1` (only `a` counts), `withRest.length = 1` (only `a` counts, rest is excluded).Q. What does a named function expression return for fn.name?
const greet = function sayHello(name) {
return `Hello, ${name}!`;
};
console.log(greet('Alice'));
console.log(greet.name);
console.log(typeof sayHello);
- A)
"Hello, Alice!","greet","function" - B)
"Hello, Alice!","sayHello","undefined" - C)
"Hello, Alice!","greet","undefined" - D)
"Hello, Alice!","sayHello","function"
Answer & Explanation
**Answer: B) `"Hello, Alice!"`, `"sayHello"`, `"undefined"`** **Explanation:** In a named function expression, the function\'s `name` property is its own name (`"sayHello"`), not the variable it\'s assigned to. The name `sayHello` is only accessible **inside** the function body (useful for recursion). Outside, `typeof sayHello` is `"undefined"`.Q. What does a function with rest parameters and spread receive?
function logAll(first, ...rest) {
console.log(first);
console.log(rest);
console.log(rest.length);
}
const args = [1, 2, 3, 4];
logAll(...args);
- A)
1,[2,3,4],3 - B)
[1,2,3,4],[],0 - C)
1,[2,3,4],4 - D)
1,2,3
Answer & Explanation
**Answer: A) `1`, `[2,3,4]`, `3`** **Explanation:** The spread operator `...args` expands the array into individual arguments. `first` captures `1`. The rest parameter `...rest` collects the remaining arguments `[2, 3, 4]` into a real array. `rest.length` is `3`. Rest parameters must always be last in the parameter list.Q. What is the output of a function with a default parameter that has a side effect?
let callCount = 0;
function getDefault() {
callCount++;
return 'default';
}
function greet(name = getDefault()) {
return `Hello, ${name}!`;
}
greet('Alice');
greet('Bob');
greet();
console.log(callCount);
- A)
0 - B)
1 - C)
2 - D)
3
Answer & Explanation
**Answer: B) `1`** **Explanation:** Default parameter expressions are evaluated **lazily** — only when the parameter is actually `undefined`. The first two calls provide explicit values, so `getDefault()` is never called. The third call omits the argument → `getDefault()` is called once. `callCount = 1`.Q. What does Function.prototype.toString return?
function add(a, b) {
return a + b;
}
const src = add.toString();
console.log(typeof src);
console.log(src.includes('return a + b'));
- A)
"function",false - B)
"string",true - C)
"object",true - D)
"string",false
Answer & Explanation
**Answer: B) `"string"`, `true`** **Explanation:** `Function.prototype.toString()` returns the source code of the function as a **string** — including whitespace and comments as written. This can be used for serialization, documentation tools, or debugging. The result contains `'return a + b'` so `includes` returns `true`.Q. What does a function returning another function (closure) produce?
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(3));
console.log(add10(3));
console.log(add5 === add10);
- A)
8,13,true - B)
8,13,false - C)
5,10,false - D)
8,8,false
Answer & Explanation
**Answer: B) `8`, `13`, `false`** **Explanation:** `makeAdder` is a closure factory. Each call creates a new function with its own `x` captured in a closure. `add5(3)` uses `x=5`: `5+3=8`. `add10(3)` uses `x=10`: `10+3=13`. `add5 !== add10` because they are distinct function objects created by separate calls.L2: Intermediate (Junior-Mid / Developer)
# 8. Scope & Closures
Q. A developer creates counter functions using closures. What does the following output?
function makeCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
value: () => count
};
}
const counter = makeCounter();
counter.increment();
counter.increment();
counter.decrement();
console.log(counter.value());
- A)
0 - B)
1 - C)
2 - D)
3
Answer & Explanation
**Answer: B) `1`** **Explanation:** The closure retains access to `count`. Starting at `0`: `increment()` → `1`, `increment()` → `2`, `decrement()` → `1`. `counter.value()` returns `1`.Q. What is the output of this immediately invoked function?
const result = (function() {
let x = 10;
return function(y) {
return x + y;
};
})();
console.log(result(5));
console.log(typeof x);
- A)
15,"number" - B)
15,"undefined" - C)
NaN,"undefined" - D)
ReferenceError
Answer & Explanation
**Answer: B) `15`, `"undefined"`** **Explanation:** The IIFE runs immediately, and `result` holds the inner function. `x` is enclosed in the IIFE\'s scope — it\'s inaccessible outside, so `typeof x` returns `"undefined"` (not a ReferenceError because `typeof` on an undeclared variable is safe).Q. What does a closure capture — value or reference?
function makeMultipliers() {
const fns = [];
for (let i = 1; i <= 3; i++) {
fns.push(() => i * 10);
}
return fns;
}
const [times1, times2, times3] = makeMultipliers();
console.log(times1(), times2(), times3());
- A)
10,10,10 - B)
30,30,30 - C)
10,20,30 - D)
undefined,undefined,undefined
Answer & Explanation
**Answer: C) `10`, `20`, `30`** **Explanation:** Because `let` is block-scoped, each loop iteration creates a new `i` binding. Each arrow function closes over its own `i` (1, 2, 3). With `var`, all closures would share the same `i` (→ `40, 40, 40` after the loop ends at 4). This demonstrates how `let` fixed the classic closure-in-loop bug.Q. What does the module pattern using closure produce?
const BankAccount = (function() {
let balance = 0;
return {
deposit(amount) { balance += amount; },
withdraw(amount) { if (amount <= balance) balance -= amount; },
getBalance() { return balance; }
};
})();
BankAccount.deposit(100);
BankAccount.deposit(50);
BankAccount.withdraw(30);
console.log(BankAccount.getBalance());
console.log(BankAccount.balance);
- A)
120,120 - B)
120,undefined - C)
150,undefined - D)
120,0
Answer & Explanation
**Answer: B) `120`, `undefined`** **Explanation:** The IIFE creates a private `balance` variable. `100 + 50 - 30 = 120`. `BankAccount.balance` is `undefined` — `balance` is not exposed on the returned object. This is the fundamental value of closures for encapsulation: hiding internal state.Q. What does lexical scoping mean in this example?
const name = 'Global';
function outer() {
const name = 'Outer';
return function inner() {
return name;
};
}
const fn = outer();
const name2 = 'Reassigned';
console.log(fn());
- A)
"Global" - B)
"Outer" - C)
"Reassigned" - D)
undefined
Answer & Explanation
**Answer: B) `"Outer"`** **Explanation:** Lexical scoping means a function\'s scope is determined by where it is **defined**, not where it is **called**. `inner` was defined inside `outer`, so it closes over `outer`\'s `name = 'Outer'`. Even after `outer` returns and a new `name2` is declared, `fn()` still refers to `'Outer'`.Q. What does partial application using closures produce?
function partial(fn, ...presetArgs) {
return function(...laterArgs) {
return fn(...presetArgs, ...laterArgs);
};
}
const multiply = (a, b, c) => a * b * c;
const double = partial(multiply, 2);
const sixTimes = partial(multiply, 2, 3);
console.log(double(3, 4));
console.log(sixTimes(5));
- A)
24,30 - B)
24,30 - C)
6,30 - D)
24,10
Answer & Explanation
**Answer: A) `24`, `30`** **Explanation:** Partial application pre-fills some arguments. `double(3, 4)` → `multiply(2, 3, 4) = 24`. `sixTimes(5)` → `multiply(2, 3, 5) = 30`. Closures make this possible by preserving `presetArgs` across calls. Unlike `bind`, this pattern allows remaining args to be supplied flexibly.Q. What is the output of a closure-based memoization?
function memoize(fn) {
const cache = {};
return function(n) {
if (n in cache) {
return cache[n];
}
return (cache[n] = fn(n));
};
}
let calls = 0;
const square = memoize(n => { calls++; return n * n; });
console.log(square(4), square(4), square(5));
console.log(calls);
- A)
16 16 25,3 - B)
16 16 25,2 - C)
16 undefined 25,2 - D)
16 16 25,1
Answer & Explanation
**Answer: B) `16 16 25`, `2`** **Explanation:** The cache (closed over) persists between calls. `square(4)` → cache miss, calls `fn(4)`, stores `16`. `square(4)` again → cache hit, returns `16` without calling `fn`. `square(5)` → cache miss, calls `fn(5)`. Total `fn` calls: `2`.Q. What does a closure accumulate?
function makeAccumulator(initial = 0) {
let sum = initial;
return function(value) {
sum += value;
return sum;
};
}
const acc1 = makeAccumulator();
const acc2 = makeAccumulator(10);
console.log(acc1(5));
console.log(acc1(3));
console.log(acc2(5));
console.log(acc1(2));
- A)
5,8,15,10 - B)
5,8,15,10 - C)
5,8,15,10 - D)
5,8,15,10
Answer & Explanation
**Answer: A) `5`, `8`, `15`, `10`** **Explanation:** Each call to `makeAccumulator` creates an independent `sum` variable. `acc1` starts at 0: 0+5=5, 5+3=8, then 8+2=10. `acc2` starts at 10: 10+5=15. The two accumulators do not share state.Q. What happens when a closure captures a variable that changes?
function example() {
let x = 10;
const getX = () => x;
const setX = (val) => { x = val; };
return { getX, setX };
}
const { getX, setX } = example();
console.log(getX());
setX(42);
console.log(getX());
- A)
10,10— closures take a snapshot of the value - B)
10,42— closures reference the variable, not its value - C)
undefined,42 - D)
10,undefined
Answer & Explanation
**Answer: B) `10`, `42` — closures reference the variable, not its value** **Explanation:** Closures capture **variables by reference**, not by value. Both `getX` and `setX` close over the same `x` variable. When `setX(42)` updates `x`, `getX()` reflects the change. This is why closures can implement shared mutable state.Q. What does a closure over a loop variable produce when using var vs let?
const varFns = [];
for (var i = 0; i < 3; i++) {
varFns.push(() => i);
}
const letFns = [];
for (let j = 0; j < 3; j++) {
letFns.push(() => j);
}
console.log(varFns.map(f => f()));
console.log(letFns.map(f => f()));
- A)
[0,1,2],[0,1,2] - B)
[3,3,3],[0,1,2] - C)
[0,1,2],[3,3,3] - D)
[3,3,3],[3,3,3]
Answer & Explanation
**Answer: B) `[3,3,3]`, `[0,1,2]`** **Explanation:** `var` creates a single `i` variable shared by all closures. After the loop, `i = 3` → all functions return `3`. `let` creates a new `j` binding per iteration → each closure captures a different value (0, 1, 2). This is the canonical example of why `let` was introduced.Q. What does a recursive closure compute?
const fibonacci = (function() {
const memo = {};
return function fib(n) {
if (n <= 1) return n;
if (n in memo) return memo[n];
return (memo[n] = fib(n - 1) + fib(n - 2));
};
})();
console.log(fibonacci(10));
console.log(fibonacci(0), fibonacci(1));
- A)
55,0,1 - B)
89,0,1 - C)
55,1,1 - D)
45,0,1
Answer & Explanation
**Answer: A) `55`, `0`, `1`** **Explanation:** The memoized Fibonacci function caches results. Fibonacci sequence: 0,1,1,2,3,5,8,13,21,34,55. `fibonacci(10) = 55`. `fibonacci(0) = 0`, `fibonacci(1) = 1` (base cases). The IIFE creates a private `memo` cache that persists across all calls.Q. What does an event listener using a closure produce?
function attachListeners() {
const buttons = ['A', 'B', 'C'];
return buttons.map((label, i) => {
return {
label,
handler: () => `Button ${label} at index ${i} clicked`
};
});
}
const listeners = attachListeners();
console.log(listeners[0].handler());
console.log(listeners[2].handler());
- A)
"Button A at index 0 clicked","Button C at index 2 clicked" - B)
"Button C at index 2 clicked","Button C at index 2 clicked" - C)
"Button A at index 0 clicked","Button A at index 0 clicked" - D)
ReferenceError
Answer & Explanation
**Answer: A) `"Button A at index 0 clicked"`, `"Button C at index 2 clicked"`** **Explanation:** `map` with arrow functions correctly captures each iteration\'s `label` and `i` via closures. Since `map` creates a new scope for each callback, each closure captures its specific values. This is the recommended pattern for attaching handlers to dynamically generated elements.# 9. Hoisting
Q. What is the output of the following code?
console.log(foo());
console.log(bar());
function foo() { return "foo"; }
const bar = function() { return "bar"; };
- A)
"foo","bar" - B)
"foo",TypeError: bar is not a function - C)
ReferenceError,"bar" - D)
undefined,undefined
Answer & Explanation
**Answer: B) `"foo"`, `TypeError: bar is not a function`** **Explanation:** Function declarations are fully hoisted, so `foo()` works. `bar` is a `const` variable in the Temporal Dead Zone (TDZ) at the time of the call, but since we call `bar()` after its declaration line... Wait, actually `const bar` is declared but its value (function expression) isn\'t assigned yet at hoisting time. At the point `console.log(bar())` is called, `bar` is in TDZ and will throw a `ReferenceError`. However, if `var bar` were used, it would be `TypeError`. With `const`, it\'s actually `ReferenceError: Cannot access 'bar' before initialization`. **Corrected Answer: B) — Note:** With `const`, accessing `bar` before its declaration throws `ReferenceError: Cannot access 'bar' before initialization` due to the Temporal Dead Zone. With `var`, it would be `TypeError: bar is not a function`.Q. What does the following output?
var x = "global";
function outer() {
console.log(x);
var x = "local";
console.log(x);
}
outer();
console.log(x);
- A)
"global","local","global" - B)
"global","local","local" - C)
undefined,"local","global" - D)
ReferenceError,"local","global"
Answer & Explanation
**Answer: C) `undefined`, `"local"`, `"global"`** **Explanation:** Inside `outer()`, the local `var x` is hoisted to the top of the function, shadowing the global. At the first `console.log(x)`, `x` is hoisted but not yet assigned → `undefined`. After assignment, it\'s `"local"`. The global `x` remains `"global"`.Q. Are class declarations hoisted?
try {
const obj = new MyClass();
} catch (e) {
console.log(e.constructor.name);
}
class MyClass {
greet() { return 'hello'; }
}
const obj2 = new MyClass();
console.log(obj2.greet());
- A) No error,
"hello" - B)
"ReferenceError","hello" - C)
"TypeError","hello" - D)
"SyntaxError","hello"
Answer & Explanation
**Answer: B) `"ReferenceError"`, `"hello"`** **Explanation:** Class declarations are hoisted but NOT initialized — they are in the Temporal Dead Zone until the declaration is reached. Accessing a class before its declaration throws `ReferenceError`. After the declaration, the class can be instantiated normally. This is the same behavior as `let`/`const`.Q. What is the hoisting behavior of var versus function declarations?
console.log(typeof myVar);
console.log(typeof myFunc);
var myVar = 'hello';
function myFunc() { return 'world'; }
console.log(typeof myVar);
console.log(typeof myFunc);
- A)
"undefined","undefined","string","function" - B)
"undefined","function","string","function" - C)
"string","function","string","function" - D)
ReferenceError,"function","string","function"
Answer & Explanation
**Answer: B) `"undefined"`, `"function"`, `"string"`, `"function"`** **Explanation:** `var` is hoisted and initialized to `undefined`. Function declarations are **fully** hoisted (name AND body). Before any code runs, `myFunc` is already a complete function. After their declarations are reached, `myVar` becomes `"string"` and `myFunc` remains `"function"`.Q. What happens when multiple var declarations exist for the same name?
var x = 1;
var x = 2;
var x = 3;
console.log(x);
function test() {
var y = 1;
var y = 2;
console.log(y);
}
test();
- A)
SyntaxError— duplicate var declarations - B)
3,2 - C)
1,1 - D)
3,1
Answer & Explanation
**Answer: B) `3`, `2`** **Explanation:** `var` allows duplicate declarations — later declarations effectively re-assign the variable. This is one of `var`\'s problematic behaviors. `let` and `const` throw `SyntaxError` for duplicate declarations in the same scope. The last assignment wins.Q. What is the output of hoisting with a function declaration inside a conditional?
console.log(typeof greet);
if (true) {
function greet() { return 'hello'; }
}
console.log(typeof greet);
- A)
"undefined","function"— function hoisted after block - B)
"function","function"— fully hoisted before block - C)
"undefined","undefined"— function declarations in blocks are not hoisted - D) Behavior is implementation-defined (varies between strict and sloppy mode)
Answer & Explanation
**Answer: D) Behavior is implementation-defined (varies between strict and sloppy mode)** **Explanation:** Function declarations inside blocks behave differently in strict vs. sloppy mode, and also vary between environments. In strict mode, block-level function declarations are scoped to the block. In sloppy mode, behavior is engine-specific. This is one reason to always use function expressions in blocks: `const greet = function() {}`.Q. What is the hoisting order when var and function share a name?
console.log(typeof foo);
var foo = 'variable';
function foo() { return 'function'; }
console.log(foo);
- A)
"undefined","variable" - B)
"function","variable" - C)
"variable","variable" - D)
SyntaxError— duplicate declaration
Answer & Explanation
**Answer: B) `"function"`, `"variable"`** **Explanation:** Function declarations are hoisted above `var` declarations. Before execution: `foo` is the function. The `var foo` declaration is ignored (already declared by function), but the assignment `foo = 'variable'` runs. Before the assignment: `typeof foo` → `"function"`. After: `foo` → `"variable"`.Q. What does accessing let/const in the TDZ specifically throw?
function test() {
try {
console.log(x);
} catch (e) {
console.log(e instanceof ReferenceError);
console.log(e.message.includes('Cannot access'));
}
let x = 5;
console.log(x);
}
test();
- A)
true,false,5 - B)
false,true,5 - C)
true,true,5 - D)
false,false,5
Answer & Explanation
**Answer: C) `true`, `true`, `5`** **Explanation:** Accessing a `let` or `const` variable before its declaration throws a `ReferenceError`. The message typically says `"Cannot access 'x' before initialization"`. After the declaration line is passed, `x` is initialized to `5` and can be used normally. The TDZ exists to catch programming errors.Q. What does hoisting produce with nested functions?
function outer() {
console.log(inner());
function inner() { return 'inner result'; }
}
outer();
- A)
ReferenceError: inner is not defined - B)
"inner result" - C)
undefined - D)
TypeError: inner is not a function
Answer & Explanation
**Answer: B) `"inner result"`** **Explanation:** Function declarations are fully hoisted within their containing function scope. `inner` is defined as a function declaration, so it is hoisted to the top of `outer()`\'s scope. It can be called before its declaration line. This is different from function expressions, which are NOT hoisted.Q. What is the output of hoisting with const inside a block?
{
console.log(typeof blockLet);
let blockLet = 'hello';
}
console.log(typeof blockLet);
- A)
"undefined","string" - B)
ReferenceError, then"undefined" - C)
"string","undefined" - D)
"undefined","undefined"
Answer & Explanation
**Answer: B) `ReferenceError`, then `"undefined"`** **Explanation:** Inside the block, `blockLet` is in the TDZ before its declaration — accessing it throws `ReferenceError`. The second `typeof blockLet` is outside the block where `blockLet` doesn\'t exist at all. `typeof` on an undeclared name safely returns `"undefined"` without throwing.Q. What is the output of a function expression versus declaration used before definition?
try { console.log(expr()); } catch (e) { console.log('expr error'); }
try { console.log(decl()); } catch (e) { console.log('decl error'); }
var expr = function() { return 'expression'; };
function decl() { return 'declaration'; }
- A)
"expression","declaration" - B)
"expr error","declaration" - C)
"expr error","decl error" - D)
"expression","decl error"
Answer & Explanation
**Answer: B) `"expr error"`, `"declaration"`** **Explanation:** `var expr` is hoisted as `undefined`, so calling `expr()` throws `TypeError: expr is not a function`. The `decl` function declaration is fully hoisted — it can be called before its position in the code. This is the key difference between the two syntax forms.Q. What is the output of var hoisting across if-else blocks?
function test(flag) {
if (flag) {
var result = 'true branch';
} else {
var result = 'false branch';
}
return result;
}
console.log(test(true));
console.log(test(false));
- A)
"true branch",ReferenceError - B)
"true branch","false branch" - C)
undefined,undefined - D)
"true branch",undefined
Answer & Explanation
**Answer: B) `"true branch"`, `"false branch"`** **Explanation:** `var` is function-scoped — the `result` declared in both branches is the **same** variable, hoisted to the top of `test()`. Whichever branch executes assigns the value. With `let`, both would be block-scoped to their respective `if`/`else` blocks, but the function-level `result` would be inaccessible (causing `ReferenceError`).# 10. ES6 Features
Q. A developer uses destructuring. What is the output?
const [a, , b, c = 99] = [10, 20, 30];
console.log(a, b, c);
- A)
10,20,30 - B)
10,30,99 - C)
10,30,undefined - D)
10,20,99
Answer & Explanation
**Answer: B) `10`, `30`, `99`** **Explanation:** The second element (`20`) is skipped using an empty slot. `b` captures `30`. `c` has a default of `99` and since there is no fourth element, it uses the default.Q. What does this template literal and tagged template return?
const name = "World";
const greeting = `Hello, ${name.toUpperCase()}! ${2 + 2} items.`;
console.log(greeting);
- A)
"Hello, ${name.toUpperCase()}! ${2 + 2} items." - B)
"Hello, World! 4 items." - C)
"Hello, WORLD! 4 items." - D)
SyntaxError
Answer & Explanation
**Answer: C) `"Hello, WORLD! 4 items."`** **Explanation:** Template literals evaluate expressions inside `${}`. `name.toUpperCase()` produces `"WORLD"` and `2 + 2` evaluates to `4`.Q. A developer converts a function to an arrow function. What is the key behavioral difference?
const obj = {
value: 42,
regular: function() { return this.value; },
arrow: () => this.value
};
console.log(obj.regular());
console.log(obj.arrow());
- A)
42,42 - B)
42,undefined - C)
undefined,42 - D) Both throw
TypeError
Answer & Explanation
**Answer: B) `42`, `undefined`** **Explanation:** Regular functions get their own `this` based on how they are called. When `obj.regular()` is called, `this` is `obj`, so it returns `42`. Arrow functions inherit `this` from the surrounding lexical scope. Since this object literal is in the global scope, `this.value` is `undefined` (or throws in strict mode).Q. What is the output of this spread/rest example?
function sum(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
const values = [1, 2, 3];
console.log(sum(...values, 4, 5));
- A)
[1, 2, 3, 4, 5] - B)
15 - C)
10 - D)
TypeError
Answer & Explanation
**Answer: B) `15`** **Explanation:** The spread operator `...values` expands the array into individual arguments. Combined with `4` and `5`, `sum` receives `1, 2, 3, 4, 5`. The rest parameter `...nums` collects all arguments into an array. `reduce` sums them to `15`.Q. What does object destructuring with renaming and defaults produce?
const { name: firstName, age = 18, country = 'US' } = { name: 'Alice', age: 25 };
console.log(firstName, age, country);
console.log(typeof name);
- A)
"Alice"25"US","string" - B)
"Alice"25"US","undefined" - C)
undefined25"US","string" - D)
"Alice"18"US","undefined"
Answer & Explanation
**Answer: B) `"Alice"` `25` `"US"`, `"undefined"`** **Explanation:** `name: firstName` renames `name` to `firstName`. `age = 18` defaults only if `age` is `undefined` — since it\'s `25`, the default is not used. `country` is not in the source object, so it defaults to `'US'`. The variable `name` is never created → `typeof name` is `"undefined"`.Q. What does Map return compared to a plain object?
const map = new Map();
map.set('a', 1);
map.set(1, 'one');
map.set(true, 'bool');
console.log(map.size);
console.log(map.get(1));
console.log(map.has(true));
for (const [key, val] of map) {
console.log(typeof key);
}
- A)
3,"one",true,"string","string","string" - B)
3,"one",true,"string","number","boolean" - C)
3,1,true,"string","number","boolean" - D)
2,"one",false,"string","number","boolean"
Answer & Explanation
**Answer: B) `3`, `"one"`, `true`, `"string"`, `"number"`, `"boolean"`** **Explanation:** Unlike plain objects (where keys are always strings/Symbols), `Map` can use any value as a key: strings, numbers, booleans, objects, etc. `map.size` gives the count. Iterating with `for...of` yields `[key, value]` pairs in insertion order, preserving key types.Q. What does a Set produce?
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log(set.size);
set.add(4);
set.add(2);
console.log(set.size);
console.log([...set]);
- A)
3,4,[1,2,3,4] - B)
6,7,[1,2,2,3,3,3,4,2] - C)
3,4,[1,2,3,4,2] - D)
3,5,[1,2,3,4,2]
Answer & Explanation
**Answer: A) `3`, `4`, `[1,2,3,4]`** **Explanation:** A `Set` stores only unique values — duplicates are silently ignored. `new Set([1,2,2,3,3,3])` has size `3`. Adding `4` increases size to `4`. Adding `2` again (already exists) does not change the set. Spreading the Set gives `[1,2,3,4]` in insertion order.Q. What does a tagged template literal receive?
function tag(strings, ...values) {
return strings.raw[0] + values[0] + strings.raw[1];
}
const name = 'World';
const result = tag`Hello\n${name}!`;
console.log(result);
- A)
"Hello\nWorld!" - B)
"Hello\\nWorld!" - C)
"HelloWorld" - D)
SyntaxError
Answer & Explanation
**Answer: B) `"Hello\\nWorld!"`** **Explanation:** Tagged templates receive a `strings` array where `strings.raw` preserves escape sequences as-is (raw strings). `strings.raw[0]` is `"Hello\\n"` (literal backslash-n, not a newline). `values[0]` is `"World"`. `strings.raw[1]` is `"!"`. Result: `"Hello\\nWorld!"`.Q. What does a generator function produce?
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
const gen = range(1, 3);
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
- A)
{value:1,done:false},{value:2,done:false},{value:3,done:false},{value:undefined,done:true} - B)
1,2,3,undefined - C)
{value:1,done:true},{value:2,done:true},{value:3,done:true},{value:undefined,done:false} - D)
{value:1,done:false},{value:2,done:false},{value:3,done:true},{value:undefined,done:true}
Answer & Explanation
**Answer: A) `{value:1,done:false}`, `{value:2,done:false}`, `{value:3,done:false}`, `{value:undefined,done:true}`** **Explanation:** Generators are lazy iterators. Each `next()` call runs until the next `yield`, returning `{value, done}`. `done: false` while values remain, `done: true` after the last yield. The final `next()` returns `{value: undefined, done: true}`.Q. What does object shorthand and computed properties produce?
const key = 'dynamic';
const value = 42;
const obj = {
key,
value,
[key + 'Key']: true,
greet() { return 'hello'; }
};
console.log(obj.key, obj.value, obj.dynamicKey, obj.greet());
- A)
"key",42,undefined,"hello" - B)
"key",42,true,"hello" - C)
"dynamic",42,true,"hello" - D)
"dynamic",42,undefined,"hello"
Answer & Explanation
**Answer: C) `"dynamic"`, `42`, `true`, `"hello"`** **Explanation:** Property shorthand `{ key }` is `{ key: key }` where `key = 'dynamic'` → property named `key` with value `'dynamic'`. Similarly `{ value }` → `{ value: 42 }`. Computed property `[key + 'Key']` evaluates to `'dynamicKey'`. Method shorthand `greet()` creates a method.Q. What does the WeakMap guarantee?
let obj = { data: 'private' };
const weakMap = new WeakMap();
weakMap.set(obj, 'metadata');
console.log(weakMap.has(obj));
obj = null; // obj is garbage collectable
console.log(weakMap.size);
- A)
true,0 - B)
true,1 - C)
true,undefined— WeakMap has nosizeproperty - D)
false,undefined
Answer & Explanation
**Answer: C) `true`, `undefined` — WeakMap has no `size` property** **Explanation:** `WeakMap` holds **weak references** to its keys — if the key object has no other references, it can be garbage collected. `weakMap.has(obj)` → `true`. `weakMap.size` is `undefined` — `WeakMap` deliberately has no `size` property (and is not iterable) because the entries may disappear at any time due to GC.Q. What does for...of with destructuring on a Map produce?
const scores = new Map([
['Alice', 95],
['Bob', 82],
['Carol', 78]
]);
const names = [];
for (const [name, score] of scores) {
if (score >= 80) names.push(name);
}
console.log(names);
- A)
["Alice", "Bob", "Carol"] - B)
["Alice", "Bob"] - C)
["Alice"] - D)
[]
Answer & Explanation
**Answer: B) `["Alice", "Bob"]`** **Explanation:** `for...of` on a `Map` yields `[key, value]` pairs in insertion order. Destructuring `[name, score]` unpacks each pair. Alice (95 ≥ 80) and Bob (82 ≥ 80) are included. Carol (78 < 80) is excluded. `Map.forEach` or `for...of` are the idiomatic ways to iterate Maps.Q. What does the Symbol.iterator protocol produce?
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range]);
- A)
[] - B)
[1, 2, 3] - C)
[0, 1, 2, 3] - D)
TypeError: range is not iterable
Answer & Explanation
**Answer: B) `[1, 2, 3]`** **Explanation:** An object is iterable if it implements `Symbol.iterator` — a method that returns an iterator object with a `next()` method. The spread operator and `for...of` use this protocol. Here, the iterator yields `1, 2, 3` and signals completion with `done: true`.Q. What does the nullish coalescing assignment ??= do?
let a = null;
let b = undefined;
let c = 0;
let d = '';
a ??= 'default-a';
b ??= 'default-b';
c ??= 'default-c';
d ??= 'default-d';
console.log(a, b, c, d);
- A)
"default-a","default-b","default-c","default-d" - B)
"default-a","default-b",0,"" - C)
"default-a","default-b",0,"default-d" - D)
"default-a","default-b","default-c",""
Answer & Explanation
**Answer: B) `"default-a"`, `"default-b"`, `0`, `""`** **Explanation:** `??=` (nullish assignment) only assigns if the left side is `null` or `undefined`. `a = null` and `b = undefined` are nullish → assigned. `c = 0` and `d = ''` are **not** nullish (they have values, even falsy ones) → not assigned. This differs from `||=` which assigns for any falsy value.# 11. DOM & Events
Q. A developer attaches event listeners with addEventListener. What is logged when the button is clicked?
const btn = document.querySelector("#btn");
btn.addEventListener("click", function() {
console.log("listener 1");
});
btn.addEventListener("click", function() {
console.log("listener 2");
});
- A) Only
"listener 2"— second listener overwrites the first - B)
"listener 1"then"listener 2"— both fire - C)
"listener 1"only — first listener takes priority - D) Neither fires — duplicate event type causes an error
Answer & Explanation
**Answer: B) `"listener 1"` then `"listener 2"` — both fire** **Explanation:** Unlike `onclick = fn`, which allows only one handler, `addEventListener` registers multiple listeners for the same event type. Both execute in the order they were registered.Q. A developer has nested elements with click handlers. What is the console output when the inner <div> is clicked?
document.getElementById("outer").addEventListener("click", () => console.log("outer"));
document.getElementById("inner").addEventListener("click", () => console.log("inner"));
HTML: <div id="outer"><div id="inner">Click me</div></div>
- A)
"outer"only - B)
"inner"only - C)
"inner"then"outer"— event bubbles up - D)
"outer"then"inner"— event captures down
Answer & Explanation
**Answer: C) `"inner"` then `"outer"` — event bubbles up** **Explanation:** By default, `addEventListener` uses the **bubbling phase**. The event fires on the target (`"inner"`) first, then bubbles up to ancestors. To use capturing (top-down), pass `{ capture: true }` as the third argument.Q. A developer calls event.stopPropagation(). What happens?
document.getElementById("outer").addEventListener("click", () => console.log("outer"));
document.getElementById("inner").addEventListener("click", (e) => {
e.stopPropagation();
console.log("inner");
});
// User clicks inner div
- A) Both
"inner"and"outer"are logged - B) Only
"inner"is logged — propagation is stopped - C) Only
"outer"is logged — inner is prevented - D) Neither is logged — stop propagation prevents all handlers
Answer & Explanation
**Answer: B) Only `"inner"` is logged — propagation is stopped** **Explanation:** `stopPropagation()` stops the event from bubbling up to parent elements. The inner handler runs, but the outer handler never fires. Compare with `stopImmediatePropagation()` which additionally prevents other listeners on the **same** element from running.Q. What is event delegation and how does it work?
document.getElementById("list").addEventListener("click", function(e) {
if (e.target.tagName === "LI") {
console.log("Clicked:", e.target.textContent);
}
});
HTML:
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
- A) This requires separate click listeners on each
<li>— the above doesn't work - B) This uses event delegation — one listener on the parent handles clicks on all children via bubbling
- C) This only works for the first
<li>element - D)
e.targetalways refers to the<ul>, not the clicked<li>
Answer & Explanation
**Answer: B) This uses event delegation — one listener on the parent handles clicks on all children via bubbling** **Explanation:** Event delegation attaches a single listener to a parent element and uses `e.target` to identify which child was clicked. Since events bubble, clicks on `- `. This is more efficient (one listener vs. many) and automatically handles dynamically added children.
</details>
## Q. What is the difference between `event.target` and `event.currentTarget`?
```javascript
document.getElementById("outer").addEventListener("click", function(e) {
console.log("target:", e.target.id);
console.log("currentTarget:", e.currentTarget.id);
});
// User clicks the inner div (id="inner") inside the outer div
```
- A) Both log `"outer"`
- B) Both log `"inner"`
- C) `e.target` → `"inner"`, `e.currentTarget` → `"outer"`
- D) `e.target` → `"outer"`, `e.currentTarget` → `"inner"`
Answer & Explanation
**Answer: C) `e.target` → `"inner"`, `e.currentTarget` → `"outer"`** **Explanation:** `e.target` is the element that **triggered** the event (the innermost clicked element). `e.currentTarget` is the element the listener is **attached to**. During bubbling, `currentTarget` changes with each handler, while `target` always remains the original element that was clicked.Answer & Explanation
**Answer: B) `1` — the handler fires only once then auto-removes itself** **Explanation:** The `{ once: true }` option automatically removes the event listener after it fires for the first time. This is equivalent to calling `removeEventListener` inside the handler, but cleaner. Useful for one-time initialization or single-use dialogs.Answer & Explanation
**Answer: B) Prevents the browser\'s default behavior (page reload/navigation) without stopping propagation** **Explanation:** `preventDefault()` stops the browser's default action for the event (like form submission causing a page reload, or link navigation). It does **not** stop propagation — other listeners still fire. Use it to implement custom form handling with `fetch()` or AJAX.Answer & Explanation
**Answer: B) `DOMContentLoaded` fires when the HTML is parsed; `load` fires after all images and stylesheets also load** **Explanation:** `DOMContentLoaded` fires when the HTML is fully parsed and the DOM is ready, without waiting for stylesheets, images, or subframes. `load` fires after **all** resources (images, CSS, fonts) have finished loading. For DOM manipulation, use `DOMContentLoaded`; for resource-dependent operations, use `load`.Answer & Explanation
**Answer: C) Logs `"Order: 123 Total: 99.99"` — the custom event bubbles up to `#app` with the detail payload** **Explanation:** `CustomEvent` allows creating arbitrary events with a `detail` payload. `bubbles: true` means the event bubbles up from `#checkout` to `#app`. This enables decoupled communication between components without tight coupling, similar to a pub/sub pattern.Answer & Explanation
**Answer: B) DOM additions/removals in the observed element and its descendants** **Explanation:** `MutationObserver` detects DOM changes asynchronously. `childList: true` monitors child node additions/removals. `subtree: true` extends monitoring to all descendants. When a `div` is appended, the callback fires with `m.type = 'childList'` and `m.addedNodes.length = 1`.Answer & Explanation
**Answer: B) Prevents `preventDefault()` from working, but allows the browser to optimize scrolling** **Explanation:** Passive event listeners tell the browser that the handler will **not** call `preventDefault()`. This allows the browser to start scrolling immediately without waiting for the JavaScript handler to complete. Marking scroll/touchmove handlers as `passive: true` significantly improves scroll performance on mobile.Answer & Explanation
**Answer: D) Only Attempt 2 works — same reference is required for removal** **Explanation:** `removeEventListener` requires the **exact same function reference** used in `addEventListener`. Each arrow function expression creates a new object — two identical-looking anonymous functions are different references. Only when the same variable (`handler`) is used for both add and remove does removal succeed.Answer & Explanation
**Answer: B) The callback fires before the next browser repaint, synchronized with the display refresh rate** **Explanation:** `requestAnimationFrame` schedules callbacks to run before the next browser repaint, synchronized to the display\'s refresh rate (typically 60fps → ~16.67ms). This ensures smooth animations without tearing. Unlike `setTimeout`, it automatically pauses in hidden tabs to save battery.## Q. A developer validates an email-like pattern. What does the following return? ```javascript const pattern = /^[a-zA-Z0-9]+@[a-zA-Z]+\.[a-zA-Z]{2,}$/; console.log(pattern.test("user@example.com")); console.log(pattern.test("user @example.com")); console.log(pattern.test("user@.com")); ``` - A) `true`, `true`, `true` - B) `true`, `false`, `false` - C) `true`, `true`, `false` - D) `false`, `false`, `true`
Answer & Explanation
**Answer: B) `true`, `false`, `false`** **Explanation:** The first matches the pattern. The second has a space (not in `[a-zA-Z0-9]+`), so it fails. The third has no domain name before `.com` (`[a-zA-Z]+` requires at least one letter), so it fails.Answer & Explanation
**Answer: A) `"qux bar foo baz foo"`, `"qux bar qux baz qux"`** **Explanation:** Without the `g` (global) flag, `.replace()` only replaces the **first** occurrence. With `/g`, all occurrences are replaced.Answer & Explanation
**Answer: A) `"2024"`, `"05"`, `"15"`** **Explanation:** Named capture groups use the `(?Answer & Explanation
**Answer: A) `['$10', '$20', '$40']`** **Explanation:** `/(?<=\$)\d+/` uses a **lookbehind** — it matches digits preceded by `$` without including `$` in the match. `test()` returns true for strings containing those digits. `filter` returns the original strings (`'$10'`, `'$20'`, `'$40'`), not just the digit portions.Answer & Explanation
**Answer: B) `["123", "456", "789"]`** **Explanation:** With the `g` flag, `regex.exec()` remembers its position via `regex.lastIndex`. Each call finds the next match. When no more matches exist, it returns `null` and resets `lastIndex` to 0. The `while` loop collects all three numbers.Answer & Explanation
**Answer: B) `"Hello World"`** **Explanation:** When the second argument to `replace` is a function, it\'s called for each match. The function receives the matched string (and optionally capture groups, offset, and original string) and returns the replacement. `\b\w` matches the first character of each word. `toUpperCase()` capitalizes it.Answer & Explanation
**Answer: B) `"bold and italic"`, `""`** **Explanation:** Greedy `.+` matches as much as possible → matches everything from the first `<` to the last `>`. Non-greedy `.+?` matches as little as possible → stops at the first `>`, capturing `""`. Add `?` after quantifiers (`*?`, `+?`, `{n,m}?`) to make them non-greedy. </details> ## Q. What does `String.matchAll` return? ```javascript const str = 'test1 test2 test3'; const matches = [...str.matchAll(/test(\d)/g)]; console.log(matches.length); console.log(matches[0][0], matches[0][1]); console.log(matches[2][0], matches[2][1]); ``` - A) `3`, `"test1"` `"1"`, `"test3"` `"3"` - B) `1`, `"test1"` `"1"`, `undefined` `undefined` - C) `3`, `"test1"` `undefined`, `"test3"` `undefined` - D) `["test1","test2","test3"]`Answer & Explanation
**Answer: A) `3`, `"test1"` `"1"`, `"test3"` `"3"`** **Explanation:** `matchAll` returns an iterator of all match objects including capture groups. Unlike `match(/g/)` which returns only matched strings, each entry in `matchAll` contains the full match info: `[0]` is the full match, `[1]` is the first capture group. Requires the `g` flag.Answer & Explanation
**Answer: A) `["cat"]`, `["cat","cat","cat","cat"]`** **Explanation:** `\b` matches a word boundary (transition between a word character and a non-word character). `/\bcat\b/g` only matches the standalone word `"cat"`. Without `\b`, `/cat/g` matches `"cat"` everywhere it appears: in `cat`, `concatenate`, `catfish`, and `category`.Answer & Explanation
**Answer: A) `["one","1","two","2","three","3","four"]`, `["one","two","three","four"]`** **Explanation:** When `split` is given a regex with a **capture group**, the captured portions are included in the result array. `split(/(\d)/)` includes the digits as separate elements. `split(/\d/)` (no capture group) splits on digits but discards them.Answer & Explanation
**Answer: B) `3`, `3`** **Explanation:** `/hello/gi` with `g` (global) + `i` (case-insensitive) matches all three occurrences. Without `m`, `^` only matches the start of the entire string. With `m` (multiline), `^` matches the start of each line. All three `"hello"` variants appear at line starts → 3 matches.Answer & Explanation
**Answer: C) `false`, `true`** **Explanation:** By default, `.` matches any character **except** newlines (`\n`, `\r`, etc.). Without the `s` flag, `/Hello.World/` does not match across a newline → `false`. With the `s` (dotAll) flag (ES2018), `.` matches **all** characters including newlines → `true`. This flag is essential for matching multi-line content.Answer & Explanation
**Answer: B) `4`, `1`** **Explanation:** `match()` (without `g`) returns an array where index 0 is the full match and indices 1+ are capture groups. With `()`: 1 full match + 3 groups = length 4. With `(?:)` (non-capturing groups): 1 full match only = length 1. Use `(?:)` when you need grouping for alternation/repetition but don\'t need to capture the value.## Q. A developer calls an API and handles errors. What does the following log? ```javascript function riskyOp() { throw new TypeError("Invalid input"); } try { riskyOp(); console.log("success"); } catch (e) { console.log(e instanceof TypeError, e.message); } finally { console.log("cleanup"); } ``` - A) `"success"`, `"cleanup"` - B) `true "Invalid input"`, `"cleanup"` - C) `true "Invalid input"` only - D) `false "Invalid input"`, `"cleanup"`
Answer & Explanation
**Answer: B) `true "Invalid input"`, `"cleanup"`** **Explanation:** `throw new TypeError(...)` is caught by `catch`. `e instanceof TypeError` is `true`, and `e.message` is `"Invalid input"`. The `finally` block **always** runs regardless of whether an exception was thrown.Answer & Explanation
**Answer: A) `{name: "Alice"}`, `null`** **Explanation:** Valid JSON is parsed successfully. `"not json"` causes `JSON.parse` to throw a `SyntaxError`, which is caught and `null` is returned. The optional catch binding (`catch` without a parameter) is valid ES2019+.Answer & Explanation
**Answer: B) `true`, `true`, `"ValidationError"` `"email"`** **Explanation:** `extends Error` makes `ValidationError` a subclass of `Error`, so `instanceof Error` is `true`. Setting `this.name` to `'ValidationError'` overrides the default name (otherwise it would show `"Error"`). Adding custom fields (`field`) lets callers extract structured error information.Answer & Explanation
**Answer: B) `"finally"`** **Explanation:** If the `finally` block has a `return` statement, it **overrides** the `return` from the `try` block. The `finally` block always executes, and its `return` takes precedence. This is a gotcha — avoid `return` in `finally` blocks as it can silently swallow return values from `try`.Answer & Explanation
**Answer: B) `["TypeError","ReferenceError","RangeError","URIError","SyntaxError"]`** **Explanation:** `null.property` → `TypeError`. `undeclaredVar` → `ReferenceError`. `new Array(-1)` → `RangeError` (invalid length). `decodeURIComponent('%')` → `URIError` (malformed URI). `eval('}{')` → `SyntaxError`. Knowing error types helps write precise `catch` handlers.Answer & Explanation
**Answer: B) `true` — the error is wrapped with a descriptive message** **Explanation:** Rethrowing errors is a best practice for creating error layers. Catch only the errors you expect (`SyntaxError`), wrap them with context, and rethrow unknown errors to avoid swallowing bugs. The new message starts with `'Invalid'` → `startsWith` returns `true`.Answer & Explanation
**Answer: C) `"Failed to fetch data"`, `true`** **Explanation:** `Error.cause` (ES2022) allows chaining errors to preserve the original cause when wrapping errors. `new Error(msg, { cause: originalError })` stores the original error as `e.cause`. Logging tools can walk the `cause` chain to show the complete error context. `e.cause instanceof TypeError` → `true`.Answer & Explanation
**Answer: B) `"caught: try error"`, `"finally"`, `"outer catch: catch error"`** **Explanation:** The sequence: `try` throws → `catch` logs `"caught: try error"` then throws a new error → `finally` runs (always!) → `finally` completes without throwing → the error from `catch` propagates → outer `catch` receives `"catch error"`. If `finally` threw, it would replace the `catch` error.Answer & Explanation
**Answer: B) `"Unhandled: whoops"` and `"Caught: handled"` — only uncaught rejections trigger the handler** **Explanation:** `unhandledRejection` fires for promises that have no rejection handler attached. The first `Promise.reject` has no `.catch()` → triggers the handler. The second has `.catch()` → handled normally. In Node.js, unhandled rejections can terminate the process in recent versions.Answer & Explanation
**Answer: B) `"Caught async error: async error"`** **Explanation:** `await` unwraps rejected promises and throws them as synchronous exceptions inside the `async` function. The `try...catch` block catches it. The `.catch()` on `main()` is not triggered because `main()` itself doesn\'t throw — the error was handled internally.Answer & Explanation
**Answer: B) `"Caught: constructor error"` — thrown errors in Promise constructors are converted to rejections** **Explanation:** The Promise constructor wraps the executor in a try/catch. Any synchronous throw inside the executor is automatically converted into a rejection. This means `.catch()` can handle both explicit `reject()` calls and thrown errors equivalently.Answer & Explanation
**Answer: B) `"level2 handled: RangeError"`** **Explanation:** `level3` throws a `RangeError`. `level2`\'s catch checks: `e instanceof TypeError` → `false` (it\'s a `RangeError`). So it logs `"level2 handled: RangeError"` and does NOT rethrow. The error is fully handled in `level2`. `level1`\'s catch never runs.## Q. A developer stores user preferences. What is the key difference between `localStorage` and `sessionStorage`? ```javascript localStorage.setItem("theme", "dark"); sessionStorage.setItem("token", "abc123"); ``` - A) `localStorage` stores numbers; `sessionStorage` stores strings - B) `localStorage` persists across browser sessions; `sessionStorage` is cleared when the tab/window closes - C) `sessionStorage` can store more data than `localStorage` - D) `localStorage` is synchronous; `sessionStorage` is asynchronous
Answer & Explanation
**Answer: B) `localStorage` persists across browser sessions; `sessionStorage` is cleared when the tab/window closes** **Explanation:** `localStorage` data persists until explicitly cleared. `sessionStorage` is scoped to the browser tab/window session and is lost when it closes. Both share the same API and store data as strings.Answer & Explanation
**Answer: B) `"51"`, `"string"`** **Explanation:** Web Storage always stores and retrieves values as **strings**. Even though `5` was stored as a number, `getItem()` returns `"5"`. String concatenation: `"5" + 1 = "51"`. Parse first: `parseInt(count) + 1`.Answer & Explanation
**Answer: B) `"Alice"`, `true`, `false`** **Explanation:** Objects must be serialized with `JSON.stringify()` before storage and deserialized with `JSON.parse()` on retrieval. The retrieved object is a new copy — it has the same values but is a different object reference (`retrieved === user` → `false`). Note: `JSON.stringify` loses functions, `undefined` values, and `Date` objects become strings.Answer & Explanation
**Answer: B) `3`** **Explanation:** `localStorage.length` gives the number of stored items. `localStorage.key(i)` returns the key at position `i`. This is the standard way to enumerate all keys since `localStorage` is not directly iterable. Alternative: `Object.keys(localStorage)` also works in most browsers.Answer & Explanation
**Answer: B) The `storage` event fires in OTHER tabs/windows sharing the same origin, not in the tab that made the change** **Explanation:** The `storage` event is a cross-tab communication mechanism. It fires in all windows/tabs of the same origin **except** the one that triggered the change. This allows tabs to synchronize state (e.g., logout across tabs). The event object contains `key`, `oldValue`, `newValue`, and `url`.Answer & Explanation
**Answer: C) May log either, depending on available space. Typical limit is 5-10MB per origin** **Explanation:** `localStorage` is limited to approximately 5-10MB per origin (varies by browser). Exceeding the quota throws a `QuotaExceededError` (a `DOMException`). Always wrap `localStorage.setItem` in try-catch for large data. For larger storage needs, use `IndexedDB`.Answer & Explanation
**Answer: B) `2`, `null`, `0`** **Explanation:** `removeItem('b')` removes only the `b` key. `length` drops from 3 to 2. `getItem('b')` returns `null` (not `undefined`) for missing keys. `clear()` removes **all** items in the storage for the origin → `length` becomes `0`.Answer & Explanation
**Answer: B) `localStorage` is accessible to any JavaScript on the page, making it vulnerable to XSS attacks** **Explanation:** If an attacker injects malicious JavaScript (XSS), they can read everything in `localStorage` with `localStorage.getItem()`. Never store tokens, passwords, or PII in `localStorage`. For authentication tokens, use `HttpOnly` cookies (inaccessible to JavaScript). If `localStorage` must be used, at minimum implement a Content Security Policy to prevent XSS.Answer & Explanation
**Answer: B) The duplicated tab gets a copy of the `sessionStorage` from the original, but they are independent after that** **Explanation:** When a tab is duplicated (Ctrl+D or `window.open`), the new tab gets a copy of the original\'s `sessionStorage`. However, changes in one tab do not affect the other — they are independent. `sessionStorage` is scoped per-tab, not per-origin (unlike `localStorage` which is shared across all tabs of the same origin).Answer & Explanation
**Answer: B) Cookies can have expiry dates, are sent with HTTP requests, and can be `HttpOnly`; `localStorage` is larger, never sent to the server, and always JS-accessible** **Explanation:** Key differences: Cookies are automatically sent with every HTTP request (useful for auth) and can be `HttpOnly` (not accessible to JS, preventing XSS theft). `localStorage` is ~5-10MB (vs ~4KB for cookies), never sent to the server, and always accessible to JavaScript. Use cookies for auth tokens; `localStorage` for UI preferences.Answer & Explanation
**Answer: B) `localStorage` works in private mode but is cleared when the private session ends (acts like `sessionStorage`)** **Explanation:** In most browsers, `localStorage` in private/incognito mode is functional during the session but does not persist after the window closes — it behaves like `sessionStorage`. Code using `localStorage` generally works in private mode without errors, but developers should not rely on persistence for users who frequently browse privately.Answer & Explanation
**Answer: B) Asynchronous, transactional storage with support for complex queries, indexes, and large binary data** **Explanation:** `IndexedDB` is a full client-side database: async (non-blocking), supports transactions, complex queries via indexes, large storage (hundreds of MB to GBs), and binary data (`ArrayBuffer`, `Blob`). `localStorage` is synchronous (can block the UI), limited to ~5-10MB, and only stores strings. Use `IndexedDB` (or a library like `idb`) for structured or large data.## # 15. Promises
## Q. A developer chains promises. What is logged? ```javascript Promise.resolve(1) .then(x => x + 1) .then(x => { throw new Error("oops"); }) .then(x => console.log("then:", x)) .catch(e => console.log("catch:", e.message)) .then(() => console.log("after catch")); ``` - A) `"then: 2"`, `"after catch"` - B) `"catch: oops"`, `"after catch"` - C) `"catch: oops"` only - D) Unhandled Promise Rejection
Answer & Explanation
**Answer: B) `"catch: oops"`, `"after catch"`** **Explanation:** The chain starts resolving. The second `.then` throws, which skips the next `.then` and jumps to `.catch`. The `.catch` handles the error and returns `undefined` (a resolved promise), so `.then` after `.catch` runs.Answer & Explanation
**Answer: A) `"all: fail"`, `"fulfilled"`, `"rejected"`, `"fulfilled"`** **Explanation:** `Promise.all` short-circuits on the first rejection. `Promise.allSettled` waits for all promises to settle (regardless of outcome) and returns an array of `{ status, value/reason }` objects — never rejects.Answer & Explanation
**Answer: B) `"fast"`** **Explanation:** `Promise.race` resolves or rejects as soon as the **first** promise settles (either resolves or rejects). `fast` resolves at 50ms, before `fail` rejects at 100ms and `slow` resolves at 200ms. `Promise.race` is useful for timeouts: `Promise.race([fetchData(), timeout(5000)])`.Answer & Explanation
**Answer: B) `"Resolved: success"`** **Explanation:** `Promise.any` resolves with the **first fulfilled** promise, ignoring rejections. Only rejects with an `AggregateError` if **all** promises reject. Here, `p2` resolves (eventually) with `'success'`. Use `Promise.any` when you have multiple redundant sources and need the fastest successful result.Answer & Explanation
**Answer: A) `"executor runs synchronously"`, `"after creation"`, `"resolved: 42"`** **Explanation:** The executor runs **synchronously** when the Promise is created. A Promise can only be resolved once — additional `resolve`/`reject` calls after the first are silently ignored. `.then` callbacks are microtasks scheduled after the current synchronous code, so `"after creation"` logs before `"resolved: 42"`.Answer & Explanation
**Answer: C) `undefined`, `undefined`** **Explanation:** `1 + 1 = 2`, then `Promise.resolve(2 * 3) = 6`. The next `.then` returns `undefined` (no return value). After that, every chained `.then` also receives `undefined`. A bare `return` is equivalent to `return undefined`.Answer & Explanation
**Answer: B) `"file contents"`** **Explanation:** `promisify` wraps a Node-style `(err, result)` callback function into a Promise. The `new Promise` executor calls the original function with the extra callback appended. On success, `resolve(result)` is called; on error, `reject(err)`. Node.js provides `util.promisify()` built-in.Answer & Explanation
**Answer: B) `"sync"`, `"1: hello"`, `"2: hello"`** **Explanation:** `.then` callbacks are always asynchronous microtasks, even if the Promise is already resolved. Synchronous code (`"sync"`) always runs first. Multiple `.then` handlers can be attached to the same promise — they all fire independently (not chained).Answer & Explanation
**Answer: B) `"caught: step 1 failed"`, `"step 3: recovered"`** **Explanation:** A `throw` in `.then` converts to a rejection, skipping all subsequent `.then` until a `.catch` is reached. `.catch` handles the error and returns `'recovered'` — a resolved value. The `.then` after `.catch` receives `'recovered'` and continues the chain.Answer & Explanation
**Answer: B) `"all failed: fail"`, `"settled count: 3"`** **Explanation:** `Promise.all` rejects immediately on the first rejection → `"all failed: fail"`. `Promise.allSettled` waits for all promises regardless and returns all results → `"settled count: 3"`. Use `allSettled` when you need results from all promises even if some fail.Answer & Explanation
**Answer: B) `3`, `3`** **Explanation:** Both produce `3` (1 + 2). The difference is performance: `sequential` awaits each promise one by one (if they took 1s each → 2s total). `parallel` starts both simultaneously via `Promise.all` (1s total). Always use `Promise.all` for independent async operations.Answer & Explanation
**Answer: A) `"resolved: late value"` — the promise resolves after 100ms** **Explanation:** The Deferred pattern exposes the `resolve`/`reject` functions outside the Promise constructor by storing them. This allows resolving a promise from any external location. While useful in some patterns, prefer explicit async/await in most cases. The promise resolves with `'late value'` after 100ms.## Q. What is the output of this async/await code? ```javascript async function fetchData() { return 42; } async function main() { const result = await fetchData(); console.log(result); console.log(result + 1); } console.log("start"); main(); console.log("end"); ``` - A) `"start"`, `42`, `43`, `"end"` - B) `"start"`, `"end"`, `42`, `43` - C) `42`, `43`, `"start"`, `"end"` - D) `"start"`, `42`, `"end"`, `43`
Answer & Explanation
**Answer: B) `"start"`, `"end"`, `42`, `43`** **Explanation:** `console.log("start")` runs synchronously. `main()` is called and starts executing, but `await` suspends `main` and control returns to the call site. `console.log("end")` runs synchronously. Then the microtask queue resolves the awaited promise, and `42` and `43` log.Answer & Explanation
**Answer: B) `"Error: Invalid ID"`** **Explanation:** `async` functions that `throw` return a rejected promise. `await` unwraps the rejection and throws it, which is caught by the `try...catch`. This is the idiomatic way to handle async errors.Answer & Explanation
**Answer: B) `true`, `"function"`, `"value: 5"`** **Explanation:** Every `async` function always returns a `Promise`, regardless of what the body returns. A plain `return value` is automatically wrapped in `Promise.resolve(value)`. The returned `result` is a Promise with `.then` method. Awaiting it yields `5`.Answer & Explanation
**Answer: B) `[42, 'hello', null]` — non-Promise values are wrapped in `Promise.resolve()`** **Explanation:** `await` wraps any non-thenable value in `Promise.resolve()`. `await 42` is equivalent to `await Promise.resolve(42)` and yields `42`. This makes `await` safe to use with any value — Promise or not.Answer & Explanation
**Answer: B) `processAll` takes ~1000ms; `processAllFast` takes ~100ms** **Explanation:** `await` in a loop processes items **sequentially** (one after another) — 10 × 100ms = ~1000ms. `Promise.all` starts **all** async operations simultaneously and resolves when all complete — ~100ms. Use `Promise.all` for independent operations. Sequential `await` is only needed when each operation depends on the previous result.Answer & Explanation
**Answer: C) `"caught in loadDashboard: Network error"`** **Explanation:** `await` converts promise rejections into thrown exceptions, making them catchable by `try...catch`. Even though `fetchUser` is an `async` function (returns a rejected promise), `await fetchUser()` throws the rejection reason synchronously within the async function. The `catch` block handles it.Answer & Explanation
**Answer: A) `[2, 4, 6]`** **Explanation:** `for await...of` iterates over async iterables (or regular iterables of Promises). It awaits each yielded value. Here the iterable is an array of Promises, each resolving to `n * 2`. The result is `[2, 4, 6]`. It\'s equivalent to a `for...of` loop with `await` inside, but also works with `AsyncIterator` objects like async generators or Node.js streams.Answer & Explanation
**Answer: B) `"done"`, then `2`, `4`, `6`** **Explanation:** `Array.forEach` does not await async callbacks. Each async callback returns a Promise, but `forEach` ignores it. So `"done"` is logged before the awaited results. Use `for...of` with `await` or `Promise.all(items.map(async ...))` when you need to await all callbacks.Answer & Explanation
**Answer: B) Valid ES2022+ top-level await — the module pauses execution until the awaited Promise resolves** **Explanation:** Top-level `await` (ES2022) allows using `await` at the top level of ES modules without wrapping in an `async` function. The module execution pauses at the `await`, and importing modules wait for the exporting module to fully initialize. This is useful for dynamic configuration loading or database connections at module startup.Answer & Explanation
**Answer: D) Both B and C — they describe the same behavior** **Explanation:** `Promise.race` resolves/rejects with the first settled promise. If `fetch` completes before `ms` milliseconds, it wins. If the timeout resolves first, it rejects with `"Timeout"`. The `catch` returns a descriptive string. This pattern effectively enforces a max wait time — implementing both a timeout and parallel execution.Answer & Explanation
**Answer: B) `"bar start"`, `"foo start"`, `"sync"`, `"foo end"`, `"bar end"`** **Explanation:** `bar()` runs synchronously until its first `await` (which is `foo()`). `foo()` runs synchronously until its first `await` → suspension. Control returns to `bar()` which suspends (awaiting `foo()`). Then `"sync"` logs. Microtask queue: `foo` resumes → `"foo end"` → `foo` resolves → `bar` resumes → `"bar end"`.## Q. What is the exact order of the console output? ```javascript console.log("1"); setTimeout(() => console.log("2"), 0); Promise.resolve().then(() => console.log("3")); console.log("4"); ``` - A) `1`, `2`, `3`, `4` - B) `1`, `4`, `2`, `3` - C) `1`, `4`, `3`, `2` - D) `1`, `3`, `4`, `2`
Answer & Explanation
**Answer: C) `1`, `4`, `3`, `2`** **Explanation:** Synchronous code runs first: `"1"`, `"4"`. Then the **microtask queue** (Promises) runs before the **macrotask queue** (setTimeout). So `"3"` (Promise) runs before `"2"` (setTimeout with 0ms delay).Answer & Explanation
**Answer: B) `"timeout 1"`, `"microtask inside timeout"`, `"timeout 2"`** **Explanation:** After each macrotask (setTimeout callback), the engine processes **all pending microtasks** before picking the next macrotask. So after `"timeout 1"`, the microtask queue is drained (`"microtask inside timeout"`), then `"timeout 2"` runs.Answer & Explanation
**Answer: A) `"Hello, I'm Alice!"`, `"Hi, I'm Alice?"`, `"Hey, I'm Alice..."`** **Explanation:** `.call(ctx, arg1, arg2)` invokes immediately with individual args. `.apply(ctx, [args])` invokes immediately with an array. `.bind(ctx, arg1)` returns a new function with `this` and first argument pre-filled (partial application).Answer & Explanation
**Answer: B) `undefined`, `"Timer"`** **Explanation:** A regular function in `setTimeout` is called with `this` set to the global object (or `undefined` in strict mode) — not `obj`. An arrow function captures the `this` from the enclosing lexical context (`obj.start()`\'s `this`, which is `obj`). Arrow functions are the idiomatic solution for preserving `this` in callbacks.Answer & Explanation
**Answer: B) `showThis` logs the global object; `strictThis` logs `undefined`** **Explanation:** In sloppy mode, a standalone function call sets `this` to the global object (`window` in browsers, `global` in Node.js). In strict mode, `this` is `undefined` for standalone calls. Arrow functions, unlike regular functions, don\'t have their own `this` binding at all.Answer & Explanation
**Answer: B) `1`, `Error: TypeError`** **Explanation:** Class bodies are always in strict mode. When `increment` is called as a method on `c`, `this` is `c`. When destructured and called as a standalone function, `this` is `undefined` (strict mode). Accessing `undefined.count` throws `TypeError`. Fix: bind in constructor — `this.increment = this.increment.bind(this)` — or use an arrow class field.Answer & Explanation
**Answer: B) `"Hello, Alice"`, `"Hello, Bob"`** **Explanation:** When called with `new`, `this` inside the constructor refers to the newly created object. `alice.greet()` — `this` is `alice`. When `greet` is assigned to `bob` and called as `bob.greet()`, `this` is `bob`. The method\'s `this` is determined by the **call site**, not where the method was defined.Answer & Explanation
**Answer: B) `undefined`, `42`, `42`** **Explanation:** `detached()` — `this` is global (no `x` → `undefined`). `bound()` — permanently bound to `module`, returns `42`. Importantly, `.call()` **cannot** override a bound function\'s `this` — `.bind()` creates a function with a hardcoded `this` that ignores subsequent `.call()/.apply()/.bind()` attempts.Answer & Explanation
**Answer: B) `10`, `undefined`** **Explanation:** `inner` is an arrow function defined inside `outer` method — it captures `outer`\'s `this`, which is `obj`. So `inner()` returns `10`. `wrong` is an arrow function defined directly in the object literal — the enclosing lexical context is the module/global scope where `this` is `undefined` (strict) or global.Answer & Explanation
**Answer: B) `"78.54"`, `NaN`** **Explanation:** `circle.area` invokes the getter with `this = circle` → `Math.PI * 25 ≈ 78.54`. When you destructure a getter with `const { area } = circle`, you get the **current value** (a number), not the getter function. So `area` is `78.54` and `area` (the variable) equals that number — no error, but `NaN` wouldn\'t occur. Actually, `area` is `78.54`, not NaN. The correct answer is A. **Correction — Answer: A) `"78.54"`, `"78.54"`** **Explanation:** Destructuring `{ area }` from an object with a getter evaluates the getter immediately and stores the **result** (a number) in `area`. Both `circle.area.toFixed(2)` and the local `area` variable contain the same numeric value. Unlike methods, getters return values — not functions.Answer & Explanation
**Answer: B) `"a, b, c"`** **Explanation:** The fluent/builder pattern works by returning `this` from each method. `add` pushes to `this.items` and returns `this` (the same `Builder` instance). Each chained `.add()` call operates on the same object. Finally, `.build()` joins the collected items. This pattern is used in libraries like jQuery, Lodash chains, and query builders.Answer & Explanation
**Answer: C) `TypeError`, then `"Clicked: Submit"`** **Explanation:** When `handler()` is called as a standalone function in class (strict mode), `this` is `undefined`. Accessing `undefined.label` throws `TypeError`. In DOM event listeners, `this` defaults to the element — not the class instance. Fix: `element.addEventListener('click', btn.handleClick.bind(btn))` or use an arrow class field: `handleClick = () => {...}`.Answer & Explanation
**Answer: A) `"Hello, I'm Alice!"`, `"Hi, I'm Alice?"`, `"Hey, I'm Alice..."`** **Explanation:** `.call(ctx, arg1, arg2)` invokes with explicit `this`. `.apply(ctx, [args])` takes arguments as array. `.bind(ctx, arg1)` returns a new function (partial application) — `boundIntro` already has `'Hey'` as first arg, only needs `'...'`. All three set `this` to `person`.## Q. What does `this` refer to in a method shorthand inside an object literal? ```javascript const user = { name: 'Alice', getName() { return this.name; }, getNameArrow: () => this.name }; console.log(user.getName()); console.log(user.getNameArrow()); ``` - A) `"Alice"`, `"Alice"` - B) `"Alice"`, `undefined` - C) `undefined`, `"Alice"` - D) Both `undefined`
Answer & Explanation
**Answer: B) `"Alice"`, `undefined`** **Explanation:** Method shorthands (`getName()`) have their own `this` binding — when called as `user.getName()`, `this` is `user`. Arrow functions (`getNameArrow: () => ...`) capture `this` from the surrounding lexical context at definition time — the module/global scope where `this.name` is `undefined`. Never use arrow functions as object methods when you need `this`.Answer & Explanation
**Answer: B) `false`, `"Tesla"`** **Explanation:** When `new Car('Tesla')` is called, a new empty object is created, `this` is set to that object, and the constructor runs. At the time `console.log(this === car)` runs inside the constructor, `car` hasn\'t been assigned yet (the constructor is still executing) — `car` is `undefined`, so `this === undefined` is `false`. After construction, `car` refers to the new object with `model: "Tesla"`.Answer & Explanation
**Answer: B) `new` > explicit (call/apply/bind) > implicit (method call) > default > arrow (no binding)** **Explanation:** `this` binding priority (highest to lowest): 1) **`new`** — creates a new object. 2) **Explicit** (`.call/.apply/.bind`) — overrides everything except `new`. 3) **Implicit** (method call: `obj.method()`) — `this` is the object. 4) **Default** (standalone call) — global or `undefined` in strict. Arrow functions don\'t have their own `this` — they always inherit from lexical scope.Answer & Explanation
**Answer: B) First throws `TypeError`; second logs `"[INFO]: a"`, `"[INFO]: b"`, `"[INFO]: c"`** **Explanation:** `logger.log` passed as a callback loses its `this` context. In class (strict mode), `this` becomes `undefined` → `this.prefix` throws `TypeError`. `.bind(logger)` creates a new function permanently bound to `logger`, so `this.prefix` is `"[INFO]"`.Answer & Explanation
**Answer: B) `"I am MathUtils"`, `25`** **Explanation:** In a static method, `this` refers to the **class itself** (the constructor function), not an instance. `this.name` on a function/class is its name string → `"MathUtils"`. Static methods are called on the class, so `this` is the class. Instance properties (like `this.value`) are not accessible from static methods.Answer & Explanation
**Answer: C) `TypeError`, `TypeError`** **Explanation:** Both methods are regular functions. When destructured and called as standalone functions in strict mode, `this` is `undefined`. Both will throw `TypeError: Cannot read properties of undefined (reading 'sound')`. The distinction between own vs prototype methods doesn\'t change `this` binding behavior — binding is determined by **call site**, not definition location.Answer & Explanation
**Answer: B) `150`** **Explanation:** Private fields (`#balance`) are scoped to the class and accessed via `this.#field`. `deposit` uses the fluent pattern — `return this` allows chaining. After `deposit(100)`, `#balance = 100`. After `deposit(50)`, `#balance = 150`. `this` in `deposit` and `getBalance` refers to the instance (called as methods).Answer & Explanation
**Answer: B) Logs `0` — `this.increment` inside `setInterval` loses `this` context** **Explanation:** `this.increment` is passed to `setInterval` as a **reference** — when called by the browser timer, `this` is the global object (or `undefined` in strict mode), not `counter`. So `this.count++` increments `window.count`, not `counter.count`. Fix: `setInterval(() => this.increment(), 1000)` or `setInterval(this.increment.bind(this), 1000)`.Answer & Explanation
**Answer: C) `"tick after 100ms"` — arrow class fields preserve `this`** **Explanation:** Arrow class fields create a new function **per instance** with `this` permanently bound to the instance. Unlike prototype methods, they're not on `Timer.prototype` — they're set in the constructor. This means `this.tick` can safely be passed as a callback without `.bind()`. This is the modern preferred pattern for event handlers in React and similar frameworks.Answer & Explanation
**Answer: A) `"I am Rex, a Labrador"`** **Explanation:** `super.describe()` calls the parent class method, but `this` inside that parent method still refers to the **current instance** (`d`). So `this.name` in `Animal.describe()` resolves to `'Rex'` from the `Dog` instance. `super` determines which method to call, not which `this` to use.## Q. A developer inspects the prototype chain. What does the following output? ```javascript function Animal(name) { this.name = name; } Animal.prototype.speak = function() { return `${this.name} makes a noise.`; }; const dog = new Animal("Rex"); console.log(dog.speak()); console.log(dog.hasOwnProperty("speak")); console.log(dog.hasOwnProperty("name")); ``` - A) `"Rex makes a noise."`, `true`, `true` - B) `"Rex makes a noise."`, `false`, `true` - C) `"Rex makes a noise."`, `true`, `false` - D) `undefined`, `false`, `true`
Answer & Explanation
**Answer: B) `"Rex makes a noise."`, `false`, `true`** **Explanation:** `speak` is on `Animal.prototype`, not on `dog` directly — so `hasOwnProperty("speak")` is `false`. `name` is set by the constructor on the instance itself — so `hasOwnProperty("name")` is `true`.Answer & Explanation
**Answer: B) `"Hello, Bob"`, `true`** **Explanation:** `Object.create(base)` creates a new object whose prototype is `base`. The `greet` method is inherited via the prototype chain. `Object.getPrototypeOf(child) === base` confirms this.Answer & Explanation
**Answer: B) `1`, `99` — Object.assign creates a shallow copy (nested objects are shared)** **Explanation:** `Object.assign` performs a **shallow copy** — primitive values are copied by value, but nested objects are copied by reference. `copy.a = 99` only affects `copy` (primitives are value-copied). `copy.b.c = 99` mutates the shared `b` object, affecting both `copy` and `original`. Use `structuredClone()` or `JSON.parse(JSON.stringify())` for deep copies.Answer & Explanation
**Answer: C) `3000`, `"prod"` — freeze is shallow; nested objects are still mutable** **Explanation:** `Object.freeze` prevents adding/removing/modifying **own properties** of the frozen object (silently in sloppy mode, `TypeError` in strict mode). However, it\'s **shallow** — nested objects (`config.db`) are not frozen and remain mutable. For deep freeze, you'd need a recursive function.Answer & Explanation
**Answer: B) `42`, `[]`** **Explanation:** `Object.defineProperty` provides fine-grained control over property descriptors. `writable: false` prevents changing the value (silently in sloppy mode). `enumerable: false` hides the property from `Object.keys()`, `for...in`, and `JSON.stringify`. `configurable: false` prevents redefining or deleting the property.Answer & Explanation
**Answer: A) `"red shape"`, `true`** **Explanation:** `Shape.call(this, color)` sets `this.color = 'red'` on the `Circle` instance. `describe()` is not on `Circle.prototype` — it\'s found up the prototype chain on `Shape.prototype`. `this.color` in `describe` resolves to `'red'`. `instanceof Shape` checks the prototype chain → `true`.Answer & Explanation
**Answer: B) `true`, `true`, `true`, `false`** **Explanation:** The `in` operator checks the **entire prototype chain** — both `type` (own) and `wheels` (prototype) are found. `hasOwnProperty` checks **only the instance\'s own properties** — `type` is own, `wheels` is inherited (not own). Use `hasOwnProperty` (or `Object.hasOwn(obj, key)`) to distinguish own vs inherited.Answer & Explanation
**Answer: B) `["visible","normal"]`, `["hidden","visible","normal"]`** **Explanation:** `Object.keys` returns only **enumerable own** properties. `Object.getOwnPropertyNames` returns **all own** properties (enumerable and non-enumerable). `hidden` is non-enumerable, so `Object.keys` skips it. `getOwnPropertyNames` includes it. Neither includes prototype properties.Answer & Explanation
**Answer: B) `"Alice"`, `"Property 'age' not found"`** **Explanation:** `Proxy` intercepts fundamental operations on an object. The `get` trap intercepts property access. When `safe.age` is accessed, the `get` trap checks if `'age'` is in `target` — it isn\'t, so returns the custom message. This pattern is useful for safe access, validation, default values, or observable objects.Answer & Explanation
**Answer: B) `{ alice: 'B', bob: 'A', carol: 'C' }`** **Explanation:** `Object.entries` returns `[['alice',85],['bob',92],['carol',78]]`. `reduce` builds a new object. Destructuring `[name, score]` in the callback extracts each pair. `85 >= 90` → false, `85 >= 80` → true → `'B'`. `92 >= 90` → true → `'A'`. `78 >= 90` → false, `78 >= 80` → false → `'C'`.Answer & Explanation
**Answer: B) `true`, `true`, `true`** **Explanation:** `Object.create(proto)` creates a new object with the specified prototype. `Object.setPrototypeOf(obj, proto)` changes the prototype of an **existing** object. Both result in the same prototype chain. `dog1.breathes` and `dog2.breathes` both find `breathes: true` via prototype lookup. Their prototypes are the same `animal` object → `true`. Note: `setPrototypeOf` on existing objects is slow — prefer `Object.create`.Answer & Explanation
**Answer: B) `{ theme: 'dark', lang: 'en', fontSize: 16, version: '2.0' }`** **Explanation:** Object spread copies enumerable own properties. Later properties override earlier ones. `defaults` provides all three properties. `userPrefs` overrides `theme` and `fontSize`. `lang: 'en'` from `defaults` is preserved (not in `userPrefs`). `version: '2.0'` is added last. This is the idiomatic pattern for merging/applying default settings.## Q. A developer transforms user data. What does this output? ```javascript const users = [ { name: "Alice", age: 25, active: true }, { name: "Bob", age: 17, active: false }, { name: "Carol", age: 30, active: true } ]; const result = users .filter(u => u.active && u.age >= 18) .map(u => u.name.toUpperCase()); console.log(result); ``` - A) `["ALICE", "BOB", "CAROL"]` - B) `["ALICE", "CAROL"]` - C) `["Alice", "Carol"]` - D) `["ALICE"]`
Answer & Explanation
**Answer: B) `["ALICE", "CAROL"]`** **Explanation:** `.filter()` keeps only users where `active === true` AND `age >= 18`: Alice (25, active) and Carol (30, active). Bob is excluded (not active and underage). `.map()` then uppercases the names.Answer & Explanation
**Answer: A) `3`, `2`, `1`** **Explanation:** `reduce` accumulates a frequency map. `"apple"` appears 3 times, `"banana"` 2 times, `"cherry"` 1 time. Using `acc[item] || 0` safely initializes missing keys to `0` before incrementing.Answer & Explanation
**Answer: B) `[2,4,6]`, `undefined`** **Explanation:** `map` creates and returns a **new array** with transformed values. `forEach` iterates for side effects and **always returns `undefined`**. In functional programming, `map` is preferred over `forEach` because it\'s a pure transformation that produces a new value without mutation.Answer & Explanation
**Answer: B) `64`, `64`** **Explanation:** `compose` applies functions **right-to-left**: `add1(3)=4`, `double(4)=8`, `square(8)=64`. `pipe` applies **left-to-right**: `add1(3)=4`, `double(4)=8`, `square(8)=64`. When the same functions are in the same logical order (right-to-left in compose = left-to-right in pipe), they produce the same result.Answer & Explanation
**Answer: B) `130`, `120`** **Explanation:** `calculateTotal` is **impure** — it depends on the external mutable variable `tax`. After `tax = 0.3`, the same input `100` produces `130` (not `120`). `pureCalculateTotal` is **pure** — same inputs always produce the same output (`100 * 1.2 = 120`). Pure functions are predictable, testable, and composable.Answer & Explanation
**Answer: A) `6`, `6`, `6`** **Explanation:** The curry function checks if it has received all required arguments (`fn.length` = 3). If not, it returns a new function collecting more args. All three call patterns eventually pass 3 args (1+2+3=6). Currying enables **partial application** and building specialized functions from general ones.Answer & Explanation
**Answer: B) `99`, `198`** **Explanation:** `mutated = original` creates an alias (same reference). `mutated[0].val = 99` mutates `original[0].val` to `99`. `immutable` spreads each item into a new object, so `original[0].val` is `99` at map time → `99 * 2 = 198`. The `immutable` array contains new objects, not references to originals.Answer & Explanation
**Answer: A) `[4,16,36,64,100]`, `true`** **Explanation:** Both produce the same result: even numbers squared. The `reduce` approach is a transducer-style single pass — no intermediate array created. `filter().map()` creates an intermediate array. For large datasets, single-pass is more memory-efficient. Both produce `[4, 16, 36, 64, 100]` and have the same length.Answer & Explanation
**Answer: B) `2`** **Explanation:** Memoization caches results by input. First `expensiveFn(5)` — cache miss, calls fn, `callCount=1`. Second `expensiveFn(5)` — cache hit, returns cached `25`, `callCount` unchanged. `expensiveFn(6)` — cache miss, calls fn, `callCount=2`. Only 2 actual function calls despite 3 invocations.Answer & Explanation
**Answer: A) `["hello","world"]`, `["hello","world"]`, `true`** **Explanation:** Words with length > 3: `"hello"` (5), `"world"` (5) pass. `"foo"` (3), `"bar"` (3), `"baz"` (3) fail (not strictly greater than 3). Both approaches use the same predicate logic and produce the same result. `longerThan(3)` is a curried predicate factory — `longerThan(3)` returns `word => word.length > 3`.Answer & Explanation
**Answer: B) `["hello","world","foo","bar"]`, `true`** **Explanation:** `flatMap` is equivalent to `map` followed by `flat(1)`. Each sentence is split into an array (`["hello","world"]`, `["foo","bar"]`). `flatMap` flattens one level automatically. Both produce `["hello","world","foo","bar"]` with length 4. `flatMap` is slightly more efficient (single iteration).Answer & Explanation
**Answer: B) `"Alice"`, `"Bob"`, `false`** **Explanation:** `setIn` recursively creates new objects at each level of the path — an immutable deep update (lens pattern). `state` is unchanged (`name` still `"Alice"`). `newState` is a new object tree where `name` is `"Bob"`. `state === newState` is `false` — they are different objects (immutable update). This pattern is fundamental in Redux reducers and functional state management.## Q. A developer uses class inheritance. What is the output? ```javascript class Shape { constructor(color) { this.color = color; } describe() { return `A ${this.color} shape`; } } class Circle extends Shape { constructor(color, radius) { super(color); this.radius = radius; } describe() { return `${super.describe()} with radius ${this.radius}`; } } const c = new Circle("red", 5); console.log(c.describe()); console.log(c instanceof Shape); ``` - A) `"A red shape with radius 5"`, `false` - B) `"A red shape with radius 5"`, `true` - C) `"A shape with radius 5"`, `true` - D) `TypeError: Must call super constructor`
Answer & Explanation
**Answer: B) `"A red shape with radius 5"`, `true`** **Explanation:** `super(color)` must be called before accessing `this` in a derived class constructor. `super.describe()` calls the parent method. `instanceof` walks the prototype chain, and `Circle` extends `Shape`, so `c instanceof Shape` is `true`.Answer & Explanation
**Answer: B) `5`, `6`, `TypeError: m.add is not a function`** **Explanation:** Static methods belong to the class itself, not instances. `MathHelper.add()` works; `m.add()` throws `TypeError`. Instance methods like `multiply` are accessible on the instance.Answer & Explanation
**Answer: A) `"Rex says Woof"`, `true`** **Explanation:** `super('Woof')` must be called in the subclass constructor before accessing `this`. `super.speak()` calls the parent\'s `speak()` method — which accesses `#sound` on the instance (`'Woof'`). Private fields are accessible within the class that defines them, so `speak()` can access `#sound`. `instanceof` checks the prototype chain → `true`.Answer & Explanation
**Answer: D) `3`, `undefined`, `0`** **Explanation:** Static fields/methods belong to the **class**, not instances. After 3 `new Config()` calls, `Config.instances = 3`. Accessing a static field via an instance (`new Config('c').instances`) returns `undefined` — instances don\'t inherit static properties. `Config.reset()` sets `instances` back to `0`.Answer & Explanation
**Answer: C) `100`, `Error: SyntaxError`** **Explanation:** Private class fields (`#field`) are a **syntax-level** restriction — accessing `#balance` outside the class body is a `SyntaxError` (caught at parse time, not runtime). The getter `balance` provides controlled public access. Private fields truly encapsulate data, unlike the `_convention` which is just a naming hint.Answer & Explanation
**Answer: B) `"Shape is abstract"`, `"78.54"`** **Explanation:** `new.target` inside a constructor refers to the class being constructed. If `Shape` is instantiated directly, `new.target === Shape` → throw. If a subclass extends `Shape`, `new.target` is the subclass (`Circle`) → passes. `Circle` overrides `area()` → works correctly. This is JavaScript\'s pattern for abstract classes.Answer & Explanation
**Answer: A) `true`, `"string"`** **Explanation:** Mixins are functions that take a base class and return an extended class. Composing `Serializable(Validatable(Entity))` creates a class that has methods from all three. `u` has `name` and `age` (2 keys) → `validate()` returns `true`. `serialize()` returns `JSON.stringify(u)` — a string. Mixins enable multiple-inheritance-like composition.Answer & Explanation
**Answer: A) `[1, 2, 3, 4]`** **Explanation:** Implementing `[Symbol.iterator]()` makes the class iterable. The method returns an iterator object with a `next()` function. Spread syntax (`[...new Range(1, 4)]`) uses the iterator protocol. The iterator yields `1, 2, 3, 4` then signals `done: true`. This makes the class work with `for...of`, spread, destructuring, etc.Answer & Explanation
**Answer: B) `"Celsius: 100"`, `"Fahrenheit: 212"`** **Explanation:** Class expressions can be returned from functions, assigned to variables, or passed as values. Here, `createClass` is a factory that creates classes with closured `name`. Each generated class captures a different `name` via closure. This pattern enables dynamic class generation and is used in higher-order patterns.Answer & Explanation
**Answer: A) `32`, `100`** **Explanation:** `new Temperature(0)` → `#celsius = 0`. `t.fahrenheit` getter: `0 * 9/5 + 32 = 32`. `t.fahrenheit = 212` setter: `#celsius = (212 - 32) * 5/9 = 100`. `t.celsius` getter returns `100`. Getters/setters let you expose derived/computed properties while maintaining encapsulation.Answer & Explanation
**Answer: B) `"Error: ReferenceError"`, `"After super: 1 undefined"`** **Explanation:** In a derived class, `this` is not available until `super()` is called. Accessing `this` before `super()` throws a `ReferenceError`. After `super()` is called, `this` is available. `this.x = 1` is set by `Base` constructor. `this.y` was never assigned (the assignment threw) → `undefined`. So `"After super: 1 undefined"`.## Q. A project uses ES Modules. Which statement is correct about the following import? ```javascript // math.js export const PI = 3.14159; export default function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } // app.js import add, { PI, multiply } from "./math.js"; ``` - A) This throws a `SyntaxError` — you cannot mix default and named imports - B) `add` is the default export; `PI` and `multiply` are named exports — this is valid - C) `add` will be `undefined` because default exports must use curly braces - D) `multiply` must be imported before `PI`
Answer & Explanation
**Answer: B) `add` is the default export; `PI` and `multiply` are named exports — this is valid** **Explanation:** ES Modules allow one default export and multiple named exports per file. Default imports have no curly braces; named imports use `{ }`. Combining both in one import statement is perfectly valid.Answer & Explanation
**Answer: B) Named exports are statically analyzable and enable better tree-shaking; default exports that export objects can prevent tree-shaking** **Explanation:** Bundlers (webpack, Rollup) perform tree-shaking by analyzing static imports. Named exports (`export const fn`) allow bundlers to know exactly which exports are used and eliminate unused ones. Default exports that export objects (`export default { fn1, fn2 }`) prevent tree-shaking because the whole object must be imported. Use named exports for utility functions to enable effective tree-shaking.Answer & Explanation
**Answer: B) Dynamic `import()` is a Promise-based expression that enables lazy/conditional loading of modules** **Explanation:** `import()` is an async function that returns a Promise resolving to the module\'s namespace object. It enables: code splitting (only load what\'s needed), conditional imports, lazy loading on user interaction, and runtime path determination. Static imports are hoisted and always loaded — dynamic imports are flexible runtime operations.Answer & Explanation
**Answer: C) One of them will log `undefined` due to the circular dependency — the import is a live binding but may not be initialized yet** **Explanation:** ES Modules handle circular imports with **live bindings** — the binding exists but may be `undefined` at first execution. `b.js` imports `a` from `a.js`, but `a.js` hasn\'t finished evaluating yet → `a` is `undefined` when `b.js` logs it. Circular dependencies are legal but can cause subtle initialization order bugs. Avoid them or restructure shared code into a third module.Answer & Explanation
**Answer: B) `2` — both imports share the same module singleton** **Explanation:** ES Modules are **singletons** — a module is evaluated once and cached. All imports of the same module share the same instance. Both `increment` and `inc2` reference the same function that closes over the same `count` variable. Two `increment()` calls → `count = 2`.Answer & Explanation
**Answer: B) `3.14`, `16`, `"utils-default"`** **Explanation:** `import * as utils` creates a **namespace object** containing all exports. Named exports are accessible as properties (`utils.PI`, `utils.square`). The default export is accessible as `utils.default`. The namespace object is **live** — if the module updates an exported variable, the namespace object reflects the update.Answer & Explanation
**Answer: B) `5`, `6`** **Explanation:** `export { name as alias }` re-exports a binding under a different name. This is the **renaming export** syntax — useful for keeping internal implementation names private while providing a cleaner public API. Consumers use `add` and `multiply` without knowing the internal naming convention.Answer & Explanation
**Answer: C) `"string"`, `undefined` (in plain Node.js without bundler)** **Explanation:** `import.meta` is a meta-property available in ES Modules. `import.meta.url` is always a string — the URL/path of the current module (set by the runtime). `import.meta.env` is injected by bundlers like Vite/webpack for environment variables — not available in plain Node.js. `import.meta` properties are host-defined, so they vary by environment.Answer & Explanation
**Answer: C) ES Modules use `import/export` (static, async-friendly, tree-shakeable); CommonJS uses `require/module.exports` (dynamic, synchronous, not tree-shakeable)** **Explanation:** Key differences: ESM uses `import/export` (static, hoisted, live bindings, async loading, tree-shakeable). CJS uses `require()` (dynamic, synchronous, cached, returns snapshot). ESM\'s static structure enables tree-shaking and better optimization. CJS\'s `require()` can be used conditionally or in loops. Node.js supports both, but they have interop nuances.Answer & Explanation
**Answer: B) A barrel/index file that aggregates exports, providing a single import point for consumers** **Explanation:** Re-exporting (`export { X } from './X.js'`) creates **barrel files** that aggregate multiple module exports. Consumers import from a single path instead of knowing the internal file structure. `export * from` re-exports all named exports. `export { default as Input }` re-exports a default export as a named export. Barrel files improve API ergonomics but can hurt tree-shaking if implemented poorly.## Q. A developer makes a GET request using `fetch()`. The server returns a 404 status. What is the behavior of the returned Promise? ```javascript fetch('/api/nonexistent') .then(response => console.log('resolved:', response.ok, response.status)) .catch(err => console.log('rejected:', err.message)); ``` - A) The Promise rejects because 404 is an error status - B) The Promise resolves with `response.ok === false` and `response.status === 404` - C) The Promise resolves with `response.ok === true` — 404 is a valid HTTP response - D) `fetch()` throws synchronously for 4xx status codes
Answer & Explanation
**Answer: B) The Promise resolves with `response.ok === false` and `response.status === 404`** **Explanation:** `fetch()` only rejects its Promise for **network failures** (DNS resolution failure, no connection, etc.) — not for HTTP error status codes. A 404 or 500 response is still a valid HTTP response, so the Promise resolves. Always check `response.ok` (true for status 200–299) or `response.status` to detect HTTP errors explicitly.Answer & Explanation
**Answer: B) This correctly handles both network errors and HTTP status errors, and properly reads the JSON body** **Explanation:** `response.json()` returns a **Promise** that resolves to the parsed JSON body — it must be `await`ed. Checking `response.ok` before calling `.json()` is the correct pattern to distinguish HTTP errors (which `fetch` resolves) from successful responses. The `throw` inside an `async` function returns a rejected Promise, which callers can `catch`.Answer & Explanation
**Answer: C) This correctly sends a POST request with a JSON-serialized body and proper Content-Type header** **Explanation:** To send JSON, you must: 1) set `method: 'POST'`, 2) set `Content-Type: application/json` header (tells the server how to parse the body), and 3) `JSON.stringify` the object (the `body` must be a string, `Blob`, or `FormData` — not a plain object). Omitting `JSON.stringify` sends `"[object Object]"` as the body.Answer & Explanation
**Answer: B) Throws `TypeError: body stream is locked` (or `body used already`) — response bodies can only be consumed once** **Explanation:** The `Response` body is a readable stream that can only be consumed **once**. After `response.json()` (or `response.text()`, `response.blob()`) is called, the body stream is fully read and cannot be read again. If you need to use the body multiple times, call `response.clone()` before reading: `const clone = response.clone()`.Answer & Explanation
**Answer: B) All three return Promises: `json()` parses the body as JSON, `text()` returns a raw string, `blob()` returns binary data as a `Blob`** **Explanation:** All response body reading methods return Promises and consume the body stream. Use `json()` for API responses returning JSON. Use `text()` for HTML, CSV, or plain text. Use `blob()` for images, audio, or file downloads. Use `arrayBuffer()` for low-level binary manipulation. Choose based on the `Content-Type` of the response.Answer & Explanation
**Answer: C) This correctly attaches a bearer token and implements a 5-second timeout using `AbortController`** **Explanation:** `AbortController` and its `signal` allow cancelling a `fetch` request. When `abort()` is called, the Promise rejects with an `AbortError`. This pattern combines: auth header attachment, a 5-second timeout using `setTimeout`, and cleanup with `clearTimeout` on success. `AbortController` actually cancels the underlying network request, not just the Promise.Answer & Explanation
**Answer: B) This runs all three requests in parallel and resolves when all complete; rejects if any fails** **Explanation:** `Promise.all` starts all Promises simultaneously — all three `fetch` calls are initiated at once. If the user, posts, and comments APIs each take 200ms, sequential awaits would take ~600ms total; `Promise.all` takes ~200ms. If any request fails, `Promise.all` rejects immediately. Use `Promise.allSettled` if partial failure is acceptable.Answer & Explanation
**Answer: C) `fetch` requires explicit `response.ok` checking for HTTP errors; `XMLHttpRequest` uses separate `onload`/`onerror` events; both need HTTP status checks** **Explanation:** Both APIs require manual HTTP status checking. `fetch` is Promise-based (cleaner with `async/await`), supports streaming, and has a modern API. `XMLHttpRequest` is callback-based, uses events (`onload`, `onerror`, `onprogress`), and is more verbose. Neither automatically throws for HTTP error codes — developers must check `response.ok` (fetch) or `xhr.status` (XHR). `fetch` is preferred in modern code.## Q. What is the correct order of events when JavaScript starts executing a program? ```javascript var name = 'Global'; function greet() { var name = 'Local'; return name; } greet(); ``` - A) Functions are executed immediately when declared; variables are set up last - B) 1) Global Execution Context is created (hoisting: `name = undefined`, `greet` = function); 2) Code runs line by line; 3) `greet()` creates a new Function Execution Context pushed onto the Call Stack - C) The Call Stack processes items from a queue (FIFO), not a stack - D) Each line creates its own separate execution context
Answer & Explanation
**Answer: B) 1) Global Execution Context is created (hoisting: `name = undefined`, `greet` = function); 2) Code runs line by line; 3) `greet()` creates a new Function Execution Context pushed onto the Call Stack** **Explanation:** JavaScript execution has two phases per context: **Creation Phase** (variables hoisted as `undefined`, function declarations fully hoisted, `this` bound) and **Execution Phase** (code runs line by line). When a function is called, a new Execution Context is pushed onto the Call Stack. When it returns, it is popped off. The Global Execution Context remains until the program ends.Answer & Explanation
**Answer: B) `[Global, a, b, c]` — each nested call adds a frame; on return each is popped in reverse order** **Explanation:** The Call Stack is a LIFO (Last In, First Out) data structure. Global is always the base. `a()` is called → pushed. `a` calls `b` → pushed. `b` calls `c` → pushed. At the peak: `[Global, a, b, c]`. `c` returns → popped. `b` returns → popped. `a` returns → popped. This is why it's called a "stack" and why deeply nested calls can cause stack overflows.Answer & Explanation
**Answer: B) Each `countdown` call pushes a new frame onto the Call Stack without ever popping; the stack overflows when its size limit is reached** **Explanation:** Every function call consumes a Call Stack frame. Without a base case, `countdown` calls itself infinitely. Each call adds a frame; none return to be popped. Eventually the engine's stack size limit (typically ~10,000–15,000 frames) is hit, throwing `RangeError: Maximum call stack size exceeded`. Fix: add a base case (`if (n <= 0) return`) or convert to iteration / use `setTimeout` for very deep recursion.Answer & Explanation
**Answer: B) `undefined`, `ReferenceError` — `var` is hoisted and initialized to `undefined`; `let` is hoisted but stays in the TDZ (uninitialized)** **Explanation:** During the **creation phase**, the engine: 1) creates the Variable Environment (`var` → `undefined`, function declarations → full function), 2) creates the Lexical Environment (`let`/`const` → hoisted but **uninitialized** in TDZ), 3) binds `this`. Only during the **execution phase** are values assigned. Accessing a `let` variable before its declaration line (while still in the TDZ) throws `ReferenceError`.Answer & Explanation
**Answer: B) `"outer"` — the scope chain walks from inner's context → outer's context → global; `x = 'outer'` is found first** **Explanation:** The **scope chain** is built when a function is defined (lexical scoping). When `inner()` runs, its execution context has a reference to the outer function's environment (where `x = 'outer'`), and that has a reference to the global environment (where `x = 'global'`). Variable lookup walks inward-to-outward and stops at the first match. This chain is created at definition time, not at call time.## # 25. Performance Optimization
## Q. A developer wants to prevent excessive API calls while typing in a search box. Which technique is correct? ```javascript function debounce(fn, delay) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } const search = debounce((query) => console.log("Searching:", query), 300); ``` - A) This is throttling — it limits calls to one per interval - B) This is debouncing — it waits for inactivity before calling the function - C) This is memoization — it caches the result of the function - D) This creates a memory leak because `timer` is never cleared
Answer & Explanation
**Answer: B) This is debouncing — it waits for inactivity before calling the function** **Explanation:** Debouncing delays function execution until after a period of inactivity. Each call resets the timer. Throttling would cap calls to a maximum frequency. Memoization caches results. `clearTimeout` prevents the memory leak.Answer & Explanation
**Answer: B) `16`, `16`, `25`, `"calls: 2"`** **Explanation:** First call `heavy(4)` — cache miss, computes `16`, stores it. Second call `heavy(4)` — cache hit, returns `16` without calling the function. `heavy(5)` — cache miss, computes `25`. Only 2 actual computations occur.Answer & Explanation
**Answer: A) Debounce delays execution until after a pause; throttle limits execution to once per interval** **Explanation:** **Debounce**: delays function until after `delay` ms of silence (no new calls). Use for search input (wait until user stops typing). **Throttle**: ensures function runs at most once per `interval` ms regardless of call frequency. Use for scroll/resize handlers. Rule of thumb: debounce = "wait for quiet time"; throttle = "rate-limit".Answer & Explanation
**Answer: B) `requestAnimationFrame` syncs DOM updates with the browser\'s repaint cycle (~60fps), preventing jank** **Explanation:** `requestAnimationFrame` schedules the callback just before the browser\'s next repaint. Unlike `setTimeout(fn, 16)` (which drifts and can miss frames), `rAF` is synchronized with the display refresh rate. The browser can also batch/optimize `rAF` callbacks and pause them in background tabs to save power.Answer & Explanation
**Answer: B) `DocumentFragment` batches all nodes and triggers a single reflow/repaint on insertion** **Explanation:** Each `appendChild` to a live DOM element can trigger reflow/repaint. `DocumentFragment` is an off-screen container — nodes added to it don\'t trigger reflows. When the fragment is appended to the DOM, all its children are inserted in one operation — a single reflow/repaint. For 1000 items, this is a significant performance gain.Answer & Explanation
**Answer: B) Virtual DOM minimizes expensive real DOM operations by computing the minimal diff (reconciliation) needed and applying only those changes in batch** **Explanation:** Direct DOM manipulation is expensive (triggers reflow/repaint). Virtual DOM frameworks (React, Vue) maintain an in-memory representation of the DOM. When state changes, a new virtual DOM is computed and **diffed** against the previous one. Only the minimal set of actual DOM changes is applied. For frequent updates, this batching approach can significantly reduce layout thrashing.Answer & Explanation
**Answer: B) Dividing the bundle into smaller chunks that are loaded on demand, reducing initial load time** **Explanation:** Code splitting (via `import()`, webpack, Rollup) divides the app into chunks. Only the initial chunk loads at startup; other chunks load when needed (route change, user interaction). This reduces Time to First Byte (TTFB) and Time to Interactive (TTI). Combined with lazy loading and route-based splitting, it dramatically improves perceived performance for large SPAs.Answer & Explanation
**Answer: B) Memory usage grows indefinitely, eventually causing slowdowns, tab crashes, or OOM errors** **Explanation:** A memory leak occurs when objects that are no longer needed are still referenced (preventing garbage collection). Here, `cache` grows every 100ms with 1MB arrays. GC cannot collect them because `leaky` holds a closure reference. Over time: slowdowns → UI freezes → browser tab crash (OOM). Use Chrome DevTools Memory profiler to detect heap growth and leaked object retention.Answer & Explanation
**Answer: B) Interleaving reads/writes forces the browser to synchronously recalculate layout repeatedly; batching reads then writes allows a single calculation** **Explanation:** Layout properties (`offsetHeight`, `clientWidth`, `getBoundingClientRect`, etc.) trigger a **synchronous layout calculation** to return an accurate value. If you write to the DOM first (invalidating layout) and then read, the browser is forced to recalculate layout synchronously. Batching all reads first (letting the browser defer layout) then writing avoids this expensive forced synchronous layout.Answer & Explanation
**Answer: B) `WeakRef` holds a weak reference — the object can be collected; `deref()` returns `undefined` after collection** **Explanation:** `WeakRef` allows you to hold a reference to an object without preventing its garbage collection. `deref()` returns the object if it still exists, or `undefined` if collected. Useful for caches where you want to allow eviction under memory pressure. The GC timing is non-deterministic — the output could be either depending on GC activity.Answer & Explanation
**Answer: B) Worker threads run in a separate thread — heavy computation doesn\'t block the main thread\'s UI rendering** **Explanation:** JavaScript is single-threaded. `heavySync()` blocks the event loop — the UI freezes for the entire duration. Web Workers run in a separate OS thread with their own event loop. Communication is via `postMessage` (structured clone or transferable objects). The main thread remains responsive. Use Workers for image processing, encryption, data transformation, or any CPU-intensive work.## Q. A developer implements a Singleton. Is the following a correct Singleton pattern? ```javascript const Database = (() => { let instance; function createInstance() { return { connection: "db://localhost" }; } return { getInstance() { if (!instance) instance = createInstance(); return instance; } }; })(); const db1 = Database.getInstance(); const db2 = Database.getInstance(); console.log(db1 === db2); ``` - A) `false` — each `getInstance()` call creates a new object - B) `true` — this correctly implements the Singleton pattern - C) `TypeError` — `instance` is not accessible outside `createInstance` - D) `false` — IIFE prevents Singleton behavior
Answer & Explanation
**Answer: B) `true` — this correctly implements the Singleton pattern** **Explanation:** The IIFE creates a private `instance` variable. `getInstance()` creates the instance only once (lazy initialization) and returns the same object on subsequent calls. `db1 === db2` is `true` because they reference the same object.Answer & Explanation
**Answer: C) `"listener1: 5"`, `"listener2: 10"`** **Explanation:** Both listeners are registered for `"data"`. When `emit("data", 5)` is called, all registered listeners execute with `data = 5`. This is the Publish-Subscribe pattern used extensively in Node.js\'s `EventEmitter`.Answer & Explanation
**Answer: B) `"square"`, `16`** **Explanation:** The Factory returns an object based on `type`. For `"square"` with `size = 4`, `area()` computes `4 ** 2 = 16`. The factory pattern decouples object creation from usage, making it easy to add new shapes.Answer & Explanation
**Answer: B) It adds logging behavior to `add` without modifying its source — separating cross-cutting concerns** **Explanation:** The Decorator pattern wraps a function/object to add behavior without modifying the original. `withLogging` is a higher-order function (decorator) that adds logging as a cross-cutting concern. The original `add` is unchanged and reusable. This pattern is the basis for TypeScript decorators, middleware, and aspect-oriented programming.Answer & Explanation
**Answer: B) `[1,2,3]`** **Explanation:** The Strategy pattern encapsulates interchangeable algorithms. The `Sorter` class delegates to a strategy function selected at runtime. Switching algorithms requires only changing the strategy, not the `Sorter` class. In real implementations, each strategy would have different algorithm implementations. Useful for payment processors, compression algorithms, validation rules, etc.Answer & Explanation
**Answer: B) The Command pattern encapsulates operations as objects, enabling queuing, undo/redo, logging, and remote execution** **Explanation:** The Command pattern turns operations into first-class objects with `execute()` and optionally `undo()`. This enables: **undo/redo** (keep a history of commands); **queuing** (store commands for later execution); **logging** (serialize command history for debugging/audit); **remote execution** (send serialized commands across a network). Used in text editors, game engines, and transactional systems.Answer & Explanation
**Answer: B) `"https://api.example.com"`, `true`** **Explanation:** `Proxy` wraps the target and intercepts operations. The `set` trap fires when attempting to write a property — here it throws. The `get` trap is not defined, so reads pass through to the target normally. This implements read-only objects without modifying the original. `e.message.includes('read-only')` → `true`.Answer & Explanation
**Answer: B) The Mediator centralizes inter-component communication — components don\'t talk to each other directly, reducing coupling** **Explanation:** The Mediator acts as a hub. Components (colleagues) communicate through the mediator, not directly with each other. This reduces dependencies from O(n²) (all-to-all) to O(n) (all-to-mediator). Examples: air traffic control (planes ↔ tower ↔ planes), chat rooms (users ↔ server ↔ users), React\'s lifting state up, Redux store. Contrast with Observer: Mediator has logic; Pub-Sub is passive.Answer & Explanation
**Answer: B) `"Processed: payload"`, `"Unauthorized"`** **Explanation:** Chain of Responsibility passes a request along a chain of handlers. Each handler decides to process or pass forward. Request 1: `token=true` → passes auth → `rateOk=true` → passes rate limit → processed. Request 2: `token=false` → auth handler rejects → `"Unauthorized"`. Used in middleware pipelines (Express, Koa), validation chains, event handling.Answer & Explanation
**Answer: B) `1`, `undefined`** **Explanation:** The Revealing Module Pattern uses an IIFE to create a private scope. `_count` is private — not exposed in the returned object. Only `increment`, `decrement`, `getCount` are public. `increment()` twice (→ 2), `decrement()` once (→ 1). `counter.getCount()` returns `1`. `counter._count` is `undefined` — the private variable is inaccessible from outside.Answer & Explanation
**Answer: C) `["a", "b", "c"]`** **Explanation:** Template Method defines the skeleton of an algorithm in the base class, letting subclasses override specific steps without changing the overall structure. `process()` is the template — it calls `parse` → `validate` → `format`. `CSVProcessor` overrides `parse` (split by comma) and `format` (trim). `validate` uses the default pass-through. Result: `["a","b","c"]`.## Q. A developer dynamically inserts user input into the DOM. Which code is vulnerable to XSS and what is the fix? ```javascript // Vulnerable function renderMessage(userInput) { document.getElementById("output").innerHTML = userInput; } // Fix — which is correct? ``` - A) Use `innerText` or `textContent` instead of `innerHTML` - B) Wrap the input in ` ``` - A) SRI prevents the script from accessing DOM APIs - B) SRI verifies the fetched resource matches the expected hash — preventing modified/compromised CDN resources from executing - C) SRI only works with CSS files, not JavaScript - D) SRI prevents the script from making network requests
Answer & Explanation
**Answer: B) SRI verifies the fetched resource matches the expected hash — preventing modified/compromised CDN resources from executing** **Explanation:** SRI mitigates supply chain attacks — if a CDN is compromised and serves a modified script, the `integrity` hash won\'t match and the browser will block execution. The browser computes the hash of the downloaded resource and compares it to the `integrity` attribute. If they don\'t match, the script is not executed. Always use SRI with third-party CDN resources.Answer & Explanation
**Answer: B) JWTs in `localStorage` are accessible to any JavaScript on the page — vulnerable to XSS attacks that can steal the token and impersonate the user** **Explanation:** A token in `localStorage` is readable by any script on the page. If an XSS vulnerability exists, an attacker can run `localStorage.getItem('jwt')` and exfiltrate the token. Safer: store JWTs in `HttpOnly` cookies (not accessible to JavaScript). Tradeoff: HttpOnly cookies require CSRF protection. For SPAs, implement a layered approach: short-lived access tokens in memory + HttpOnly refresh token cookies.## Q. A developer reads a DOM property inside a loop. What is the performance problem? ```javascript // Slow for (let i = 0; i < 1000; i++) { document.getElementById("box").style.width = i + "px"; console.log(document.getElementById("box").offsetWidth); // forces reflow } ``` - A) `getElementById` is not safe inside loops - B) Reading `offsetWidth` inside the write loop forces synchronous reflow (layout thrashing) - C) `style.width` should use `classList` instead - D) `console.log` is the performance bottleneck
Answer & Explanation
**Answer: B) Reading `offsetWidth` inside the write loop forces synchronous reflow (layout thrashing)** **Explanation:** Writing styles (invalidates layout) and then reading layout properties (`offsetWidth`, `getBoundingClientRect`) forces the browser to synchronously recalculate layout. The fix is to batch reads together and writes together, or use `requestAnimationFrame`.Answer & Explanation
**Answer: B) Changing geometric properties (`width`, `height`, `margin`) triggers reflow; changing visual properties (`color`, `background`) triggers repaint only** **Explanation:** A **reflow** (layout) recalculates element positions and sizes — it is expensive. A **repaint** just redraws pixels for visual changes without affecting layout — cheaper. Reflows always trigger a repaint, but not vice versa. Use CSS `transform` and `opacity` for animations as they can be composited on the GPU without triggering reflow.Answer & Explanation
**Answer: B) The sequence of steps the browser takes to convert HTML, CSS, and JavaScript into pixels on the screen** **Explanation:** The Critical Rendering Path: 1) Parse HTML → DOM. 2) Parse CSS → CSSOM. 3) Combine DOM + CSSOM → Render Tree. 4) Layout (calculate positions/sizes). 5) Paint (draw pixels). 6) Composite (GPU layers). Optimizing the CRP (inline critical CSS, defer non-critical JS, minimize render-blocking resources) reduces Time to First Paint and Time to Interactive.Answer & Explanation
**Answer: B) `will-change` hints to the browser that the element will be transformed, enabling GPU compositing and avoiding repaints during animation** **Explanation:** `will-change: transform` tells the browser to promote the element to its own compositing layer in advance. During animation, the GPU handles the layer composite without triggering reflow/repaint. Use sparingly — every composited layer uses GPU memory. Don\'t apply it to everything; only elements with frequent transitions/animations that cause jank.Answer & Explanation
**Answer: B) `DOMContentLoaded` fires when HTML is parsed (no waiting for images/CSS); `load` fires when ALL resources (images, stylesheets, iframes) are loaded** **Explanation:** `DOMContentLoaded` is the right event to initialize JavaScript that only needs DOM structure. `load` is needed when you require computed sizes of images or iframes. For performance, minimize work in both event handlers. Most app initialization should be done as early as possible — even in ` ``` - A) `defer` and `async` both block HTML parsing - B) `defer` downloads in parallel and executes in order after HTML parsing; `async` downloads in parallel and executes immediately when ready (order not guaranteed) - C) `async` preserves execution order; `defer` does not - D) `defer` only works with inline scriptsAnswer & Explanation
**Answer: B) `defer` downloads in parallel and executes in order after HTML parsing; `async` downloads in parallel and executes immediately when ready (order not guaranteed)** **Explanation:** `defer`: downloads while HTML parses; executes in document order after parsing completes, before `DOMContentLoaded`. Good for scripts that depend on DOM or each other. `async`: downloads while HTML parses; executes immediately when downloaded, interrupting parsing. Order not guaranteed. Good for independent analytics/tracking scripts. Neither blocks HTML parsing during download.## Q. A developer registers a Service Worker. What does the following do? ```javascript if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js") .then(reg => console.log("SW registered:", reg.scope)) .catch(err => console.log("SW failed:", err)); } ``` - A) Registers a Web Worker for parallel computation - B) Registers a Service Worker that can intercept network requests and cache assets - C) Enables push notifications without user permission - D) Creates a shared worker accessible across all browser tabs
Answer & Explanation
**Answer: B) Registers a Service Worker that can intercept network requests and cache assets** **Explanation:** A Service Worker is a script that runs in the background, separate from the web page. It acts as a proxy for network requests, enabling offline support via caching strategies (Cache-First, Network-First, etc.), background sync, and push notifications.Answer & Explanation
**Answer: B) Returns cached response if available; falls back to network if not cached** **Explanation:** Cache-First prioritizes the cache for speed, making the app work offline or in low-network conditions. If the resource is not cached, it fetches from the network. Contrast with Network-First (tries network, falls back to cache) for frequently updated content.Answer & Explanation
**Answer: B) A Web App Manifest, a Service Worker, and HTTPS** **Explanation:** The three PWA requirements: 1) **Web App Manifest** (`manifest.json`) — metadata for add-to-homescreen (name, icons, display mode). 2) **Service Worker** — enables offline capability, background sync, push notifications (requires HTTPS). 3) **HTTPS** — required by browsers for Service Workers (except `localhost`). Together these enable installability, offline support, and app-like experience.Answer & Explanation
**Answer: B) All network requests from the controlled page — the SW can respond with cached data, modify requests, or pass through to the network** **Explanation:** The `fetch` event fires for every network request from controlled pages (except navigations with `navigate` scope issues). `event.respondWith()` intercepts the request and provides a response. The Service Worker acts as a programmable network proxy, enabling caching strategies, offline support, and request transformation.Answer & Explanation
**Answer: B) `beforeinstallprompt` allows the app to defer and customize the browser\'s install prompt for the PWA** **Explanation:** By default, browsers show an install banner automatically. `e.preventDefault()` suppresses it, storing the event. The app can show a custom install button at the right moment (after meaningful interaction). `deferredPrompt.prompt()` shows the native browser install dialog. `userChoice` tells you if the user accepted. This pattern improves install conversion rates.Answer & Explanation
**Answer: B) The app opens without browser UI chrome (no address bar, back/forward buttons) — appears like a native app** **Explanation:** `display: standalone` removes browser chrome (address bar, navigation buttons) when launched from the homescreen. The app gets its own window, taskbar entry, and splash screen. `fullscreen` goes further (no OS chrome). `minimal-ui` keeps some navigation. `browser` is the default tab experience. Standalone is the most common choice for app-like PWAs.Answer & Explanation
**Answer: B) Background Sync enables deferred actions (form submissions, data sync) to be retried when the user regains connectivity** **Explanation:** Background Sync registers a sync event with the Service Worker. If the user submits a form offline, the action is queued. When connectivity is restored, the browser fires a `sync` event in the Service Worker, which then makes the deferred request. This ensures data isn\'t lost due to intermittent connectivity — critical for mobile-first applications.Answer & Explanation
**Answer: B) The app must be on HTTPS, have a valid manifest, and have an active Service Worker** **Explanation:** Chrome\'s install criteria: 1) HTTPS. 2) Valid manifest with `name`/`short_name`, `icons` (192px + 512px), `start_url`, and `display`. 3) Registered Service Worker with a `fetch` handler. 4) Has not been dismissed by the user recently. Lighthouse PWA audit checks all these criteria and reports installability issues with specific fixes.Answer & Explanation
**Answer: B) Returns the cached response immediately while fetching an update in the background** **Explanation:** Stale-While-Revalidate is optimal for non-critical, frequently changing resources (news feeds, social content). The user sees stale (but fast) content immediately. In the background, the Service Worker fetches the latest version and updates the cache — the next visit shows fresh content. This balances speed (serve cached) with freshness (update in background). Libraries like Workbox implement this easily.Answer & Explanation
**Answer: B) The Push API allows servers to send messages to Service Workers even when the app is closed** **Explanation:** Push works even when the browser is closed: the push service (browser vendor\'s) receives the server\'s message and wakes the Service Worker. `event.waitUntil` keeps the SW alive while showing the notification. The user must grant notification permission. The server sends pushes via the Web Push Protocol using VAPID keys. This enables PWAs to re-engage users like native apps.Answer & Explanation
**Answer: B) New SW installs while old SW controls the page; new SW waits in "waiting" state until all tabs close or `skipWaiting()` is called** **Explanation:** SW update lifecycle: 1) Browser detects new SW file. 2) New SW **installs** (`install` event). 3) New SW enters **waiting** state (old SW still controls pages). 4) When all controlled tabs close, new SW **activates**. Use `self.skipWaiting()` in `install` + `clients.claim()` in `activate` to take control immediately (with caution — can cause version mismatches).## Q. A developer needs to deep clone an object without using a library. Which approach is correct and safe for JSON-serializable data? ```javascript const original = { a: 1, b: { c: 2 }, d: [3, 4] }; // Option A const clone1 = Object.assign({}, original); // Option B const clone2 = JSON.parse(JSON.stringify(original)); // Option C const clone3 = { ...original }; ``` - A) Option A — `Object.assign` deep clones all nested objects - B) Option B — `JSON.parse(JSON.stringify(...))` creates a true deep clone for JSON-safe data - C) Option C — spread operator deep clones arrays and objects - D) All three produce identical deep clones
Answer & Explanation
**Answer: B) Option B — `JSON.parse(JSON.stringify(...))` creates a true deep clone for JSON-safe data** **Explanation:** Both `Object.assign` and spread `{...}` perform **shallow copies** — nested objects still share references. `JSON.parse(JSON.stringify())` creates a true deep clone but loses `undefined`, `Date` objects, `functions`, and circular references. For production, use `structuredClone()` (modern) or a library like Lodash\'s `_.cloneDeep`.Answer & Explanation
**Answer: B) This correctly allows removal because the same function reference is stored** **Explanation:** `removeEventListener` requires the **exact same function reference** used in `addEventListener`. By storing `this.handleClick = this.handleClick.bind(this)` in the constructor, the same reference is used for both adding and removing. Anonymous functions or inline `bind()` calls in `addEventListener` cannot be removed.Answer & Explanation
**Answer: A) `[1,2,3,4,5]`, `[1,2,[3,[4,[5]]]]`, `[1,2,3,[4,[5]]]`** **Explanation:** `.flat(depth)` flattens the array by the specified depth. `Infinity` flattens completely. `flat(1)` removes one level of nesting. `flat(2)` removes two levels. This is an ES2019 built-in method.Answer & Explanation
**Answer: A) `2`, `"Bob"`** **Explanation:** `Object.groupBy()` (ES2024) groups array items into an object by the return value of the callback. `"Engineering"` gets Alice and Carol (length `2`); `"Marketing"` gets Bob. Use `.reduce()` as a polyfill for older environments.Answer & Explanation
**Answer: C) `1`, `"undefined"`, `"number"`** **Explanation:** The expression `let x = (y = 0)` is evaluated right-to-left. `y` is never declared with `let`/`var`/`const`, so it becomes an implicit global variable. `x` is block-scoped to `foo`. After the call, `typeof x` is `"undefined"` (no such variable in outer scope) and `typeof y` is `"number"` (global `y` holds `1` after `y++`).Answer & Explanation
**Answer: C) `60`** **Explanation:** `filter` keeps only even numbers `[2, 4, 6, 8, 10]`. `map` doubles each → `[4, 8, 12, 16, 20]`. `reduce` sums all values starting from `0` → `4 + 8 + 12 + 16 + 20 = 60`.Answer & Explanation
**Answer: B) `Caught: Network error`, then `Result: null`** **Explanation:** `fetchData()` rejects immediately. The `catch` block in `getData()` handles the error, logs `'Caught: Network error'`, and returns `null`. The `.then()` on the resolved `getData()` promise then logs `'Result: null'`.Answer & Explanation
**Answer: C) `SyntaxError: A class may only have one constructor`** **Explanation:** JavaScript classes do not support constructor overloading. Defining more than one `constructor` in a class body is a syntax error and throws `SyntaxError` before any code executes. Use default parameters or factory patterns to simulate overloading.Answer & Explanation
**Answer: B) `1`, `2`, `1`, `3`** **Explanation:** Each call to `createCounter()` creates a new closure with its own independent `count` variable. `counter1` and `counter2` do not share state. `counter1` increments to 1, 2, then 3. `counter2` starts its own sequence from 1.Answer & Explanation
**Answer: C) `Name: Guest, Age: 18, Country: undefined`** **Explanation:** The `= {}` at the end of the parameter list provides a default empty object when no argument is passed, preventing a `TypeError`. `name` and `age` use their defaults (`'Guest'` and `18`). `country` has no default, so it is `undefined`.Answer & Explanation
**Answer: B) `A`, `C`, `B`** **Explanation:** Even with a delay of `0`, `setTimeout` places its callback in the macrotask queue. The call stack must be empty before the event loop picks it up. So `'A'` and `'C'` are logged synchronously first, then `'B'` is logged after `main()` returns.Answer & Explanation
**Answer: B) `false`** **Explanation:** Due to IEEE 754 binary floating-point representation, `0.1 + 0.2` evaluates to `0.30000000000000004`, not exactly `0.3`. To safely compare floating-point numbers, use `Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON`.Answer & Explanation
**Answer: C) `I'm John, 25 yrs old`** **Explanation:** The arrow function inside `getProfile()` does not have its own `this`. It lexically inherits `this` from the enclosing `getProfile` method, which was called on `user`. Therefore `this.name` is `'John'` and `this.age` is `25`.Answer & Explanation
**Answer: D) `I'm , undefined yrs old`** **Explanation:** When `profile()` is called as a plain function (not as a method), `this` refers to the global object (`window` in browsers). `window.name` defaults to `''` (empty string) and `window.age` is `undefined`. Arrow functions solve this; alternatively, use `.bind(this)` or store `const self = this`.Answer & Explanation
**Answer: C) `"1undefined"`** **Explanation:** The named function expression `function f(){}` is truthy, so the `if` block runs. However, the name `f` is only accessible inside the function expression\'s own body — it is not in scope inside the `if` block. `typeof f` returns `"undefined"` (not a `ReferenceError`, since `typeof` is safe for undeclared names). `1 + "undefined"` coerces to `"1undefined"`.Answer & Explanation
**Answer: C) `Vehicle { model: 'Honda', color: 'white', year: '2010', country: 'UK' }`** **Explanation:** Function declarations are fully hoisted, meaning both the name and the implementation are available throughout the entire scope before execution. `new Vehicle(...)` works even before the declaration line because the engine hoists the entire `Vehicle` function to the top.Answer & Explanation
**Answer: C) `undefined`** **Explanation:** Without `new`, `Vehicle` is called as a plain function. `this` refers to the global object, so the properties are set on `window`/`global`. The function has no explicit `return` statement, so it returns `undefined`, which is assigned to `car`.Answer & Explanation
**Answer: B) `true` — both syntaxes produce equivalent objects** **Explanation:** ES6 property shorthand `{ name, age }` is syntactic sugar for `{ name: name, age: age }`. Both objects have identical property values, so `user1.name === user2.name` is `true`.Answer & Explanation
**Answer: C) `Caught: Error occurred`** **Explanation:** `Promise.all()` short-circuits on the first rejection. As soon as `p3` rejects with `'Error occurred'`, the whole `Promise.all()` rejects immediately, regardless of the other pending promises. The `.catch()` handler receives the rejection reason.Answer & Explanation
**Answer: B) `Rex barks.`, `true`, `true`** **Explanation:** `Dog.prototype` overrides `speak`, so `dog.speak()` logs `'Rex barks.'`. `Object.create(Animal.prototype)` links the prototype chain, making `dog instanceof Animal` return `true`. `Dog.prototype.constructor = Dog` correctly restores the constructor reference.Answer & Explanation
**Answer: C) `undefined`** **Explanation:** JavaScript\'s Automatic Semicolon Insertion (ASI) inserts a semicolon after the `return` keyword because a line break follows. The function effectively returns `undefined`, and the object literal becomes unreachable dead code. To fix this, place the opening brace on the same line as `return`.Answer & Explanation
**Answer: B) `{ a: 1, b: 3, c: 4 }`** **Explanation:** When spreading multiple objects, later properties overwrite earlier ones with the same key. `obj2.b` (value `3`) overwrites `obj1.b` (value `2`). Spread creates a shallow merge — properties from `obj2` take precedence.Answer & Explanation
**Answer: C) `1`, `20`** **Explanation:** The spread operator creates a **shallow copy**. Primitive values (`x`) are copied by value, so `copy.x = 10` does not affect `original.x`. Nested objects (`y`) are copied by reference, so `copy.y` and `original.y` point to the same object — mutating `copy.y.z` also changes `original.y.z`.Answer & Explanation
**Answer: B) `undefined`, then `ReferenceError: Cannot access 'letVariable' before initialization`** **Explanation:** `var` is hoisted and initialized to `undefined`, so the first log succeeds. `let` is hoisted but not initialized — it sits in the **Temporal Dead Zone (TDZ)** from the start of the block until the declaration line. Accessing it before declaration throws a `ReferenceError`.Answer & Explanation
**Answer: A) `true`, `false`, `true`, `false`** **Explanation:** `[] == false`: `[]` coerces to `''`, `false` coerces to `0`, `''` coerces to `0` — `0 == 0` is `true`. `[] === false`: different types, so `false`. `null == undefined` is a special case defined as `true` in the spec. `null === undefined`: different types, so `false`. Always prefer `===` to avoid unexpected coercion.Answer & Explanation
**Answer: B) `[[], [1], [2], [2,1], [3], [3,1], [3,2], [3,2,1]]`** **Explanation:** The accumulator starts as `[[]]`. For each `value`, the existing subsets are mapped to prepend `value`, and the results are concatenated onto the current subsets. After `1`: `[[], [1]]`. After `2`: `[[], [1], [2], [2,1]]`. After `3`: `[[], [1], [2], [2,1], [3], [3,1], [3,2], [3,2,1]]` — 2³ = 8 subsets total.Answer & Explanation
**Answer: A) `red/tv` and `silver/phone` are removed; `blue/phone` and `green/phone` remain** **Explanation:** `excludes.some()` checks if any rule matches the item. `red/tv` matches `{k:'type', v:'tv'}` and `silver/phone` matches `{k:'color', v:'silver'}` — both are excluded. `blue/phone` and `green/phone` match no rules and are kept.Answer & Explanation
**Answer: C) `{ "a/b/c": 12, "a/b/d": "Hello World", "a/b/e": null, "a/f": [1,2,3] }`** **Explanation:** The `flatten` function recurses into nested plain objects, building up path keys with `/` separators. Arrays are not recursed (they are treated as leaf values), so `"a/f"` holds the entire `[1,2,3]` array. `null` is also treated as a leaf because `val != null` catches it.Answer & Explanation
**Answer: B) `2`** **Explanation:** Three `addEdge` calls set `numberOfEdges` to `3`. `removeEdge(1, 3)` finds and splices both directions from the adjacency list and decrements `numberOfEdges` once (only when `~index1` is true). Result: `3 - 1 = 2`.Answer & Explanation
**Answer: C) `3`** **Explanation:** The longest substring without repeating characters in `"abcabcbb"` is `"abc"` (length 3). The algorithm uses a hash map to store the last-seen index of each character and moves the `start` pointer forward whenever a duplicate is found within the current window.Answer & Explanation
**Answer: C) `6`** **Explanation:** The algorithm tracks `currentSum` — if adding the next element would make it negative, it resets to `0`. The subarray `[4, -1, 2, 1]` gives the maximum sum of `6`. `acc` always holds the best sum seen so far. Note: this variant returns `0` for all-negative arrays (it never goes below `0`).Answer & Explanation
**Answer: B) `3` — evaluates to `(sortedArr.length - 1) / 2 = 7 / 2 = 3`** **Explanation:** The comma operator evaluates each operand left-to-right and returns the **rightmost** value. `(0, sortedArr.length - 1)` returns `sortedArr.length - 1 = 7`. So `mid = Math.floor(7 / 2) = 3`. This is likely an unintentional use of the comma operator — the developer probably meant `Math.floor((sortedArr.length - 1) / 2)`, which coincidentally produces the same result here.Answer & Explanation
**Answer: B) `[[1,6],[8,10],[15,18]]`** **Explanation:** After sorting by start, the reduce processes each interval. `[1,3]` and `[2,6]` overlap (`3 > 2`), so they merge to `[1,6]`. `[8,10]` does not overlap with `[1,6]`, so it is kept separately. `[15,18]` does not overlap with `[8,10]`, so it is kept. Result: `[[1,6],[8,10],[15,18]]`.Answer & Explanation
**Answer: C) `true`, `false`** **Explanation:** `rect1` spans x:[250,400], y:[250,350]. `rect2` spans x:[100,400], y:[100,300]. They share y overlap (250 < 300) and x overlap, so `true`. `rect3` spans x:[450,600], y:[450,550]. `rect2`\'s right edge is `x=400`, which is less than `rect3.x=450` — the condition `rect2.x + rect2.width > rect3.x` → `400 > 450` is `false`, so no overlap.Answer & Explanation
**Answer: C) `9`** **Explanation:** Iterating: `el=1` → largest=1, second=-1. `el=10` → largest=10, second=1. `el=2` → 2 > second(1), so second=2. `el=9` → 9 > second(2), so second=9. Final: `9`. Note: this implementation has a limitation — it initialises both sentinels to `-1`, so it fails for all-negative arrays.Answer & Explanation
**Answer: B) A stray semicolon (`;`) after `[...chain, result])` prematurely terminates the arrow function expression before the outer `)` closes** **Explanation:** The inner `.then(result => [...chain, result])` is correct, but the `;` immediately after it ends the `promiseChain.then(chain => ...)` callback before its closing `)` — making it a syntax error. The fix is to remove that semicolon so the return value of `currentTask.then(...)` properly flows back as the resolved value.Answer & Explanation
**Answer: C) `4`** **Explanation:** The first call has `start=0`, `end=6`. `mid = Math.floor(6/2) = 3`. `nums[3] = 4` becomes the root. This mid-point selection ensures the tree is height-balanced: left subtree holds `[1,2,3]` and right subtree holds `[5,6,7]`.Answer & Explanation
**Answer: C) `24`** **Explanation:** The number of permutations of `n` distinct characters is `n!`. For `"abcd"` (4 characters), that is `4! = 4 × 3 × 2 × 1 = 24`. The algorithm picks each character as the first element, then recursively permutes the remaining string, and concatenates all results.Answer & Explanation
**Answer: B) `true` — `words[i][j] === words[j][i]` holds for all positions** **Explanation:** A valid word square requires that the grid is symmetric across its main diagonal (i.e., it reads the same horizontally and vertically). Checking every `(i,j)` pair: `words[0][1]='b'=words[1][0]`, `words[0][2]='c'=words[2][0]`, `words[1][2]='r'=words[2][1]`, `words[2][3]='y'=words[3][2]`, etc. — all pairs match, so the function returns `true`.Answer & Explanation
**Answer: B) When `left` exceeds `400` pixels** **Explanation:** Each tick calculates the time elapsed since the last frame (`deltaT`) and advances `left` proportionally (`10 * deltaT / 16` pixels). When `left` exceeds `400`, `clearInterval(timer)` stops the interval. This delta-time technique makes the animation speed independent of frame timing jitter — unlike simply incrementing by a fixed number each tick.Answer & Explanation
**Answer: A) Automatic retry with exponential backoff for transient failures** **Explanation:** `fetchWithRetry` retries on any error up to `maxRetries`. Exponential backoff (`delay * attempt`) increases wait time between retries (1s, 2s, 3s...) to avoid overwhelming a struggling server. On the last attempt, the error is re-thrown. This handles transient failures (network blips, 503s) gracefully. In production, also add jitter (randomized delay) and a circuit breaker.Answer & Explanation
**Answer: B) AbortController allows cancelling in-flight fetch requests to prevent memory leaks and state updates on unmounted components** **Explanation:** Without cancellation, a fetch completes even if the user navigated away — updating state on an unmounted component (memory leak/warning). `AbortController` provides a `signal` passed to `fetch`. Calling `controller.abort()` rejects the promise with `AbortError`. In React, call `controller.abort()` in the `useEffect` cleanup function. This is the correct pattern for all production fetch operations.Answer & Explanation
**Answer: B) It provides a lazy, iterable stream of pages — processing each page as it arrives without loading all data into memory** **Explanation:** `async function*` (async generator) yields values asynchronously. `for await...of` consumes them one at a time. Each iteration fetches the next page and processes it before fetching the next. This prevents loading an entire dataset into memory at once (critical for large datasets). The generator encapsulates the pagination logic cleanly, and consumers use a standard `for await` loop.Answer & Explanation
**Answer: B) `"JSON failed: TypeError"`, `true`, `true`** **Explanation:** `JSON.stringify` throws `TypeError` on circular references. `structuredClone` (ES2022) handles: circular references, `Date` (preserves type), `Map`, `Set`, `ArrayBuffer`, `RegExp`. It does NOT clone: functions, DOM nodes, or class instances (they're plain objects). `cloned.date instanceof Date` → `true` (unlike JSON which converts to string).Answer & Explanation
**Answer: B) This implements a sliding window rate limiter — tracks timestamps and rejects requests exceeding the limit** **Explanation:** The sliding window approach maintains a list of request timestamps. Old timestamps (outside `windowMs`) are removed. If remaining count ≥ `maxRequests`, the request is rejected. Otherwise, the timestamp is added and the function runs. This prevents bursts of requests. In production, also implement server-side rate limiting — client-side alone is bypassable.Answer & Explanation
**Answer: B) `"Welcome, Alice"` only — `unsub()` removes the handler** **Explanation:** `subscribe` returns a cleanup function that removes the handler from the `Set`. After `unsub()`, the handler is deleted. The second `publish` finds the event in the Map but the `Set` is empty — `forEach` runs 0 times. Using `Set` instead of `Array` prevents duplicate handlers and enables O(1) deletion. The cleanup pattern prevents memory leaks.Answer & Explanation
**Answer: B) `IntersectionObserver` with a sentinel element provides a performant infinite scroll without scroll event listeners** **Explanation:** A sentinel element at the bottom of the list is observed. When it enters the viewport (plus 200px of lookahead margin via `rootMargin`), new items load. `loading` flag prevents duplicate requests. `IntersectionObserver` is far more performant than `scroll` event listeners. The `rootMargin: '200px'` preloads content slightly before the user reaches the end, preventing blank space.Answer & Explanation
**Answer: B) Optimistic updates immediately reflect the user\'s action with rollback on server failure** **Explanation:** Optimistic UI updates assume success and immediately update the UI, making the app feel instant. If the server fails, roll back to the previous state and show an error. This pattern requires: 1) immediate state update; 2) async server request; 3) rollback logic on error. Used by Twitter (likes), GitHub (reactions), and most social platforms for immediate feel without perceived latency.Answer & Explanation
**Answer: B) `Promise.allSettled` with partial results allows the dashboard to load even if some APIs fail** **Explanation:** `Promise.all` would fail the entire dashboard if any single API fails. `Promise.allSettled` waits for all and returns success/failure for each. The pattern extracts partial results (showing what loaded) while collecting errors for logging. Dashboards with multiple independent data sources should always use `allSettled` — users see partial data instead of a blank error page.## Q. What is the output of these type coercion expressions? ```javascript console.log(1 + "2" + 3); console.log(1 + 2 + "3"); console.log("5" - 3); console.log("5" * "2"); console.log(true + true + "1"); ``` - A) `"123"`, `"33"`, `2`, `10`, `"21"` - B) `"123"`, `"33"`, `2`, `10`, `"21"` - C) `6`, `"33"`, `2`, `10`, `"21"` - D) `"123"`, `3`, `"53"`, `10`, `"21"`
Answer & Explanation
**Answer: A) `"123"`, `"33"`, `2`, `10`, `"21"`** **Explanation:** `+` is left-to-right: `1 + "2"` → `"12"` (string concat), then `"12" + 3` → `"123"`. `1 + 2` → `3` (numeric), then `3 + "3"` → `"33"`. `-` coerces to number: `"5" - 3 = 2`. `*` coerces both: `5 * 2 = 10`. `true + true` → `1 + 1 = 2`, then `2 + "1"` → `"21"`. Rule: `+` with any string = concatenation; `-`, `*`, `/` always coerce to numbers.Answer & Explanation
**Answer: A) `""`, `"[object Object]"`, `0`, `0`, `NaN`** **Explanation:** `[] + []`: both arrays coerce to `""` (empty string via `.toString()`) → `""`. `[] + {}`: `[]` → `""`, `{}` → `"[object Object]"` → `"[object Object]"`. `{} + []` in expression context (not as a statement): `{}` is an empty object → `"[object Object]"`, `[]` → `""` → `"[object Object]"`. `+[]`: unary `+` converts `[]` → `""` → `0`. `+{}`: `{}` → `NaN`. These are notorious JavaScript gotchas.Answer & Explanation
**Answer: A) `true`, `false`, `false`, `false`, `false`, `false`** **Explanation:** `null == undefined` → `true` (special spec rule: they are only equal to each other). `null == 0` → `false` (null only equals `undefined` with `==`). `null == false` → `false` (same rule). `NaN == NaN` → `false` (NaN is never equal to anything, even itself). Use `Number.isNaN(x)` to detect NaN, and `=== undefined` only for `undefined` checks.Answer & Explanation
**Answer: D) All four produce `[1, 2, 3, 4]` but with different time complexities** **Explanation:** All four correctly deduplicate. **A & D** (Set-based): O(n) — best for primitives. **B** (filter + indexOf): O(n²) — readable but slow for large arrays. **C** (reduce + includes): O(n²) — similarly readable but O(n²). For primitives, use `[...new Set(arr)]` or `Array.from(new Set(arr))`. For objects (deduplicating by property), use `reduce` with a `Map`. Note: Sets don\'t deduplicate object references unless they are literally the same reference.Answer & Explanation
**Answer: C) All three correctly reverse `"hello"` to `"olleh"`** **Explanation:** **A**: iterates from end, builds reversed string. **B**: `[...s]` spreads string to Unicode-safe characters; `reduce` prepends each character — `ch + acc` reverses the order. **C**: recursively moves the first character to the end. All produce `"olleh"`. Note: `[...s]` (spread) is Unicode-safe for emoji/surrogate pairs; `s.split('')` may split emoji into two half-characters.Answer & Explanation
**Answer: B) Logs once after 300ms of silence: `"Searching: java"` — only the last call executes** **Explanation:** Debounce resets the timer on every new call with `clearTimeout`. Each new `search()` call cancels the previous pending timer and sets a new one. Since all four calls happen within 100ms of each other (well under the 300ms delay), earlier timers are continuously cancelled. Only after 300ms of no new calls does the last timer fire, logging `"Searching: java"`. This is ideal for search-as-you-type to reduce API calls.Answer & Explanation
**Answer: B) `"string"`, `"object"`, `false false false false false true true`, `0`, `0`, `NaN`** **Explanation:** `typeof 42` → `"number"` (a string), then `typeof "number"` → `"string"`. `typeof null` → `"object"` (historical bug). Falsy values: `null`, `undefined`, `0`, `NaN`, `""` all double-negate to `false`. Truthy: `[]` and `{}` are **objects** (truthy regardless of emptiness). `+''` → `0`. `+null` → `0`. `+undefined` → `NaN`.Answer & Explanation
**Answer: B) `3, 3, 3`, then `0, 1, 2`** **Explanation:** `var i` is function-scoped — all three callbacks share the same `i`. By the time the callbacks run (0ms, 100ms, 200ms), the loop has completed and `i = 3`. `let j` creates a **new binding per iteration** — each callback captures its own `j` (0, 1, 2). This is the canonical JS interview question. To fix the `var` version: use `let`, use an IIFE, or use `.bind(null, i)`.Answer & Explanation
**Answer: C) `1, 2, 5, 4, 3`** **Explanation:** Synchronous: `"1"` → `asyncFunc()` runs synchronously until the first `await`: logs `"2"` → suspends at `await Promise.resolve()` → `Promise.resolve().then(...)` queues `"4"` as a microtask → logs `"5"`. Microtask queue: `await Promise.resolve()` inside `asyncFunc` resolves first (it was queued before the standalone `.then()`), but `"4"` was actually queued at the same tick as `asyncFunc`\'s continuation. Microtasks run in order: `await` continuation (logs `"3"`) runs after `"4"` — actually `"4"` resolves first because `asyncFunc`\'s inner `await` schedules a microtask, but the `.then(() => '4')` was enqueued after the `await`. Result: `"5"` → microtask `"4"` → microtask `"3"`.## # 32. Code Review & Standards
## Q. During a PR review, a Tech Lead spots the following pattern repeated across the codebase. What is the primary concern and the recommended fix? ```javascript // In multiple components: useEffect(() => { fetch("/api/data") .then(res => res.json()) .then(data => setData(data)); }, []); ``` - A) `fetch` should be replaced with `axios` — it has better defaults - B) The effect has no cleanup, causing a state update on an unmounted component (memory leak / React warning) - C) The empty dependency array means the effect never re-runs, which is always a bug - D) `res.json()` should be wrapped in a try-catch inside `.then()`
Answer & Explanation
**Answer: B) The effect has no cleanup, causing a state update on an unmounted component (memory leak / React warning)** **Explanation:** If the component unmounts before the `fetch` resolves, calling `setData` on an unmounted component triggers a React warning and potential memory leak. The fix is to use an `AbortController` or an `isMounted` flag in the cleanup function returned from `useEffect`. A Tech Lead should establish this pattern as a team-wide coding standard.Answer & Explanation
**Answer: B) The function hardcodes its dependencies, making it impossible to unit test in isolation (violates Dependency Inversion)** **Explanation:** `DatabaseConnection`, `FileLogger`, and `sendEmail` are all instantiated or called directly inside the function with no way to inject mocks. A Tech Lead should refactor this to accept dependencies via parameters or a DI container: `processOrder(orderId, { db, logger, mailer })`. This makes unit testing trivial by allowing stubs/mocks.Answer & Explanation
**Answer: B) `args.toString()` creates key collisions — e.g., `memoize(f)(1,2)` and `memoize(f)("1,2")` produce the same key** **Explanation:** `[1, 2].toString()` and `["1,2"].toString()` both produce the string `"1,2"`, so two different argument lists map to the same cache key. A robust fix is to use `JSON.stringify(args)` as the key, which distinguishes `[1,2]` (`"[1,2]"`) from `["1,2"]` (`'["1,2"]'`). A Tech Lead catching this in review prevents subtle correctness bugs in production.Answer & Explanation
**Answer: B) The error is swallowed — callers receive `null` without knowing why it failed** **Explanation:** Swallowing errors (catch → log → return null) hides failures from callers. Callers can\'t distinguish "ID has no data" from "network failed". Better: rethrow a domain error (`throw new DataFetchError(id, e)`), or return a Result type `{ data, error }`. At minimum, don\'t catch errors you can\'t handle — let them propagate for the caller to decide.Answer & Explanation
**Answer: B) Cryptic abbreviations harm readability and maintainability** **Explanation:** Code is read far more than it\'s written. Cryptic abbreviations: `d` → `currentDate`, `ts` → `timestamp`, `u` → `user`, `n` → `firstName`, `ln` → `lastName`, `proc` → `formatFullName`, `r` → `formattedUsers`. Minifiers handle abbreviation automatically. A Tech Lead should enforce naming standards via ESLint rules (e.g., `id-length`) and style guide.Answer & Explanation
**Answer: B) A JSDoc comment describing parameters, return value, thrown errors, and a usage example** **Explanation:** Public utility functions should have JSDoc: `@param {Function} fn`, `@param {Object} options`, `@param {number} [options.retries=3]`, `@returns {Promise<*>}`, `@throws {Error}`, `@example`. This: enables TypeScript type checking without converting to `.ts`; provides IDE hover docs; is the foundation for auto-generated API documentation; and communicates contract to users.Answer & Explanation
**Answer: B) Missing edge cases: `b = 0`, negative numbers, non-numbers, `NaN` inputs** **Explanation:** A Tech Lead should require: `divide(0, 0)` → `NaN`; `divide(10, 0)` → `Infinity`; `divide(-10, 2)` → `-5`; `divide('a', 2)` → `NaN`; `divide(null, 2)` → `0`. Code coverage (line/branch coverage) doesn\'t capture these — a function can be 100% line-covered with one test while missing critical edge cases. Semantic coverage matters more than line coverage.Answer & Explanation
**Answer: B) Establish a layered error handling strategy with typed errors, component-level catches, and a global handler** **Explanation:** A consistent strategy: 1) **API layer**: throw typed errors (`ApiError`, `NetworkError`) with context. 2) **Business logic**: catch expected errors, handle or rethrow. 3) **UI components**: display user-friendly messages based on error type. 4) **Global handler** (`window.onerror`, `process.on('unhandledRejection')`): catch and log missed errors. Enforced via ESLint (`no-floating-promises`), code review checklist, and shared error utilities.Answer & Explanation
**Answer: B) Creating a new arrow function on every render prevents effective memoization** **Explanation:** Each render creates fresh `() => deleteUser(user.id)` functions. If child components use `React.memo`, they'll always re-render because `onClick` prop changed (new reference). Fixes: use `useCallback` for stable references; or pass `user.id` as a prop and define the handler inside the child. This anti-pattern is especially impactful in large lists.Answer & Explanation
**Answer: B) TypeScript types, input validation, semantic versioning, and CHANGELOG** **Explanation:** Public API contract requirements: 1) **Types** (TypeScript/JSDoc) — compile-time safety for consumers. 2) **Input validation** — guard against unexpected inputs at the boundary. 3) **Semantic versioning** — patch (bug fix), minor (backward-compatible feature), major (breaking change). 4) **CHANGELOG** — what changed and migration path for breaking changes. This discipline prevents "breaking the world" silently in shared code.Answer & Explanation
**Answer: B) Define branching strategy, PR size limits, review requirements, and CI gates** **Explanation:** A Tech Lead defines team norms: **Trunk-based development** (short-lived branches, frequent integration) vs **GitFlow** (release branches, hotfixes). PR standards: max lines changed (~400), required reviewers, passing CI, linked issue. Automated gates: linting, tests, code coverage thresholds, security scans. Good branching strategy reduces merge conflicts, improves review quality, and ensures production stability.Answer & Explanation
**Answer: B) Assess impact, update, test, patch, and communicate** **Explanation:** Security vulnerability response: 1) **Assess**: check if the vulnerable API/code path is used in your app (use `npm audit`, Snyk, GitHub Dependabot). 2) **Update**: bump the dependency (patch or minor version usually). 3) **Test**: run regression suite — dependency updates can cause API changes. 4) **Deploy**: patch release to production. 5) **Communicate**: notify affected parties, update SECURITY.md. Never delay security patches based on convenience.## Q. A Tech Lead must decide on a team-wide async data-fetching standard. The team has mixed usage of callbacks, raw Promises, and `async/await`. Which standard maximizes readability and error-handling consistency? ```javascript // Option A — Callbacks getData(id, function(err, data) { if (err) handleError(err); else process(data); }); // Option B — Promise chains getData(id).then(process).catch(handleError); // Option C — async/await with try/catch try { const data = await getData(id); process(data); } catch (err) { handleError(err); } ``` - A) Option A — callbacks give the most control over execution order - B) Option B — chains are easier to read than try/catch blocks - C) Option C — `async/await` reads like synchronous code and provides structured error handling with `try/catch` - D) All three are equivalent; the choice has no impact on team consistency
Answer & Explanation
**Answer: C) Option C — `async/await` reads like synchronous code and provides structured error handling with `try/catch`** **Explanation:** `async/await` is the modern standard for async code in JavaScript teams. It avoids callback hell, is more readable than chained `.then()`, and integrates naturally with `try/catch` for error handling. A Tech Lead enforcing `async/await` as a team convention reduces cognitive overhead and makes code reviews more predictable.Answer & Explanation
**Answer: B) Use `Promise.all` to run all five independent requests concurrently** **Explanation:** The current code awaits each call sequentially — if each takes 200ms, the total is ~1000ms. Since the calls are independent, `Promise.all([fetchProfile, fetchOrders, fetchMessages, fetchSettings, fetchStats])` runs them concurrently, reducing total time to ~200ms (the slowest individual call). This is one of the most impactful async patterns a Tech Lead should enforce for data-loading functions.Answer & Explanation
**Answer: B) Exponential backoff reconnection — increasing delays up to 30s** **Explanation:** WebSockets don\'t auto-reconnect. Exponential backoff: 1s → 2s → 4s → 8s → ... → 30s (capped). This prevents overwhelming a recovering server with reconnection storms. The `setTimeout` callback holds a reference to the class instance via closure — no stack overflow since it\'s not recursive via the call stack, it\'s via the event loop. In production, also reset delay on successful reconnection.Answer & Explanation
**Answer: B) Multiple concurrent requests share one in-flight request — prevents redundant network calls** **Explanation:** Without deduplication, if 5 components mount simultaneously and all call `fetchUser(123)`, you get 5 network requests. With deduplication: the first request starts and stores the Promise; subsequent requests return the same Promise. All 5 consumers await the same request. `finally` removes from map when done so future requests get fresh data. This is what SWR and React Query do internally.Answer & Explanation
**Answer: B) A maximum of 3 concurrent async tasks, preventing resource exhaustion** **Explanation:** Without concurrency control, flooding a server with 100 simultaneous requests could cause rate limiting or resource exhaustion. The queue runs up to `concurrency` (3) tasks simultaneously. When one completes, the next queued task starts (`#runNext`). This pattern is essential for: bulk API operations, file processing, database migrations, and any scenario where parallelism must be bounded.Answer & Explanation
**Answer: B) Async test utilities: async/await in tests, mock timers, waitFor, and test isolation** **Explanation:** Async testing best practices: 1) Return Promises or use `async/await` in test functions. 2) `jest.useFakeTimers()` for testing `setTimeout`/`setInterval` without actual delays. 3) `waitFor(() => expect(...))` (Testing Library) for waiting on async UI changes. 4) Mock all external async dependencies. 5) Ensure cleanup in `afterEach` to prevent test pollution. Real timers in tests cause flaky, slow test suites.Answer & Explanation
**Answer: B) After 5 failures, the circuit opens — blocking calls and allowing the service to recover** **Explanation:** Circuit Breaker states: **Closed** (normal) → **Open** (blocking calls after N failures) → **Half-open** (allow test requests after timeout). It prevents cascade failures: if a downstream service is down, fail fast instead of piling up timeouts. After the reset timeout, the circuit closes again for retry. This is critical for resilient microservice architectures.Answer & Explanation
**Answer: B) Long-polling for simple infrequent updates; SSE for server streams; WebSockets for bidirectional real-time** **Explanation:** Choose based on needs: **Long-polling** (client repeatedly polls) — simple, works everywhere, good for infrequent updates (email checks). **SSE** (`EventSource`) — efficient server-to-client streaming, HTTP/2 multiplexable, auto-reconnects, limited to text, unidirectional. **WebSockets** — bidirectional, low-latency, binary support, more complex (custom reconnect, protocols). Use the simplest tool that meets the requirements.Answer & Explanation
**Answer: B) A saga coordinates async steps with compensating rollbacks for partial failures** **Explanation:** A saga manages long-running distributed transactions. Example: Book flight → Book hotel → Charge card. If charging fails, **compensating transactions** undo prior steps (cancel flight, cancel hotel). In Redux-Saga, generators control async side effects with `take`, `put`, `call`. The pattern prevents partial state by providing explicit rollback logic — critical for e-commerce, booking systems, and financial applications.Answer & Explanation
**Answer: B) Use `storage` event, `BroadcastChannel`, or `SharedWorker` for cross-tab sync** **Explanation:** Cross-tab sync options: 1) **`storage` event** — fires in other tabs when `localStorage` changes (simplest, limited to strings). 2) **`BroadcastChannel`** — structured message passing between same-origin contexts, supports objects. 3) **`SharedWorker`** — shared thread across tabs, can maintain centralized state. Use case: logout propagation (security critical), shopping cart sync, collaborative editing state. `BroadcastChannel` is the modern recommended approach.Answer & Explanation
**Answer: B) On failure: rollback, show error, log failure, offer retry** **Explanation:** Optimistic update failure handling: 1) **Rollback** — restore exact previous state (use snapshot before update). 2) **User notification** — toast/banner with clear message and retry option. 3) **Logging** — capture error details for debugging (Sentry, Datadog). 4) **Retry** — allow user to retry with idempotency key to prevent duplicate actions. Silently refreshing on failure destroys user input and is poor UX.## Q. A Tech Lead is reviewing a `utils/index.js` barrel file that re-exports all utilities. A team member reports that the bundle size has grown significantly. What is the likely cause? ```javascript // utils/index.js export { formatDate } from "./date"; export { sortArray } from "./sort"; export { debounce } from "./debounce"; export { deepClone } from "./deepClone"; export { parseCSV } from "./csv"; // includes Papa Parse (500KB) export { renderChart } from "./chart"; // includes D3 (200KB) ``` - A) Barrel files are always larger than direct imports — avoid them entirely - B) Importing any single utility from `utils/index.js` causes the bundler to include all re-exported modules if tree-shaking fails (e.g., with CommonJS or side-effect-heavy modules) - C) The issue is that `export { }` syntax is not tree-shakeable — use `export default` instead - D) `parseCSV` and `renderChart` should be renamed to prevent bundler confusion
Answer & Explanation
**Answer: B) Importing any single utility from `utils/index.js` causes the bundler to include all re-exported modules if tree-shaking fails (e.g., with CommonJS or side-effect-heavy modules)** **Explanation:** Barrel files can defeat tree-shaking when modules have side effects or use CommonJS format. The bundler may include the entire barrel. A Tech Lead should audit barrel files, mark pure modules with `"sideEffects": false` in `package.json`, ensure all modules use ES Module syntax, or split large dependencies into separate lazy-loaded entry points.Answer & Explanation
**Answer: B) This is a circular dependency; depending on evaluation order, `a` or `b` may be `undefined` at the time of the first call, causing a `TypeError`** **Explanation:** ES Modules handle circular references through "live bindings," but if `a.js` is evaluated first, `b` will be `undefined` when `a` is defined. The fix is to break the cycle by extracting shared logic into a third module, or restructuring dependencies. Tech Leads should configure tools like `eslint-plugin-import` with `no-cycle` to catch this in CI.Answer & Explanation
**Answer: B) Single Responsibility Principle — the class has too many reasons to change** **Explanation:** SRP: a module should have one reason to change. `UserService` changes if: DB schema changes; email provider changes; logging format changes; analytics system changes; payment processor changes. Fix: split into `UserRepository`, `EmailService`, `ActivityLogger`, `AnalyticsService`, `PaymentService`. Each module has a single, clear responsibility and can be tested, replaced, and evolved independently.Answer & Explanation
**Answer: B) Create an internal npm package with semantic versioning and typed exports** **Explanation:** Shared utilities across teams require: 1) **Versioned package** (npm/monorepo) — each team pins a version; breaking changes require a major version bump. 2) **TypeScript types** — consumer type safety. 3) **Documented API** — changelog, migration guides. 4) **Tree-shakeable** (ESM). Global variables (`window.utils`) create implicit coupling, version conflicts, and runtime errors. The internal package approach scales to N teams without coordination overhead.Answer & Explanation
**Answer: B) Centralized feature flags module — components import without knowing the source** **Explanation:** Feature flag architecture: 1) Single source of truth (one fetch, one module). 2) Components import flags declaratively — no direct fetch calls scattered everywhere. 3) Easy to mock in tests (`jest.mock('./feature-flags', () => ({ newCheckout: true }))`). 4) Flags have clear naming and defaults. 5) Dead code elimination — when a flag is permanently enabled, remove the legacy branch. This scales from simple booleans to complex targeting rules.Answer & Explanation
**Answer: B) DDD organizes code by business domain — each domain is self-contained** **Explanation:** Layer-first organization (`/controllers`, `/services`, `/models`) requires changes to span multiple directories. Domain-first (`/users`, `/orders`, `/payments`) keeps related code co-located. Each domain exports a public API (facade pattern), hides internals, and can be owned by a team. This maps to micro-frontend boundaries and enables independent deployment. Tech Leads use DDD to reduce coupling and improve team autonomy.Answer & Explanation
**Answer: B) Exposing internals creates coupling; a clean public API hides implementation details** **Explanation:** Leaking internals means consumers couple to `UserModel`, `hashPassword`, etc. If you switch from bcrypt to argon2, all consumers break. Clean API: export only what consumers need (use cases, not mechanisms). For testing internals, use testing-specific exports or test through the public API. Follow the principle of least privilege — expose the minimum needed.Answer & Explanation
**Answer: B) DI injects dependencies from outside, enabling mocking in tests and swapping implementations** **Explanation:** Without DI, `OrderService` is tightly coupled to `PostgresDatabase` and `SendGridEmailer`. Tests would need real DB/email connections. With DI: pass mock objects in tests; swap `PostgresDatabase` for `MongoDB` without changing `OrderService`; the class depends on interfaces (duck typing), not implementations. This is the "D" in SOLID (Dependency Inversion Principle).Answer & Explanation
**Answer: B) Organize by domain, ensure purity, and remove duplicates of native APIs** **Explanation:** "Utils explosion" — a giant `utils.js` with everything — causes: circular dependencies, poor discoverability, untested code, and duplicating built-in APIs (custom `isEmpty` when `arr.length === 0` suffices). Best practice: domain-organized utils; pure functions (easy to test); named exports (tree-shakeable); regular audit to remove functions now available natively (e.g., custom `flatMap` pre-ES2019). Prefer standard library over custom utilities.Answer & Explanation
**Answer: B) Add new API → mark old deprecated → keep both for transition period → remove in major version** **Explanation:** Breaking change management: 1) **Add** new API, **keep** old API. 2) **`@deprecated`** JSDoc with link to replacement. 3) **Minor version** bump. 4) **Communication**: changelog, team announcement, migration guide. 5) After transition period (1-2 major versions), **remove** in major bump. This approach allows teams to migrate at their own pace and avoids "big bang" coordinations. Use `eslint-plugin-deprecation` to surface deprecated usages in CI.## Q. A Tech Lead designs a centralized error handling strategy for a Node.js Express API. Which approach is the most robust for production? ```javascript // Option A — inline try/catch in every route app.get("/users/:id", async (req, res) => { try { const user = await UserService.getById(req.params.id); res.json(user); } catch (err) { res.status(500).json({ error: err.message }); } }); // Option B — centralized error middleware + async wrapper const asyncHandler = fn => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); app.get("/users/:id", asyncHandler(async (req, res) => { const user = await UserService.getById(req.params.id); res.json(user); })); app.use((err, req, res, next) => { logger.error(err); res.status(err.statusCode || 500).json({ error: err.message }); }); ``` - A) Option A — inline try/catch is more explicit and easier for junior developers to understand - B) Option B — centralized error middleware ensures consistent error responses, logging, and status codes across all routes without duplicating error-handling logic - C) Both are equivalent; the choice is purely stylistic - D) Option B is dangerous because unhandled errors in `asyncHandler` will crash the server
Answer & Explanation
**Answer: B) Option B — centralized error middleware ensures consistent error responses, logging, and status codes across all routes without duplicating error-handling logic** **Explanation:** Option A scatters error handling across every route, leading to inconsistent formats, missing logging, and high maintenance overhead. A Tech Lead should implement a central error middleware that all routes funnel into via `next(err)`, combined with a typed error hierarchy (`AppError`, `ValidationError`, etc.) to produce structured, consistent API error responses with proper HTTP status codes.Answer & Explanation
**Answer: B) A typed hierarchy enables structured handling: catch by type, map to status codes, distinguish operational errors** **Explanation:** `isOperational: true` marks expected errors (validation, not found) that should be reported gracefully vs programming bugs (`TypeError`, `ReferenceError`) that should crash the process (or at least alert). The hierarchy enables: `if (error instanceof ValidationError) return res.status(400)`; central error handler that maps error types to responses; consistent error codes for frontend handling; and filtering operational errors from monitoring alerts.Answer & Explanation
**Answer: B) Correlation IDs that propagate through all services, included in all logs and error responses** **Explanation:** Distributed request tracing: 1) Generate a unique `correlationId` (UUID) at the API gateway on each request. 2) Pass it via HTTP header (`X-Correlation-ID`) to all downstream services. 3) Include it in every log entry. 4) Include it in error responses for user support tickets. 5) Use tools like OpenTelemetry + Jaeger/Zipkin for visual traces. When a user reports "error ID: abc123", you can find every log entry across all services in milliseconds.Answer & Explanation
**Answer: B) Granular error boundaries isolate failures — crashed widgets don\'t take down the whole app** **Explanation:** A single global boundary means any component crash shows the same full-page error. Granular boundaries at the dashboard widget or section level enable graceful degradation: the news feed crashes → shows a "Feed unavailable" message; header and sidebar still work. Tech Leads should define a standard error boundary component and establish guidelines for where to place boundaries (route level, widget level, critical sections).Answer & Explanation
**Answer: B) Log always; crash for programming errors; Node 15+ crashes by default** **Explanation:** Node.js 15+ changed the default: unhandled rejections crash the process. Best practice: 1) Catch all rejections at the call site. 2) `unhandledRejection` handler as a last resort: log the error with full context. 3) If `error.isOperational` (expected error somehow missed) → log and continue. 4) If unknown error type → it\'s a bug, crash the process, let the process manager (PM2, systemd, Kubernetes) restart it cleanly.Answer & Explanation
**Answer: B) Enriched context, alert rules, error grouping, and deployment integration** **Explanation:** Effective error monitoring: 1) **Enrich errors** — attach `user.id`, `session.id`, `release` version (commit SHA), URL, browser/OS. 2) **Alert rules** — alert on error rate > 5%, new errors, regression in known errors. 3) **Release tracking** — correlate error spikes with deployments. 4) **Before/after release** — compare error rates. 5) **Ignore expected errors** — filter out noise (network errors from user\'s ISP, bot traffic). This enables rapid identification of regressions.Answer & Explanation
**Answer: B) Return an error map instead of throwing — enables field-level error display and partial validation** **Explanation:** Form validation is not "exceptional" — invalid user input is an expected case, not an error. Throwing exceptions for validation creates awkward try/catch in form handlers. Returning an error map: enables per-field inline errors (red border + message under each field); supports submit-button enable/disable based on `isValid`; allows progressive validation on blur. Libraries like Yup, Zod, and `react-hook-form` use this pattern.Answer & Explanation
**Answer: B) Store errors in state — track loading/data/error as separate slices; UI derives display from state** **Explanation:** State machine approach: `{ loading: true, data: null, error: null }` → `{ loading: false, data: {...}, error: null }` → `{ loading: false, data: null, error: "Network error" }`. These states are mutually exclusive. Components derive behavior from state: show spinner while loading, show error message on error, show data on success. This is the standard Redux Toolkit pattern and scales to any async state management.Answer & Explanation
**Answer: B) Result type makes errors explicit — callers must handle both success and failure** **Explanation:** With exceptions, callers can forget to `try/catch`. The Result type forces callers to check `result.ok` before using `result.value`. This pattern (from Rust\'s `Result<T, E>`, Haskell\'s `Either`) makes the error possibility part of the API contract. TypeScript can type this as `{ ok: true; value: T } | { ok: false; error: string }` with full type narrowing. Useful for predictable failure cases (validation, parsing, business rules).## Q. A Tech Lead reviews a React component and identifies a performance problem. What is wrong? ```javascript function UserList({ users }) { const sortedUsers = users.sort((a, b) => a.name.localeCompare(b.name)); return (
-
{sortedUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
Answer & Explanation
**Answer: B) `.sort()` mutates the original `users` prop array and the sort runs on every render — use `useMemo` and `.slice().sort()`** **Explanation:** `Array.prototype.sort` mutates in place, which violates React\'s immutability principle and can cause subtle bugs upstream. Additionally, the sort re-runs on every render regardless of whether `users` changed. The fix: `const sortedUsers = useMemo(() => [...users].sort((a, b) => a.name.localeCompare(b.name)), [users])`. A Tech Lead should add this as a lint rule or code review checklist item.Answer & Explanation
**Answer: B) Move the blocking I/O and CPU computation to a Worker Thread to keep the event loop free** **Explanation:** `fs.readFileSync` and a heavy synchronous computation both block Node.js\'s single-threaded event loop, freezing all other requests during execution. The correct fix is: (1) replace `readFileSync` with `fs.promises.readFile` and (2) offload `heavyComputation` to a `Worker` from the `worker_threads` module. This keeps the event loop responsive while heavy work runs in a separate thread.Answer & Explanation
**Answer: B) Core Web Vitals measure LCP (load), INP (interactivity), and CLS (visual stability)** **Explanation:** Google\'s Core Web Vitals: **LCP** (Largest Contentful Paint, should be < 2.5s) — how fast the main content loads. **INP** (Interaction to Next Paint, < 200ms) — how responsive the page is to interactions. **CLS** (Cumulative Layout Shift, < 0.1) — how much content unexpectedly jumps. Failing these affects SEO rankings. A Tech Lead should monitor CWV in production with Real User Monitoring (RUM) and create tickets for regressions.Answer & Explanation
**Answer: B) Third-party scripts can block rendering and consume main thread time — load async or defer** **Explanation:** Third-party scripts often represent the majority of a page\'s JS execution time. Each should be evaluated: is it critical (chat support) or nice-to-have (heat mapping)? Load critical ones with `async`/`defer`; defer nice-to-have ones until after the page loads (`setTimeout(() => loadScript(), 3000)` or `requestIdleCallback`). Use `resource-timing` API to measure their load times. Remove scripts that provide < ROI relative to their performance cost.Answer & Explanation
**Answer: B) Bundle size, render performance, unnecessary re-renders, lazy loading, images, and CWV in production** **Explanation:** Comprehensive performance review: 1) **Bundle analysis** (webpack-bundle-analyzer) — identify large dependencies. 2) **React Profiler** — find slow renders, flamegraphs. 3) **Re-render audit** — use `why-did-you-render` to find unnecessary renders. 4) **Code splitting** — are routes lazily loaded? 5) **Images** — WebP format, `loading="lazy"`, correct dimensions. 6) **CWV in production** — RUM data from real users is more valuable than Lighthouse scores.Answer & Explanation
**Answer: B) EXPLAIN, targeted indexes, Redis caching, pagination, and avoiding N+1 queries** **Explanation:** N+1 problem: fetching 100 users then making 100 individual "fetch user\'s orders" queries = 101 DB calls. Fix: JOIN at DB level or DataLoader batching. Performance tools: `EXPLAIN ANALYZE` shows query execution plan; indexes on `WHERE`, `ORDER BY`, `JOIN` columns; Redis cache for frequently read, rarely changed data; pagination prevents unbounded result sets. A Tech Lead should set up slow query logging (> 100ms threshold) in production.Answer & Explanation
**Answer: B) Replace heavy libraries with lightweight alternatives, lazy load, and set CI bundle size budgets** **Explanation:** Systematic approach: 1) **moment → date-fns/dayjs** (2-9KB vs 232KB). 2) **lodash → lodash-es** with tree shaking. 3) **Chart.js** — dynamic `import('./chart.js')` only on chart pages. 4) Set `performance.maxAssetSize` in webpack config — fail CI if bundle exceeds budget. 5) Track bundle size in PRs (`bundlesize` CLI or size-limit). Preventing regression is more important than one-time optimization.Answer & Explanation
**Answer: B) A large context causes all consumers to re-render when any part of the value changes** **Explanation:** When `AppContext.value` changes (e.g., cart updates), every component consuming `AppContext` re-renders, even if it only reads `theme`. Solution: split context by **update frequency** (user context changes rarely; cart changes on every add/remove). Also consider `useMemo` for context values to stabilize object references. For complex state, Zustand or Redux Toolkit avoid context re-render issues entirely.Answer & Explanation
**Answer: B) TTFB includes server processing time — improve via CDN, SSR caching, and database optimization** **Explanation:** TTFB measures time from request to first byte received. Components: DNS lookup + TCP connection + TLS handshake + server processing time. Improvements: 1) **CDN** for static assets and edge-cached responses. 2) **SSR caching** — cache rendered HTML at CDN edge (Next.js ISR). 3) **Database optimization** — slow queries inflate TTFB. 4) **Connection pooling** — reuse DB connections. 5) **HTTP/2** or **HTTP/3** for multiplexing. Target TTFB < 600ms for a "Good" rating.Answer & Explanation
**Answer: B) `localStorage.setItem` is synchronous and blocking — 60fps calls cause scroll jank** **Explanation:** `localStorage` operations are **synchronous** — they block the main thread while reading/writing from disk. Calling them in high-frequency events (scroll, mousemove, input) causes jank. Fix: debounce writes (`debounce(fn, 500)` in scroll handler); use `sessionStorage` with `requestIdleCallback` for persistence; or use an in-memory variable that syncs to storage at lower frequency. For high-frequency state, keep data in memory and persist periodically.Answer & Explanation
**Answer: B) Yielding allows the browser to handle interactions between iterations — preventing jank from Long Tasks** **Explanation:** A "Long Task" (> 50ms) blocks the main thread from handling clicks, renders, and other events. For loops processing 1000+ items, yield every N items to give the browser "air" to process the interaction queue. `setTimeout(resolve, 0)` schedules in the macrotask queue after pending microtasks and a render frame. `scheduler.yield()` (Chrome 115+) is a higher-priority yield for user inputs. This makes UIs responsive even during heavy computation.## # 37. Micro-Frontend Architecture
## Q. A Technical Architect is designing a micro-frontend system where five teams independently deploy their applications. Which approach best achieves runtime integration with independent deployments and shared dependencies? - A) Build-time integration via an npm monorepo — all apps are compiled together into one bundle - B) Module Federation (Webpack 5) — each micro-frontend exposes and consumes modules at runtime without sharing build artifacts - C) iFrame isolation — each app runs in a separate iframe with `postMessage` for communication - D) Web Components with Shadow DOM — each team builds custom elements and registers them in the main shell