By Syed Muhammad Ali
🔗 Portfolio: ali-portfolio-nine.vercel.app
🐙 GitHub: github.com/Syed-Muhammad-Ali-git
💼 LinkedIn: linkedin.com/in/syed-muhammed-ali/
This repository contains 100+ JavaScript interview questions and answers from beginner to advanced levels, including real-world scenarios. It is designed to help developers prepare for interviews in small software houses to large tech companies.
- JavaScript Basics
- Operators & Control Flow
- Functions
- Arrays & Objects
- Scope & Closures
- Asynchronous JavaScript
this, Call, Apply, Bind- DOM Manipulation
- Objects, Prototypes & Classes
- Error Handling & Try/Catch
- Advanced Concepts
- Real-World Edge Cases
- Performance & Optimization
- Testing & Debugging
- Modern JavaScript (ES6+)
- Bonus / Real-World Scenarios
- Rapid-Fire Questions
Answer: var is function-scoped and hoisted (initialized to undefined), allowing redeclaration and reassignment. let and const are block-scoped, hoisted but in the Temporal Dead Zone (TDZ)—accessing them before declaration throws a ReferenceError. const prevents reassignment (but allows mutation of objects/arrays). Prefer let/const to avoid hoisting pitfalls and promote immutability.
// Example code snippet
var a = 1; // Function-scoped
if (true) {
let b = 2;
} // b not accessible outside block
const c = {};
c.prop = 1; // Mutation allowed, but c = {} throws errorAnswer: == performs type coercion (e.g., '1' == 1 is true) before comparison, which can lead to unexpected results. === checks strict equality without coercion (e.g., '1' === 1 is false). Always use === unless you specifically need coercion.
Answer: The falsy values are: false, 0, -0, 0n (BigInt zero), "" (empty string), null, undefined, NaN. Everything else is truthy, including non-empty strings, non-zero numbers, and most objects.
Answer: Use if (x == null) to check both (due to coercion). For precision: x === null or typeof x === 'undefined'. Avoid if (!x) as it catches all falsy values.
Answer: NaN (Not-a-Number) represents invalid numeric operations (e.g., 0 / 0). It's the only value not equal to itself (NaN !== NaN). Check with Number.isNaN(value) (strict, no coercion) or isNaN(value) (coerces, e.g., isNaN('abc') is true).
Answer: Template literals use backticks (`) for multi-line strings and interpolation:
const name = "World";
console.log(`Hello ${name}!`); // "Hello World!"Answer: Function parameters can have defaults if no argument is passed:
function greet(name = 'Guest') { return Hi ${name}; }
greet(); // "Hi Guest"
Answer: Use Object.assign({}, obj) or spread syntax {...obj}. Both create a shallow copy—nested objects are referenced, not cloned.
Answer: Dot notation (obj.a) requires a valid identifier (no spaces/special chars). Bracket notation (obj['a']) allows dynamic keys (e.g., obj[keyVar]) or invalid chars (e.g., obj['a-b']).
Answer: Use arr.slice(), spread [...arr], or Array.from(arr). These copy the top-level elements but share nested references.
Answer: typeof null returns 'object' due to a historical bug in JS (null was meant to be a primitive). Always treat null distinctly from objects (e.g., via x === null).
Answer: && evaluates left-to-right, returning the first falsy value or the last truthy. || returns the first truthy or the last falsy. Useful for guards/defaults: const x = input || 'default'; // x is 'default' if input falsy const y = a && b; // Short-circuits if a falsy
Answer: Extract values from arrays/objects into variables: const {a, b} = obj; // From object const [x, y] = arr; // From array const {a: renamed} = obj; // Rename
Answer: Concise syntax: const f = x => x * 2. They lexically bind this (inherit from enclosing scope), can't be used as constructors (new throws error), and lack their own arguments object. Gotcha: Alters this behavior in callbacks/objects.
Answer: Number('123'), unary +'123', parseInt('123', 10) (integers), or parseFloat('123.45'). Handle errors with checks (e.g., isNaN).
Answer: Array.isArray(arr) is reliable. Avoid typeof (returns 'object') or instanceof Array (fails in iframes/multi-frames).
Answer: JS's single-threaded concurrency model processes the call stack, then microtasks (e.g., Promises via queueMicrotask), then macrotasks (e.g., setTimeout). Microtasks run before the next macrotask, enabling async patterns without blocking. Example: Promises resolve before timers.
Answer: undefined indicates a variable/property not assigned (default for uninitialized vars). null is an explicit assignment meaning "no value/object." Use null for intentional absence.
Answer: A primitive for unique keys: const s = Symbol('desc'). Prevents key collisions in objects: obj[s] = 'value'. Symbols are not enumerable by default.
Answer: Cache args-to-result in a Map:
function memo(f) { const cache = new Map(); return (...a) => { const k = JSON.stringify(a); if (cache.has(k)) return cache.get(k); const r = f(...a); cache.set(k, r); return r; }; }
Answer: forEach iterates for side-effects (no return value). map transforms each element, returning a new array. filter returns elements passing a predicate (new array). reduce aggregates into a single value (e.g., sum). All are non-mutating except potential side-effects in callbacks.
Answer: forEach can't be broken. Use for...of, traditional for, or some()/every() (return true/false to stop).
Answer: Access nested properties safely: obj?.prop?.nested returns undefined if any part is null/undefined, avoiding errors. Also works for methods (arr?.find()) and arrays (arr?.[0]). Introduced in ES2020.
Answer: x ?? 'default' returns the right operand if left is null/undefined (ignores other falsy like 0/''). Unlike ||, it's precise for "no value" cases.
Answer: Spread expands iterables: [...arr] copies arrays, {...obj} copies objects. Rest collects: function f(...args) {} or {a, ...rest} = obj.
Answer: JSON.parse(JSON.stringify(obj)) serializes/deserializes, cloning primitives/objects/arrays but losing undefined, functions, Dates (become strings), and non-JSON types. For full deep clone, use libraries like lodash.
Answer: Prefix template literals for custom processing:
function tag(strings, ...values) { return strings[0] + values[0]; }
tagHello ${name}; // Processes raw strings and interpolated values
Used in libraries like styled-components.
Answer: {...a, ...b} or Object.assign({}, a, b) (later overrides earlier). Both shallow—nested objects merge references.
Answer: WeakMap/WeakSet hold object keys weakly (allow garbage collection if no other references). No iteration/enumeration, no size/weak keys only. Use for private data or caches to avoid memory leaks.
Answer: JS moves declarations to scope top before execution. var/function hoist with init (undefined/function body); let/const hoist but enter TDZ (error on access). Affects order: use declarations before use.
Answer: A function that accepts functions as arguments or returns them (e.g., map, filter). Enables composition: const compose = (f, g) => x => f(g(x)); const add1 = x => x + 1; const double = x => x _ 2; compose(double, add1)(5); // 12 Common in functional programming for reusability.
Answer: function f(a = 1, {b = 2} = {}) {} (destructuring defaults). Evaluated lazily on call.
Answer: Converts multi-arg function to chain of single-arg functions: const add = a => b => a + b; add(1)(2); // 3 Enables partial application (e.g., const add5 = add(5); add5(3) // 8). Useful for config or composition.
Answer: Debounce: Delays execution until after a pause in calls (e.g., search input—fires once after typing stops). Throttle: Limits to once per interval (e.g., scroll handler—fires max every 100ms). Both prevent excessive calls; debounce for final state, throttle for periodic.
Answer: Wrapper executes only first call, caches result: function once(fn) { let done = false, res; return (...a) => { if (done) return res; done = true; res = fn(...a); return res; }; } Idempotent for setup/init.
Answer: Yes, methods: const obj = { greet() { return 'Hi'; } }; obj.greet(); // "Hi" this binds to obj in non-arrow methods.
Answer: Declaration function f() {} hoists fully. Expression const f = function() {} hoists var but TDZ for const. Use declarations for top-level, expressions for conditionals.
Answer: function f(...args) {} collects trailing args into array. Can't follow rest params; use with defaults/spread.
Answer: arguments.length (array-like) or (...args) => args.length. arguments is deprecated in arrows.
Answer: function_ gen() { yield 1; yield 2; } produces iterators, pausing at yield. Resumable: const it = gen(); it.next(); // {value: 1, done: false} For lazy sequences (e.g., infinite lists) or async flows (pre-async/await). Edge: No return in loops; handles exceptions via throw().
Answer: new Function('a', 'return a*2') creates runtime function from strings (global scope). Gotchas: Security (eval-like, XSS risk), no lexical this/closures, slower parsing.
Answer: const bound = f.bind(obj) returns new function with fixed this. Partial args: bind(obj, arg1).
Answer: No: Lack prototype, new Arrow() throws "not a constructor." Use for callbacks, not classes.
Answer: f.length counts formal params (ignores defaults/rest). E.g., function(a,b=1,...c){} has length 1.
Answer: Function.prototype.myBind = function(thisArg, ...args) { const fn = this; return function(...moreArgs) { return fn.apply(thisArg, [...args, ...moreArgs]); }; }; Handles partial application; use call for immediate.
Answer: [...new Set(arr)] or Array.from(new Set(arr)). Preserves order; O(n) time.
Answer: arr.flat() or [].concat(...arr). For deeper, flat(depth).
Answer: arr.flat(Infinity) flattens all levels. Polyfill with recursion.
Answer: Object.prototype.hasOwnProperty.call(obj, 'key') (avoids prototype pollution). Or obj.hasOwnProperty('key') if trusted.
Answer: Object.keys(obj) (array of keys), Object.values(obj) (values), Object.entries(obj) (key-value pairs). ForEach over them.
Answer: Object.defineProperty(obj, 'key', { enumerable: false, writable: false, value: 1 }). Shallow; use for constants/sealing.
Answer: Map: Any key type, insertion order, size/iteration methods, no proto pollution. Object: String/symbol keys, no order guarantee, enumerable issues. Use Map for dynamic keys.
Answer: Object.fromEntries(map.entries()). Assumes string/symbol keys.
Answer: const set = new Set(arr1); return arr2.filter(x => set.has(x));. O(n+m); assumes primitives.
Answer: arr.sort((a, b) => a.key - b.key) (numbers) or a.key.localeCompare(b.key) (strings). Stable in modern engines.
Answer: obj && obj.a && obj.a.b or lodash _.get(obj, 'a.b'). Reduces errors in deep chains.
Answer: Object.freeze(obj) prevents additions/changes (shallow). Deep: Recurse or use libraries.
Answer: Both shallow merge/copy. Object.assign(target, ...sources) mutates target; {...a, ...b} creates new. Spread is ES6+ syntactic sugar.
Answer: delete obj.key (mutates). Immutable: {...obj, [key]: undefined} or destructuring {key, ...rest}.
Answer: Array.from(nodeList) or [...nodeList]. Works for arguments, DOM collections.
Answer: Array.prototype.sort is stable in V8 (Chrome/Node) since 2018, but spec doesn't require it—test or use indexed sort for guarantees (e.g., ties preserve order).
Answer: arr.reduce((acc, item) => { acc[item.key] = acc[item.key] || []; acc[item.key].push(item); return acc; }, {});
Answer: function shallowEqual(a, b) { const keys = Object.keys(a); return keys.length === Object.keys(b).length && keys.every(k => a[k] === b[k]); }
Answer: Recursive: Check types, then keys/values (handle arrays/objects). Watch for cycles (use WeakMap). Libraries: lodash isEqual. function deepEqual(a, b) { if (a === b) return true; if (typeof a !== 'object' || typeof b !== 'object') return false; // Recurse on keys... }
Answer: function chunk(arr, size) { const res = []; for (let i = 0; i < arr.length; i += size) { res.push(arr.slice(i, i + size)); } return res; }
Answer: A closure is a function bundled with its lexical environment (variables from outer scope), accessible even after outer function returns. Enables encapsulation: function outer() { let x = 1; // Captured return function inner() { return x++; }; // Accesses x } const counter = outer(); counter(); // 1, then 2 next call Common in modules/callbacks; memory: Retained until closure discarded.
Answer: - Privacy: Hide vars (e.g., counters without globals).
Memoization: Cache in outer scope. Partial/curry: Fix args in returned function. Event handlers: Preserve state across async calls. Edge: Can cause memory leaks if not managed (e.g., DOM refs).
Answer: For let/const, from scope entry to declaration line—access throws ReferenceError (unlike var's undefined). Promotes declaration-first:
console.log(a); // ReferenceError (TDZ) let a = 1; Affects blocks/functions; var has no TDZ.
Answer: var shares one binding across loop iterations; all closures capture the same (final) value. Fix: let (block-scoped per iteration) or IIFE:
// Bug for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // 3,3,3
// Fix with let for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // 0,1,2
Answer: Immediately Invoked Function Expression: (function() { /_ code / })();. Creates private scope instantly, avoiding globals. Pre-ES6 for modules: (function() { var private = 1; / ... _/ })();
Answer: - Closures (as above).
Classes: #private fields (ES2022): class MyClass { #secret = 42; getSecret() { return this.#secret; } } WeakMaps for instances. Avoids public exposure.
Answer: IIFE-based encapsulation: Reveal public API, hide private. const module = (function() { let private = 1; function privateFn() { /_ ... _/ } return { public: () => private++ }; })(); module.public(); // Access controlled
Pre-ES6 alternative to imports/exports; promotes singletons.
Answer: Object for async ops: Pending → Fulfilled (success) or Rejected (error). Chain with .then/.catch. new Promise((res,rej)=>{res('done')}).then(v=>console.log(v)); // "done"
Answer: Promises: Chainable, handle async without "callback hell." Callbacks: Nested, error-prone for deep async.
Answer: Syntactic sugar over Promises: async function f(){const v=await p;}. Errors via try/catch. async function g(){try{const d=await fetch('/api');return d.json();}catch(e){console.error(e);}}
Gotcha: Always returns Promise; use top-level await in modules.
Answer: Promise.all([p1,p2]) waits all (rejects on any); Promise.race([p1,p2]) first settle; allSettled all statuses.
Answer: resolve(value) fulfills; reject(error) rejects. Static: Promise.resolve(1) immediate fulfill.
Answer: .then returns new Promise; errors bubble to next .catch. Pitfall: Forgetting return in .then passes undefined. p.then(v=>v*2).then(w=>console.log(w)); // Chains
Answer: Browser API for HTTP: fetch(url).then(r=>r.json()). Defaults GET; options for POST/etc. Polyfill needed for old browsers.
Answer: Sync: Blocks thread (e.g., loops). Async: Non-blocking (e.g., setTimeout, Promises)—event loop queues.
Answer: Promisify: new Promise((res,rej)=>{fs.readFile(path,(e,d)=>{e?rej(e):res(d);});}). Use util.promisify in Node.
Answer: Micro: Promises/queueMicrotask—run next tick. Macro: setTimeout/setInterval—after microtasks. Affects order: Micro before render.
Answer: Catches rejections: async function h(){try{await badPromise();}catch(e){/handle/}} Uncaught if no await or top-level without global handler.
Answer: Cancel requests: const ac=new AbortController(); fetch(url,{signal:ac.signal}); ac.abort();. Prevents leaks.
Answer: class MyPromise{constructor(ex){this.s=0;this.v;this.c=[];ex((v)=>{this.s=1;this.v=v;this.c.forEach(f=>f(v));},(e)=>{this.s=2;this.v=e;});}then(onF,onR){return new MyPromise((res,rej)=>{if(this.s===1)onF(this.v);else if(this.s===2)onR(this.v);else{this.c.push(([f,r])=>{try{const rv=f(this.v);res(rv);}catch(e){rej(e);}});}});}/*simplified*/};
Handles states/chaining; edges: No race conditions.
Answer: Intercept obj ops: new Proxy(obj,{get(t,p){return t[p];}}) for validation/logging. Use in Vue for reactivity.
Answer: Mirror Proxy traps: Reflect.get(obj,prop)—functional ops without side-effects. Pairs with Proxies.
Answer: Arbitrary-precision ints: 1n or BigInt(1). For >2^53 nums; ops like a+b with n.
Answer: #private: class C{#p=1; #m(){return this.#p;}}. Truly private, no inheritance leak.
Answer: Lazy load: import('./mod.js').then(m=>m.func()). Async, code-splitting for perf.
Answer: try{}catch{/no var/}—ignores errors if unhandled.
Answer: 1_000_000 for readability; ignored in nums.
Answer: Now spec-required stable (ties preserve order). V8 was early.
Answer: Reverse find: arr.findLast(x=>x>5)—last match from end.
Answer: Weak hold on objects: new WeakRef(obj).deref()—null if GC'd. For caches without leaks.
Answer: Modern date/time: Temporal.Now.zonedDateTimeISO(). Safer than Date; not standard yet (2025 stage 3).
Answer: In modules: await p; at top—pauses module eval. For dynamic imports.
Answer: #!/usr/bin/env node—Shebang for Node scripts.
Answer: Module metadata: import.meta.url—current file URL.
Answer: x??=1 (nullish assign), x||=2 (OR), x&&=3 (AND). Concise defaults.
Answer: Objects delegate to prototypes: obj.proto. Chain: Lookup up until null. const p={g:1}; const c=Object.create(p); c.g //1 (delegates) Edges: Modifying proto affects all; use classes for clarity.
Answer: Default: Global/undefined. Implicit: Obj method. Explicit: call/apply/bind. New: Constructor. Arrow: Lexical. Gotcha: Loses in callbacks—bind.
Answer: Encapsulate code: ES modules (import/export)—static, async. CommonJS (require/module.exports)—dynamic, sync. Browsers need type="module".
Answer: class EE{constructor(){this.e={};}on(ev,f){(this.e[ev]??=[]).push(f);}emit(ev,...a){this.e[ev]?.forEach(f=>f(...a));}off(ev,f){this.e[ev]=this.e[ev]?.filter(g=>g!==f);}} Handles subscribe/emit; edges: Memory—off unused.
Answer: Stack: LIFO execution frames (primitives/refs). Heap: Objects/arrays. Overflow: Recursion depth.
Answer: Mark-and-sweep: Roots (globals/stack) mark reachable; sweep unmarked. WeakMaps aid leaks.
Answer: Polyfill: Runtime impl (e.g., Promise). Shim: Lib wrapper. Transpiler: Source convert (Babel ES6→ES5).
Answer: typeof window!=='undefined' (browser); avoid feature detect over UA sniffing.
Answer: Proxy for fetch/cache: Offline PWA. Register: navigator.serviceWorker.register('/sw.js').
Answer: Binary code in browsers: Fast, near-native. Compile C++/Rust→Wasm; JS interop via imports.
Answer: ==: Coerce. ===: Strict. Object.is: Strict but NaN===NaN, -0!==+0 false both.
Answer: const is=(a,b)=>a===b?true:(a!==a&&b!==b)?true:(a!==0||b!==0)?(1/a===1/b):false;
Answer: Reusable solutions: Singleton (one instance): Module pattern or class with static getInstance.
Answer: Curry: All unary chain. Partial: Fix some args, keep arity. Curry implies partial.
Answer: Node: process.on('uncaughtException'). Browser: window.addEventListener('unhandledrejection'). Log/notify.
This section will contain DOM manipulation questions in future updates.
This section will contain real-world edge case questions in future updates.
This section will contain performance and optimization questions in future updates.
This section will contain testing and debugging questions in future updates.
This section will contain bonus real-world scenario questions in future updates.
This section will contain rapid-fire questions in future updates.
Syed Muhammad Ali
🔗 Portfolio: ali-portfolio-nine.vercel.app 🐙 GitHub: github.com/Syed-Muhammad-Ali-git 💼 LinkedIn: linkedin.com/in/syed-muhammed-ali/