Introduction
Welcome to the ultimate JavaScript interview handbook. Whether you are a college student preparing for internships, a React developer upgrading your core engineering skills, or a senior developer brushing up on internal machinery, this guide has been designed to take you from foundational basics to advanced runtime mechanics.
Instead of dry definitions, every question here includes direct, interview-ready answers, real-world analogies, code, and conceptual "follow-up" questions that tech interviewers actually use to test your depth.
Table of Contents
- Difficulty Legend
- Beginner Essentials (Questions 1-15)
- Intermediate Mastery (Questions 16-35)
- Advanced & Runtime Mechanics (Questions 36-50)
- Final Revision Checklist
- Senior Interview Strategy Tips
Difficulty Legend
What is the difference between Primitive and Reference types?
Short Interview Answer:
Primitive types store value directly on the Stack (stack memory) and are immutable. Reference types store an address/pointer on the Stack pointing to a mutable object located in the Heap memory.
Detailed Explanation:
JavaScript memory is divided into two primary storage environments: the Stack and the Heap. Primitives like numbers, strings, and booleans are small and fixed in size. Hence, JavaScript stores them on the high-speed Stack directly. When you assign or copy a primitive, JavaScript creates a completely independent copy of that value.
Reference types like Objects, Arrays, and Functions can dynamically grow. Placing them on the Stack could choke the execution speed. Therefore, the actual contents are created in the flexible Heap, while a small, fixed-size memory reference (the address of the object in the Heap) is written onto the Stack.
Real Life Analogy:
Primitive: A physical printed book. If you give a copy of the book to your friend, they can highlight pages in theirs, but your copy remains completely untouched.
Reference: A shared Google Doc link. If you give the link to a friend, you both are viewing and modifying the exact same document online.
Code Example:
// Primitive behavior
let originalName = "John";
let copiedName = originalName;
copiedName = "Doe";
console.log(originalName); // "John" (unaffected)
// Reference behavior
let originalUser = { name: "Alice" };
let copiedUser = originalUser;
copiedUser.name = "Bob";
console.log(originalUser.name); // "Bob" (mutated via reference)
Output:
"John"
"Bob"
Common Mistakes:
- Assuming
const obj = {}prevents object properties from being changed.constonly locks the Stack reference, not the Heap contents.
Interview Follow-up Questions:
Interviewer: How can we check if a value is primitive or reference programmatically?
Candidate: We can use the typeof operator. If it returns "object" (excluding null) or "function", it is a reference type. Otherwise, it is primitive.
Quiz:
What is the output of console.log(typeof null) and why?
a) "null"
b) "undefined"
c) "object"
Answer: c) "object". This is a well-known historical bug in the original implementation of JavaScript where values were stored in 32-bit units, and the type tag for objects matched null's bitwise representation.
Key Takeaways:
- Primitives are copied by value.
- Reference types are copied by pointer reference.
- Modifying reference properties affects all variables pointing to that object.
What is Hoisting?
Short Interview Answer:
Hoisting is the mechanism in JavaScript where variable and function declarations are moved to the top of their containing scope during the compilation phase, before execution begins.
Detailed Explanation:
Before executing any line of code, the JavaScript engine parses the entire script to register variables and functions. During this phase, memory is allocated. For variables declared with var, they are initialized to undefined. For functions declared with the function keyword, the entire body is saved in memory. This gives the illusion that declarations "float" to the top of the file.
Real Life Analogy:
A theater host checking the guest list before opening the doors. Even if your name is at the bottom of the printed registration sheet, they acknowledge you are on the list as soon as they scan the paper.
Code Example:
console.log(greet()); // "Hello!" works because functions are fully hoisted
console.log(score); // undefined because only declaration is hoisted, not value
function greet() {
return "Hello!";
}
var score = 100;
Output:
"Hello!"
undefined
Common Mistakes:
- Believing arrow functions and function expressions hoist like standard function declarations. (They do not; they follow variable hoisting rules).
Interview Follow-up Questions:
Interviewer: Are variables declared with let and const hoisted?
Candidate: Yes, they are hoisted, but they are not initialized. They remain in the "Temporal Dead Zone" (TDZ) until the execution reaches the line where they are declared. Accessing them before that point throws a ReferenceError.
Quiz:
What is the output of the following block?
var x = 1;
function test() {
console.log(x);
var x = 2;
}
test();
a) 1
b) undefined
c) ReferenceError
Answer: b) undefined. Because of local hosting, the local declaration var x inside test() is hoisted to the top of the function scope, shadowing the outer variable, but is initially initialized to undefined.
Key Takeaways:
- Function declarations hoist with their definition.
varhoists withundefined.letandconsthoist into the Temporal Dead Zone.
What is the Temporal Dead Zone (TDZ)?
Short Interview Answer:
The Temporal Dead Zone is the period/state between block entry and the line of declaration where variables declared withletandconstcannot be accessed.
Detailed Explanation:
When JavaScript enters a block scope, variables defined with let and const are allocated in memory. However, they are protected with a status flags preventing read/write access. This zone of protection starts as soon as the block executes and lasts until the line where the variable declaration actually takes place is reached.
Real Life Analogy:
Reserving a table at a restaurant. The table is allocated for you, but you are not allowed to sit or eat at it until the reservation window officially starts.
Code Example:
{
// TDZ for variable 'name' begins here
// console.log(name); // Throws ReferenceError
let name = "Developer"; // TDZ ends here
console.log(name); // Safe to access!
}
Output:
"Developer"
Common Mistakes:
- Thinking "Temporal" means spatial context. It is based on time of execution, not location in physical code. Calling a function below the declaration that references the TDZ variable before declaration executes will still break.
Interview Follow-up Questions:
Interviewer: Does typeof safe-guard against the TDZ?
Candidate: No, typeof will throw a ReferenceError if called on a variable in the TDZ, whereas it simply returns "undefined" for variables that were never declared.
Quiz:
Which of the following will throw a ReferenceError?
a) let x; console.log(x);
b) console.log(y); let y = 10;
c) Both
Answer: b. Option a simply logs undefined because the variable has been initialized at execution time.
Key Takeaways:
- TDZ protects codebase predictability by preventing access before initialization.
- Only applies to
let,const, andclass.
What is the difference between Scope and Lexical Environment?
Short Interview Answer:
Scope is the actual physical boundary where a variable is accessible. Lexical Environment is the internal structure containing local variables and a reference to its parent Lexical Environment.
Detailed Explanation:
Scope defines the visibility limits of identifiers. Lexical Environment is the mechanical concept JavaScript uses to achieve Scope. When a function executes, an execution context is pushed to the call stack. This context loads its own Lexical Environment which contains: 1. Environment Record (the actual variables stored here) 2. Outer Lexical Environment reference (where to search if a variable isn't found locally).
Real Life Analogy:
Scope is like a series of nesting Russian Dolls. Lexical environment is the physical physical plastic construct of each doll, allowing it to "fit" and "see" the boundaries of the doll enclosing it.
Code Example:
let globalValue = "Global";
function outer() {
let outerValue = "Outer";
function inner() {
// Looks locally -> Outer -> Global
console.log(outerValue, globalValue);
}
inner();
}
outer();
Output:
"Outer" "Global"
Interview Follow-up Questions:
Interviewer: When is the Lexical Environment determined?
Candidate: It is determined at compile-time (lexically), which means it depends on where the function was written in the source code, not where it is called.
Quiz:
What lexical scope system does JS use?
a) Dynamic Scope
b) Lexical (Static) Scope
c) Global scope only
Answer: b) Lexical Scope.
Key Takeaways:
- Scope limits visibility.
- Lexical Environment tracks values dynamically at execution time using parent-pointing links.
What is the difference between == and ===?
Short Interview Answer:
==(Loose Equality) checks for value equality and performs implicit type coercion.===(Strict Equality) checks for both value and type without coercion.
Detailed Explanation:
When comparing different types with loose equality (==), JS tries to reconcile their types first (coercion). For example, it converts strings and booleans to numbers prior to comparing. Strict equality (===) immediately returns false if the types differ.
Real Life Analogy:
Loose equality is like checking security based on name spelling match ignoring casing. Strict equality matches both name spelling and checks their passport type.
Code Example:
console.log(5 == "5"); // true (string "5" is coerced to number 5)
console.log(5 === "5"); // false (types do not match)
Output:
true
false
Interview Follow-up Questions:
Interviewer: What is the result of null == undefined and null === undefined?
Candidate: null == undefined is true (by specification exception), while null === undefined is false.
Quiz:
What is [] == false in JS?
a) true
b) false
Answer: a) true. Because empty array is coerced to an empty string, which is coerced to 0, matching false (coerced to 0).
Key Takeaways:
- Use
===to write clean, predictable code. - Coercion rules can be non-intuitive.
What is the difference between Null and Undefined?
Short Interview Answer:
undefinedmeans a variable has been declared but not yet assigned a value.nullis an intentional assignment representing "no value" or an empty object reference.
Detailed Explanation:
JavaScript uses undefined as a default value placeholder. It is assigned automatically. null, on the other hand, is manually assigned by a developer to represent that the variable is intended to be completely empty at the moment.
Real Life Analogy:
An empty coffee cup: undefined means we haven't even decided if we are getting coffee or what goes into the cup. null means the cup was served deliberately empty.
Code Example:
let a;
let b = null;
console.log(a); // undefined
console.log(b); // null
Output:
undefined
null
Key Takeaways:
typeof undefinedis "undefined".typeof nullis "object".
What are Truthy and Falsy values?
Short Interview Answer:
A falsy value is a value that translates to false when evaluated in a boolean context. All other values are truthy.
Detailed Explanation:
There are exactly 8 falsy values in modern JS: false, 0, -0, 0n (BigInt zero), "" (empty string), null, undefined, and NaN. Absolutely everything else is truthy, including empty arrays [] and empty objects {}.
Code Example:
if ([]) {
console.log("Empty array is Truthy!");
}
if ("") {
// won't execute
} else {
console.log("Empty string is Falsy!");
}
Output:
"Empty array is Truthy!"
"Empty string is Falsy!"
Key Takeaways:
- Only 8 distinct values are falsy.
- Empty objects and arrays are truthy.
Explain pure vs impure functions.
Short Interview Answer:
A pure function always returns the exact same output for the same input and produces no side effects. An impure function can yield different outputs or modify external state.
Code Example:
// Pure
const add = (a, b) => a + b;
// Impure
let count = 0;
const increment = () => { count++; return count; };
Key Takeaways:
- Pure functions are predictable and easy to test.
- Essential for functional programming paradigms.
What is the difference between Shallow Copy and Deep Copy?
Short Interview Answer:
A shallow copy duplicates the top-level structure, keeping reference pointers to nested objects. A deep copy recursively clones every nested value and reference, decoupling them entirely.
Code Example:
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.b.c = 99; // Alters original.b.c as well!
const deep = structuredClone(original);
deep.b.c = 1000; // Original remains unchanged
Key Takeaways:
- Use
structuredClone()for modern deep copying. - Spread operator yields shallow copies.
Explain Object.freeze() vs const.
Short Interview Answer:
constprevents reassignment of a variable reference.Object.freeze()makes the properties of an object read-only and prevents adding or removing properties.
Code Example:
const user = { name: "John" };
user.name = "Alice"; // Works!
const frozenUser = Object.freeze({ name: "John" });
frozenUser.name = "Alice"; // Fails silently or throws error in strict mode
Key Takeaways:
constprotects references.Object.freezeis shallow; nested objects are not frozen automatically.
What are JavaScript Template Literals?
Short Interview Answer:
Template literals are string literals delimited with backticks (`), allowing multi-line strings, string interpolation, and tagged templates.
Code Example:
const name = "Alice";
console.log(`Hello, ${name}!`);
Key Takeaways:
- Avoid manual string concatenation.
- Supports embedded expressions inside
${}.
Explain Arrow Functions vs Regular Functions.
Short Interview Answer:
Arrow functions do not have their ownthiscontext (they inherit it lexically), do not have anargumentsobject, and cannot be used as constructors.
Code Example:
const obj = {
name: "Bob",
regular: function() { console.log(this.name); },
arrow: () => { console.log(this.name); }
};
obj.regular(); // "Bob"
obj.arrow(); // undefined (or global/window object)
Key Takeaways:
- Use arrow functions for clean syntax and preserving
thisbindings in callbacks. - Do not use arrow functions for object methods.
What is Rest and Spread Operator?
Short Interview Answer:
The spread operator (...) unpacks items from an array/object. The rest operator collects multiple separate arguments or items into a single array structure.
Code Example:
// Spread
const arr = [1, 2];
const copy = [...arr, 3]; // [1, 2, 3]
// Rest
function sum(...nums) {
return nums.reduce((acc, curr) => acc + curr, 0);
}
Key Takeaways:
- Spread "unpacks" structures.
- Rest "gathers" values into arrays.
What is Destructuring?
Short Interview Answer:
Destructuring is a clean syntax that allows unpacking properties from objects or values from arrays directly into individual variables.
Code Example:
const user = { id: 1, label: "Admin" };
const { label } = user;
console.log(label); // "Admin"
Key Takeaways:
- Makes configuration parsing and props extraction concise.
What is the Map data structure vs standard Objects?
Short Interview Answer:
Map allows keys of any data type (including objects/functions) and maintains insertion order, whereas Object keys must be Strings or Symbols.
Code Example:
const myMap = new Map();
const keyObj = {};
myMap.set(keyObj, "Object Key Value");
console.log(myMap.get(keyObj));
Key Takeaways:
- Use Map for frequent add/remove operations.
- Preserves exact key insertion order.
Intermediate Mastery (Questions 16-35)
What is a Closure?
Short Interview Answer:
A closure is the combination of a function bundled together with references to its surrounding state (the lexical environment). It lets an inner function access the outer scope even after the outer function has finished executing.
Detailed Explanation:
In JavaScript, every execution context has a reference to its lexical scope chain. When a function is declared, it "remembers" the scope environment in which it was born. If that function is returned or passed out of its parent wrapper, it carries this memory payload with it. Garbage collection cannot claim the parent variables because the active inner function maintains references to them.
Real Life Analogy:
A safe deposit box. The banker leaves (outer function completes), but your key (returned inner function) keeps access to the inner items because you still have the private relationship to that locker.
Code Example:
function createCounter() {
let count = 0; // Private state
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Output:
1
2
Interview Follow-up Questions:
Interviewer: What are closures used for in production?
Candidate: Data encapsulation/privacy, currying, function factories, memoization, and maintaining state in asynchronous environments like event listeners.
Quiz:
How many quizzes on closure? Let's trace this closure:
function outer() {
let val = 10;
return () => console.log(++val);
}
const fn = outer();
fn();
fn();
a) 11 then 11
b) 11 then 12
c) 10 then 11
Answer: b) 11 then 12. The state is maintained across separate calls of the same returned inner function.
Key Takeaways:
- Enables robust encapsulation.
- Variables stay alive in memory.
What is the Execution Context and Call Stack?
Short Interview Answer:
Execution Context is the environment in which code is evaluated and executed. The Call Stack is a LIFO (Last In, First Out) data structure used to manage execution contexts.
Detailed Explanation:
Whenever you start a JS application, the engine creates the Global Execution Context (GEC). When a function is called, a new Functional Execution Context (FEC) is created and pushed onto the Call Stack. Once completed, it is popped off.
Code Example:
function first() {
second();
}
function second() {
console.log("Running");
}
first();
Key Takeaways:
- GEC is always at the base.
- Stack overflow happens when recursion has no base case.
Explain JavaScript Prototypes and Prototype Chain.
Short Interview Answer:
A prototype is an object linked to every function/object in JS where properties and methods can be inherited. The Prototype Chain is the mechanism of traversal from child prototypes upward up to Object.prototype to resolve a property.
Code Example:
const animal = { eats: true };
const rabbit = Object.create(animal);
console.log(rabbit.eats); // true (inherited via Prototype Chain)
Key Takeaways:
- Avoids copying redundant methods across instances.
- Terminates with
nullaboveObject.prototype.
What is the this keyword?
Short Interview Answer:
this refers to the object currently executing the function, evaluated entirely at execution runtime based on how the function was invoked.
Code Example:
function show() {
console.log(this);
}
const obj = { method: show };
obj.method(); // 'this' points to 'obj'
show(); // 'this' points to Window/Global
Key Takeaways:
- Value depends on execution invocation context, not lexical birthplace (except arrows).
Explain call, apply, and bind.
Short Interview Answer:
callinvokes a function immediately, specifyingthisand passing arguments individually.applydoes the same but accepts arguments as an array.bindreturns a new function withthislocked, to be called later.
Code Example:
const user = { name: "John" };
function greet(prefix) { return `${prefix}, ${this.name}`; }
console.log(greet.call(user, "Hello")); // "Hello, John"
console.log(greet.apply(user, ["Hi"])); // "Hi, John"
const bound = greet.bind(user);
console.log(bound("Welcome")); // "Welcome, John"
Key Takeaways:
- Use
call/applyfor immediate execution. - Use
bindfor currying or binding event handler contexts.
What is a Promise?
Short Interview Answer:
A Promise is an object representing the eventual completion or failure of an asynchronous operation, existing in three states: Pending, Fulfilled, or Rejected.
Code Example:
const task = new Promise((resolve, reject) => {
setTimeout(() => resolve("Success!"), 100);
});
task.then(res => console.log(res));
Key Takeaways:
- Avoids deeply nested "Callback Hell".
- Allows seamless chaining using
.then()and.catch().
What is Async/Await?
Short Interview Answer:
Async/Await is syntactic sugar built on top of Promises. It allows writing asynchronous code that looks and behaves like synchronous sequential code.
Code Example:
async function getData() {
const result = await fetch("https://api.example.com");
return await result.json();
}
Key Takeaways:
- Always wrap in
try...catchblock for comprehensive error handling. - Functions marked with
asyncalways return a Promise.
What is the Event Loop?
Short Interview Answer:
The Event Loop is the runtime mechanism that continuously monitors the Call Stack and moves callbacks from the Callback and Microtask Queues to the stack when the stack is empty.
Detailed Explanation:
JavaScript is single-threaded, meaning it can only execute one thing at a time. Offloaded asynchronous actions are managed by browser Web APIs. When web tasks finish, their callbacks land in queue categories. The Event Loop waits for the Call Stack to hit zero and then checks the Microtask Queue first (for Promises) and then the Callback/Macrotask Queue (for timers) to push them on the stack for evaluation.
Real Life Analogy:
A restaurant dispatcher. Once a table is free (stack is empty), the dispatcher calls the next customer in line (the queues) to be seated.
Code Example:
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
Output:
"Start"
"End"
"Promise"
"Timeout"
Key Takeaways:
- Microtasks (Promises) always run before Macrotasks (timers).
- Keep the main thread unblocked to avoid hanging UI.
What is Event Delegation?
Short Interview Answer:
Event delegation is a design pattern where you attach a single event listener to a parent element instead of individual child nodes, leveraging event bubbling.
Code Example:
document.getElementById("parent-list").addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log("Clicked:", e.target.innerText);
}
});
Key Takeaways:
- Saves massive memory footprints by reducing event registration.
- Handles dynamically injected elements automatically.
What is Debouncing?
Short Interview Answer:
Debouncing limits the execution of a function until a specified idle duration has elapsed since its last invocation.
Code Example:
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
Key Takeaways:
- Ideal for live-search bar requests.
- Prevents hammering backend servers with heavy API request rates.
What is Throttling?
Short Interview Answer:
Throttling enforces a maximum frequency rate of execution, ensuring a function fires at most once per defined time window.
Code Example:
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
Key Takeaways:
- Used for high-frequency events like window resize and page scrolling.
Explain Event Bubbling vs Capturing.
Short Interview Answer:
Capturing (trickling) goes top-down from parent elements down to the target. Bubbling goes bottom-up, from the clicked child target up through parents to the global window root.
Code Example:
// Bubbling (default)
element.addEventListener("click", handler);
// Capturing (3rd param set to true)
element.addEventListener("click", handler, true);
Key Takeaways:
- Use
event.stopPropagation()to prevent events moving to parents.
What are JavaScript Classes?
Short Interview Answer:
Classes are syntactic sugar built over JavaScript's existing prototype-based inheritance model, providing cleaner OOP structure.
Code Example:
class User {
constructor(name) { this.name = name; }
sayHi() { return `Hi, I am ${this.name}`; }
}
Key Takeaways:
- Strictly runs in "strict mode" by default.
- Inherits via the
extendskeyword.
What is ES6 Module System?
Short Interview Answer:
ES6 modules useimportandexportstatements. They load statically, execute strictly, and help build decoupled tree-shakeable codebases.
Code Example:
// utils.js
export const log = (msg) => console.log(msg);
// main.js
import { log } from "./utils.js";
Key Takeaways:
- Statically analyzed by build tools.
Explain Set vs Array.
Short Interview Answer:
ASetis an unordered collection of unique values, while anArrayis an ordered collection allowing duplicate values.
Code Example:
const values = new Set([1, 2, 2, 3]);
console.log(values); // Set { 1, 2, 3 }
Key Takeaways:
- Set lookup time complexity is $O(1)$ compared to $O(n)$ in arrays.
What is Currying?
Short Interview Answer:
Currying is a pattern where a function taking multiple arguments is transformed into a series of nested functions that take only one argument each.
Code Example:
const currySum = a => b => c => a + b + c;
console.log(currySum(1)(2)(3)); // 6
Key Takeaways:
- Excellent for function composition and configuration reuse.
What is Memoization?
Short Interview Answer:
Memoization is an optimization technique that caches the return values of expensive function computations based on their input parameters.
Code Example:
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
Key Takeaways:
- Speeds up repetitive operations with trade-off memory usage.
What is a Polyfill?
Short Interview Answer:
A polyfill is fallback JS code used to provide modern feature support to older browser clients lacking that capability.
Code Example:
// Polyfill for Array.prototype.includes
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement) {
return this.indexOf(searchElement) !== -1;
};
}
Key Takeaways:
- Crucial for backward-compatibility.
Explain Promises combinators: Promise.all vs Promise.allSettled.
Short Interview Answer:
Promise.allrejects immediately if any promise in the array fails.Promise.allSettledwaits for all inputs to complete/fail and returns their status arrays.
Code Example:
const p1 = Promise.resolve(1);
const p2 = Promise.reject("error");
Promise.allSettled([p1, p2]).then(results => console.log(results));
Key Takeaways:
- Use
allSettledwhen individual tasks don't rely on each other's state.
What is a Callback Function and Callback Hell?
Short Interview Answer:
A callback is a function passed as an argument to another function. Callback Hell is the term for deeply nested, unreadable callback structures that complicate control flow.
Code Example:
asyncOperation1(() => {
asyncOperation2(() => {
asyncOperation3(() => {
// Callback hell nesting
});
});
});
Key Takeaways:
- Resolved by transitioning to Promises and Async/Await.
Advanced & Runtime Mechanics (Questions 36-50)
Explain Generators and Iterators.
Short Interview Answer:
Iterators are objects defining a standardnext()output sequence structure. Generators are special functions that can pause/resume execution via theyieldkeyword, producing iterators under the hood.
Detailed Explanation:
Normal functions exit completely once they return. Generators (marked with function*) break this paradigm. They can yield execution mid-flow, returning progress states in the form { value, done }. The engine preserves the entire local frame state, ready to resume on the next execution request.
Code Example:
function* numberGenerator() {
yield 1;
yield 2;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }
Output:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }
Interview Follow-up Questions:
Interviewer: Can we pass values back into a generator function programmatically?
Candidate: Yes, by passing a parameter into the next("value") call. It acts as the evaluated replacement of the paused yield expression inside the generator.
Key Takeaways:
- Enables lazily-evaluated infinite data streams.
- Highly effective for custom flow-control runtimes (like Redux-Saga).
What is a Memory Leak and how to detect/fix it?
Short Interview Answer:
A memory leak occurs when items are no longer used by the application but remain allocated in the Heap because references to them are still active.
Code Example:
// Memory Leak: Unintentional global variable
function leaky() {
leakedVar = new Array(1000000); // Assigned to window scope
}
Key Takeaways:
- Common causes: uncleared timers, orphan DOM nodes, and global variables.
- Monitor leaks using the Chrome DevTools Memory Profiles tab.
What is Garbage Collection?
Short Interview Answer:
Garbage Collection is an automated process in the JS engine that frees up memory occupied by objects that are no longer reachable from the root execution contexts.
Key Takeaways:
- Uses the Mark-and-Sweep garbage collection algorithm.
- Runs non-deterministically behind the scenes.
Explain WeakMap and WeakSet.
Short Interview Answer:
WeakMapandWeakSetare collections that only accept objects/symbols as keys, maintaining "weak" references to those objects so they can be collected by Garbage Collection if no other references exist.
Code Example:
let cache = new WeakMap();
let obj = {};
cache.set(obj, "metadata");
obj = null; // Object is now eligible for GC, cleared from WeakMap automatically
Key Takeaways:
- Keys are non-enumerable.
- Essential for managing cache or metadata without memory leaks.
Explain Javascript Proxies.
Short Interview Answer:
A Proxy wraps an object, letting you intercept and customize fundamental operations like reading, writing, and modifying properties.
Code Example:
const target = { price: 100 };
const proxy = new Proxy(target, {
get(target, prop) {
return prop in target ? `$${target[prop]}` : "$0";
}
});
console.log(proxy.price); // "$100"
Key Takeaways:
- Powerful for logging, profiling, validations, and reactive frameworks.
What is Reflect API?
Short Interview Answer:
Reflect is a built-in object providing methods to call interceptable JavaScript operations, mirroring proxy interceptors to simplify forwarding operations.
Code Example:
const user = { name: "Alice" };
console.log(Reflect.has(user, "name")); // true
Key Takeaways:
- Provides a clean, standardized, functional way to manipulate objects.
What are Web Workers?
Short Interview Answer:
Web Workers are browser APIs allowing JavaScript code to execute in background background threads, independent of the main user-interface thread.
Code Example:
// Main thread
const worker = new Worker("worker.js");
worker.postMessage("Start calculation");
worker.onmessage = (e) => console.log("Result:", e.data);
Key Takeaways:
- Crucial for offloading intense math tasks.
- Workers cannot access the DOM directly.
Explain V8 Engine Pipeline compilation.
Short Interview Answer:
V8 parses JavaScript into an Abstract Syntax Tree (AST), compiles it to Bytecode via the 'Ignition' interpreter, and optimizes it using 'TurboFan' into fast machine code based on hot performance loops.
Key Takeaways:
- Uses JIT (Just-In-Time) compilation.
- De-optimizes compiled code when type predictions fail.
Write a Polyfill for Promise.all.
Short Interview Answer:
A custom function that returns a Promise, iterates through the array of promises, resolves each, and collects the results in a matching-index array. If any promise rejects, it rejects immediately.
Code Example:
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
let completed = 0;
const results = [];
if (promises.length === 0) return resolve(results);
promises.forEach((p, idx) => {
Promise.resolve(p)
.then(val => {
results[idx] = val;
completed++;
if (completed === promises.length) resolve(results);
})
.catch(reject);
});
});
}
Key Takeaways:
- Ensures indexes match the original array, regardless of resolution order.
What is a Deep Copy polyfill?
Short Interview Answer:
A custom utility function that recursively copies all elements of objects and arrays, preserving depth and references while decoupling original entities.
Code Example:
function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
const clone = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
Key Takeaways:
- Essential for state preservation without libraries.
Write a Custom Promise Polyfill.
Short Interview Answer:
A Class representing a custom Promise implementation that captures status, value, and callbacks, handling asynchronous execution flows manually.
Code Example:
class MyPromise {
constructor(executor) {
this.status = "pending";
this.value = undefined;
this.onResolved = [];
const resolve = (val) => {
if (this.status === "pending") {
this.status = "fulfilled";
this.value = val;
this.onResolved.forEach(fn => fn(val));
}
};
executor(resolve);
}
then(callback) {
if (this.status === "fulfilled") {
callback(this.value);
} else {
this.onResolved.push(callback);
}
return this;
}
}
Key Takeaways:
- Underpinning mechanics of asynchronous state storage.
Explain Tail Call Optimization.
Short Interview Answer:
Tail Call Optimization is a compiler mechanism where the engine avoids allocating a new stack frame if the last action of a function is a direct call returning another function.
Code Example:
function factorial(n, accum = 1) {
if (n <= 1) return accum;
return factorial(n - 1, n * accum); // Tail Call Position
}
Key Takeaways:
- Prevents stack overflow during deep recursion.
What is Symbol and its typical use-cases?
Short Interview Answer:
A Symbol is a unique and immutable primitive value, primarily used to create hidden, collision-free object property keys.
Code Example:
const hiddenId = Symbol("id");
const item = {
[hiddenId]: 4022
};
console.log(item[hiddenId]); // 4022
Key Takeaways:
- Guarantees absolute key uniqueness.
- Symbols are ignored by standard
for...inloops.
What is a Shadow DOM?
Short Interview Answer:
A Shadow DOM is a self-contained, encapsulated DOM tree detached from the main document DOM, preventing styles and structures from leaking in or out.
Code Example:
const host = document.querySelector("#shadow-host");
const shadowRoot = host.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<style>p { color: red; }</style><p>Shadow Boundary!</p>`;
Key Takeaways:
- Foundation of Web Components encapsulation.
Advanced Interview Tricks & Edge Cases.
Short Interview Answer:
Tricks usually target unexpected behaviors like0.1 + 0.2 !== 0.3(floating-point binary representation),NaN !== NaN, and automatic semicolon insertion side-effects.
Code Example:
console.log(0.1 + 0.2 === 0.3); // false
console.log(Number.NaN === Number.NaN); // false
Key Takeaways:
- Always use epsilon checks or rounding for floating-point calculations.
- Use
Number.isNaN()to check for invalid calculations.
Final Revision Checklist
- Understand the execution context phases (Creation vs Execution).
- Master scoping paradigms (Block vs Global vs Lexical vs Function).
- Explain Event Loop task prioritization (Sync > Microtasks > Macrotasks).
- Demonstrate prototypal inheritance and class mechanisms.
- Comfortably write polyfills (Map, Filter, Reduce, Promise.all, Bind).
- Differentiate copy mechanisms (Shallow vs Deep vs JSON strategies).
Senior Interview Strategy Tips
When an interviewer asks a code execution question, trace the flow verbally. Identify the Call Stack states, mention the Microtask Queue and Macrotask Queue explicitly, and demonstrate depth over memorized definitions. That approach instantly signals high capability to senior interviewers.
