Top Javascript Interview Questions

JavaScriptHigher-Order FunctionsReal-World
Higher-order functions take functions as arguments or return functions. Examples include map for transforming data in a dashboard or setTimeout for delayed API retries in a payment system.
Example:
const multiply = x => y => x * y;
const double = multiply(2);
console.log(double(5)); // 10

JavaScriptEvent LoopMicrotasksMacrotasks
The event loop manages JavaScript’s single-threaded concurrency by processing the call stack, microtask queue (e.g., Promise.then), and macrotask queue (e.g., setTimeout). It clears the call stack, then microtasks, then one macrotask per cycle, ensuring non-blocking execution.
Example:
setTimeout(() => console.log('Macrotask'), 0);
Promise.resolve().then(() => console.log('Microtask'));
console.log('Sync'); // Sync, Microtask, Macrotask

JavaScriptFunction CurryingImplementation
Currying transforms a function with multiple arguments into a sequence of single-argument functions. It’s useful for partial application and reusable function templates.
Example:
const curry = fn => (...args) => args.length >= fn.length ? fn(...args) : curry(fn.bind(null, ...args));
const add = curry((a, b) => a + b);
console.log(add(2)(3)); // 5

JavaScriptPolyfillsArray.mapFunction.bind
A polyfill for Array.prototype.map iterates over an array, applies a callback to each element, and returns a new array. For bind, it returns a new function with a fixed this and pre-set arguments.
Example:
if (!Array.prototype.map) {
  Array.prototype.map = function(callback, thisArg) {
    const result = [];
    for (let i = 0; i < this.length; i++) {
      result.push(callback.call(thisArg, this[i], i, this));
    }
    return result;
  };
}
console.log([1, 2].map(x => x * 2)); // [2, 4]

JavaScriptcallapplybindFunction Methods
call() invokes a function with a specified this and arguments individually. apply() is similar but takes arguments as an array. bind() creates a new function with a fixed this and optional arguments. Use call/apply for immediate invocation, bind for reusable functions.
Example:
function greet(name) { return `${name}, ${this.title}`; }
const obj = { title: 'Mr' };
console.log(greet.call(obj, 'John')); // John, Mr
console.log(greet.apply(obj, ['Jane'])); // Jane, Mr
const bound = greet.bind(obj, 'Joe');
console.log(bound()); // Joe, Mr

JavaScriptClosuresReal-World
Closures occur when a function retains access to its lexical scope's variables after the outer function has finished executing. They enable data privacy and state persistence. Real-world use cases include creating private counters, event handlers with preserved state, or module patterns for encapsulation.
Example:
function createCounter() {
  let count = 0;
  return () => ++count;
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

JavaScriptthisArrow FunctionsRegular Functions
In regular functions, this is determined by how the function is called (e.g., object method, global). Arrow functions inherit this from their lexical scope, ignoring call context.
Example:
const obj = {
  name: 'Test',
  regular: function() { return this.name; },
  arrow: () => this.name
};
console.log(obj.regular()); // Test
console.log(obj.arrow()); // undefined (window.name)

JavaScriptDebouncingThrottlingImplementation
Debouncing delays a function until a specified time after the last call, useful for input validation. Throttling limits execution to once per interval, ideal for scroll events. Both optimize performance.
Example:
function debounce(fn, delay) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), delay);
  };
}
const log = debounce(console.log, 1000);
log('Test'); // Logs after 1s

JavaScriptasync/awaitInternal Working
async/await is built on promises and generators. An async function returns a promise, and await suspends execution until the promise resolves, using the event loop to manage concurrency.
Example:
async function fetchData() {
  const data = await new Promise(resolve => setTimeout(() => resolve('Data'), 1000));
  return data;
}
fetchData().then(console.log); // Data

JavaScriptPromisesCallbacksAsynchronous Programming
Promises represent asynchronous operation results with states (pending, fulfilled, rejected). Unlike callbacks, they avoid nested callback hell, support chaining, and handle errors better.
Example:
const promise = new Promise((resolve) => setTimeout(() => resolve('Done'), 1000));
promise.then(console.log); // Done

JavaScriptCallback HellAsynchronous Programming
Callback hell is deeply nested callback functions, making code hard to read. Avoid it with promises, async/await, or modularizing code.
Example:
async function fetchData() {
  const data = await new Promise(resolve => setTimeout(() => resolve('Data'), 1000));
  console.log(data);
}
fetchData(); // Cleaner than nested callbacks

JavaScriptEqualityStrict EqualityType Coercion
== performs type coercion before comparison, while === checks value and type without coercion. Using === prevents unexpected behavior from coercion, improving code reliability.
Example:
console.log(5 == '5'); // true
console.log(5 === '5'); // false

JavaScriptLexical ScopingClosures
Lexical scoping means a variable’s scope is determined by its location in the source code. Closures rely on lexical scoping to retain access to outer function variables after execution.
Example:
function outer() {
  let x = 10;
  return () => x++;
}
const inc = outer();
console.log(inc()); // 10
console.log(inc()); // 11

JavaScriptPromise.allPromise.racePromise.any
Promise.all resolves when all promises resolve or rejects on any rejection. Promise.race resolves or rejects with the first settled promise. Promise.any resolves with the first fulfilled promise, ignoring rejections unless all fail.
Example:
Promise.all([Promise.resolve(1), Promise.resolve(2)]).then(console.log); // [1, 2]
Promise.race([new Promise((_, r) => setTimeout(r, 1000)), Promise.resolve(3)]).then(console.log); // 3

JavaScriptAsync Error HandlingAsynchronous Programming
Handle async errors with .catch() for promises or try/catch for async/await. Ensure unhandled rejections are logged or managed globally.
Example:
async function fetchData() {
  try {
    await Promise.reject('Error');
  } catch (err) {
    console.log(err); // Error
  }
}
fetchData();

JavaScriptMemoizationImplementation
Memoization caches function results for given inputs to avoid redundant computation. Use a Map or object as the cache.
Example:
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (!cache.has(key)) cache.set(key, fn(...args));
    return cache.get(key);
  };
}
const add = memoize((a, b) => a + b);
console.log(add(1, 2)); // 3

JavaScriptHoistingvarletconst
var is hoisted and initialized with undefined. let and const are hoisted but not initialized, creating a Temporal Dead Zone until their declaration is reached.
Example:
console.log(x); // undefined
var x = 5;
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;

JavaScriptGarbage CollectionMemory Management
JavaScript’s garbage collector (e.g., Mark-and-Sweep) frees memory for objects no longer reachable from roots (global object, stack). It marks live objects and sweeps unmarked ones.
Example:
let obj = { data: 'test' };
obj = null; // Eligible for garbage collection

JavaScriptModule PatternClosures
The module pattern uses an IIFE to create a private scope, leveraging closures to encapsulate private data and expose a public API.
Example:
const Module = (function() {
  let privateVar = 0;
  return { increment: () => ++privateVar };
})();
console.log(Module.increment()); // 1

JavaScriptPrototypal InheritanceOOP
Prototypal inheritance links objects via their [[Prototype]], allowing property/method lookup through the prototype chain until null is reached.
Example:
const parent = { greet: () => 'Hello' };
const child = Object.create(parent);
console.log(child.greet()); // Hello

JavaScriptCustom Event SystemEvent Handling
A custom event system stores event handlers in an object and provides methods to subscribe, unsubscribe, and trigger events.
Example:
class EventEmitter {
  constructor() { this.events = {}; }
  on(event, cb) { this.events[event] = this.events[event] || []; this.events[event].push(cb); }
  emit(event, ...args) { this.events[event]?.forEach(cb => cb(...args)); }
}
const ee = new EventEmitter();
ee.on('greet', name => console.log(name));
ee.emit('greet', 'Alice'); // Alice

JavaScriptClassical InheritancePrototypal Inheritance
Classical inheritance (e.g., Java) uses classes and rigid hierarchies. Prototypal inheritance (JavaScript) is dynamic, linking objects directly via prototypes, offering flexibility but less structure.
Example:
function Animal() {}
Animal.prototype.speak = function() { return 'Sound'; };
const dog = new Animal();
console.log(dog.speak()); // Sound

JavaScriptObject.createClass SyntaxConstructor Functions
Object.create creates an object with a specified prototype. Constructor functions use new to instantiate objects with a shared prototype. Class syntax is syntactic sugar over constructor functions, adding cleaner syntax for inheritance.
Example:
const proto = { x: 1 };
const obj = Object.create(proto);
function Constr() { this.y = 2; }
const constr = new Constr();
class MyClass { constructor() { this.z = 3; } }
const cls = new MyClass();
console.log(obj.x, constr.y, cls.z); // 1, 2, 3

JavaScriptEvent DelegationEvent Handling
Event delegation attaches a single event listener to a parent element to handle events from descendants, leveraging bubbling. It’s useful for dynamic elements and reducing memory usage.
Example:
document.getElementById('parent').addEventListener('click', e => {
  if (e.target.tagName === 'BUTTON') console.log('Button clicked');
});

JavaScriptnew KeywordImplementation
Implement new by creating an object, setting its prototype, calling the constructor with this, and returning the result (or the object if undefined).
Example:
function myNew(Constr, ...args) {
  const obj = Object.create(Constr.prototype);
  const result = Constr.apply(obj, args);
  return typeof result === 'object' && result !== null ? result : obj;
}
function Test(a) { this.a = a; }
const instance = myNew(Test, 5);
console.log(instance.a); // 5

JavaScriptMemory LeaksSingle-Page Applications
Prevent memory leaks by removing event listeners, clearing timers, nullifying references, and using WeakMap/WeakSet. Profile with dev tools to detect leaks.
Example:
const btn = document.getElementById('btn');
const handler = () => console.log('Click');
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler); // Cleanup

JavaScriptinnerHTMLtextContentDOM
innerHTML sets or gets HTML content, parsing it as HTML, which can execute scripts and is prone to XSS. textContent sets plain text, ignoring HTML, making it safer and faster.
Example:
const div = document.createElement('div');
div.innerHTML = '

Test

'; // Renders as HTML div.textContent = '

Test

'; // Renders as text

JavaScriptEvent PropagationCapturingBubbling
Capturing (trickling) propagates events from the root to the target. Bubbling propagates from the target to the root. Bubbling is the default; capturing is enabled with addEventListener’s capture option.
Example:
document.getElementById('parent').addEventListener('click', () => console.log('Parent'), true); // Capture
document.getElementById('child').addEventListener('click', () => console.log('Child')); // Bubble

JavaScriptObservable PatternReactive State
Implement an observable pattern with a class that stores subscribers and notifies them on state changes, enabling reactive updates.
Example:
class Observable {
  constructor() { this.subscribers = []; }
  subscribe(fn) { this.subscribers.push(fn); }
  notify(data) { this.subscribers.forEach(fn => fn(data)); }
}
const obs = new Observable();
obs.subscribe(data => console.log(data));
obs.notify('Update'); // Update

JavaScriptArray FlatteningAlgorithms
Flatten a nested array using recursion or Array.prototype.flat(). Recursively process each element, concatenating sub-arrays.
Example:
function flatten(arr) {
  return arr.reduce((flat, item) => flat.concat(Array.isArray(item) ? flatten(item) : item), []);
}
console.log(flatten([1, [2, [3, 4]], 5])); // [1, 2, 3, 4, 5]

JavaScriptDebounceThrottleImplementation
Debounce delays execution until a specified time after the last call. Throttle limits execution to once per interval.
Example:
function debounce(fn, delay) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), delay);
  };
}
const log = debounce(console.log, 1000);
log('Test'); // Logs after 1s

JavaScriptImmutabilityMutabilityData Handling
Mutability allows object changes; immutability prevents them. Enforce immutability with Object.freeze, const, or libraries like Immutable.js.
Example:
const obj = Object.freeze({ a: 1 });
obj.a = 2; // Ignored
console.log(obj.a); // 1

JavaScriptDeep CloneImplementation
Implement deep clone by recursively copying nested objects, handling arrays and circular references with a WeakMap. Alternatively, use structuredClone().
Example:
function deepClone(obj, seen = new WeakMap()) {
  if (obj == null || typeof obj !== 'object') return obj;
  if (seen.has(obj)) return seen.get(obj);
  const clone = Array.isArray(obj) ? [] : {};
  seen.set(obj, clone);
  for (let key in obj) clone[key] = deepClone(obj[key], seen);
  return clone;
}
const obj = { a: { b: 1 } };
const clone = deepClone(obj);
obj.a.b = 2;
console.log(clone.a.b); // 1

JavaScriptProject StructureScalability
Structure a codebase with modules, ES modules for imports, and patterns like MVC or component-based architecture. Use linters, TypeScript, and CI/CD for consistency. Document with JSDoc and enforce code reviews.
Example:
// index.js
import { Component } from './component.js';
const app = new Component();
app.render();

JavaScriptChainable APIDesign Patterns
Create a chainable API by returning this from each method, allowing method calls to be chained.
Example:
class Chainable {
  value = 0;
  add(n) { this.value += n; return this; }
  get() { return this.value; }
}
const chain = new Chainable();
console.log(chain.add(1).add(2).get()); // 3