Top Javascript Interview Questions

JavaScriptMemory ManagementGarbage Collection
JavaScript uses automatic memory management with a garbage collector, typically the Mark-and-Sweep algorithm. It allocates memory for objects and variables and frees it when they are no longer reachable. The garbage collector identifies unreachable objects by marking live objects from roots (e.g., global object) and sweeping unmarked ones.
Example:
let obj = { data: 'test' };
obj = null; // obj is now eligible for garbage collection

JavaScriptIIFEScope
IIFEs are functions defined and executed immediately, often used to create a private scope and avoid polluting the global namespace. They are wrapped in parentheses and invoked with ().
Example:
(function() {
    const x = 10;
    console.log(x); // 10
})();
console.log(typeof x); // undefined

JavaScriptPrototypal InheritanceOOP
Prototypal inheritance allows objects to inherit properties and methods from other objects via their prototype. Each object has a [[Prototype]] link, accessed via __proto__ or Object.getPrototypeOf. When a property is accessed, JavaScript looks up the prototype chain until it finds the property or reaches null.
Example:
const parent = { greet: () => 'Hello' };
const child = Object.create(parent);
console.log(child.greet()); // Hello
console.log(Object.getPrototypeOf(child) === parent); // true

JavaScriptDeep CopyShallow CopyObject Cloning
A shallow copy duplicates only the top-level properties of an object, with nested objects still referencing the original. A deep copy duplicates all levels, creating fully independent copies. Shallow copy can be done with Object.assign or spread operator; deep copy requires structuredClone or custom logic.
Example:
const obj = { a: 1, b: { c: 2 } };
const shallow = { ...obj };
const deep = structuredClone(obj);
obj.b.c = 3;
console.log(shallow.b.c); // 3 (shallow)
console.log(deep.b.c); // 2 (deep)

JavaScriptEvent LoopAsynchronous Programming
The event loop enables asynchronous execution in JavaScript’s single-threaded model. It processes the call stack, then checks the task queue (macrotasks like setTimeout) and microtask queue (promises). Microtasks run before the next macrotask, ensuring non-blocking behavior.
Example:
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('End'); // Start, End, Promise, Timeout

JavaScriptModulesImport/ExportES6
JavaScript modules (ES Modules) allow code to be split into reusable files. Use export to expose variables, functions, or classes, and import to use them in other modules. Modules are executed in strict mode and have their own scope.
Example:
// module.js
export const greet = () => 'Hello';

// main.js
import { greet } from './module.js';
console.log(greet()); // Hello

JavaScriptMicrotasksMacrotasksEvent Loop
Microtasks (e.g., Promise.then, queueMicrotask) are high-priority tasks executed immediately after the current call stack clears. Macrotasks (e.g., setTimeout, setInterval, DOM events) are lower-priority, executed in the next event loop iteration. Microtasks can delay macrotasks.
Example:
setTimeout(() => console.log('Macrotask'), 0);
Promise.resolve().then(() => console.log('Microtask'));
// Microtask, Macrotask

JavaScriptPromise ChainAsynchronous Programming
A promise chain is a sequence of .then() or .catch() calls on a promise, allowing sequential asynchronous operations. Each .then() returns a new promise, enabling chaining. Errors are caught by the nearest .catch().
Example:
Promise.resolve(1)
    .then(x => x * 2)
    .then(x => x + 1)
    .then(result => console.log(result)) // 3
    .catch(err => console.log(err));

JavaScriptDebouncingThrottlingPerformance
Debouncing delays a function’s execution until after a specified time since the last call, useful for events like input typing. Throttling limits execution to once every specified interval, ideal for events like scrolling.
Example:
function debounce(fn, delay) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => fn(...args), delay);
    };
}
const log = debounce(() => console.log('Debounced'), 1000);
log(); log(); // Logs once after 1s

JavaScriptasync/awaitAsynchronous Programming
async/await is syntactic sugar over promises. An async function returns a promise, and await pauses execution until the promise resolves, using the event loop to handle concurrency. Internally, it uses generators and promise resolution.
Example:
async function fetchData() {
    const data = await new Promise(resolve => setTimeout(() => resolve('Data'), 1000));
    console.log(data); // Data
}
fetchData();

JavaScriptCall StackExecution Context
The call stack is a LIFO (Last In, First Out) structure tracking function execution in JavaScript. Each function call adds a frame to the stack; when it returns, the frame is popped. Stack overflow occurs if the stack grows too large (e.g., infinite recursion).
Example:
function a() { b(); }
function b() { console.log('b'); }
a(); // Call stack: a -> b -> (pop b) -> (pop a)

JavaScriptService WorkersWeb APIs
Service workers are JavaScript scripts running in the background, enabling offline capabilities, push notifications, and caching for Progressive Web Apps. They intercept network requests and manage cache with the Cache API.
Example:
// sw.js
self.addEventListener('fetch', event => {
    event.respondWith(caches.match(event.request).then(response => response || fetch(event.request)));
});

// main.js
navigator.serviceWorker.register('/sw.js');

JavaScriptWeakMapWeakSetMemory Management
WeakMap and WeakSet are collections that hold weak references to objects, allowing garbage collection if no other references exist. WeakMap keys are objects, and WeakSet values are objects. They are non-iterable and used for memory management.
Example:
let obj = {};
const weakMap = new WeakMap();
weakMap.set(obj, 'data');
console.log(weakMap.get(obj)); // data
obj = null; // obj is eligible for garbage collection

JavaScriptGeneratorsIterators
Generators are functions that can pause and resume execution using yield, returning an iterator. They are defined with function* and used for lazy evaluation or asynchronous flows.
Example:
function* generator() {
    yield 1;
    yield 2;
    return 3;
}
const gen = generator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

JavaScriptDeep CloneObject Cloning
Implement a deep clone by recursively copying all nested objects, handling arrays, dates, and circular references. Alternatively, use structuredClone() for modern browsers.
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: 1, b: { c: 2 } };
const cloned = deepClone(obj);
obj.b.c = 3;
console.log(cloned.b.c); // 2

JavaScriptFunction CurryingFunctional Programming
Currying transforms a function with multiple arguments into a sequence of single-argument functions. It’s useful for partial application, creating reusable function templates, and functional programming.
Example:
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) return fn(...args);
        return (...nextArgs) => curried(...args, ...nextArgs);
    };
}
const add = curry((a, b) => a + b);
console.log(add(2)(3)); // 5
console.log(add(2, 3)); // 5

JavaScriptMemory LeaksPerformance
Prevent memory leaks by removing event listeners, clearing timers, nullifying references, using WeakMap/WeakSet, and avoiding global variables. Profile with browser dev tools to identify leaks.
Example:
const btn = document.getElementById('btn');
const handler = () => console.log('Clicked');
btn.addEventListener('click', handler);
// Cleanup
btn.removeEventListener('click', handler);

JavaScriptES6Features
Common ES6+ features include arrow functions, let/const, destructuring, spread/rest operators, template literals, promises, async/await, modules, default parameters, and classes.
Example:
const add = (a, b = 0) => a + b;
const { x, y } = { x: 1, y: 2 };
const arr = [...[1, 2], 3];
console.log(add(5), x, arr); // 5, 1, [1, 2, 3]

JavaScriptcallapplybindFunction Methods
.call() invokes a function with a specified this and individual arguments. .apply() is similar but takes arguments as an array. .bind() creates a new function with a fixed this and optional pre-set arguments, without invoking immediately.
Example:
function greet(greeting) { return `${greeting}, ${this.name}`; }
const obj = { name: 'Alice' };
console.log(greet.call(obj, 'Hello')); // Hello, Alice
console.log(greet.apply(obj, ['Hi'])); // Hi, Alice
const bound = greet.bind(obj, 'Hey');
console.log(bound()); // Hey, Alice

JavaScriptMemoizationPerformance
Memoization caches a function’s results for given inputs to avoid redundant computation. Implement it with a cache (e.g., object or Map) to store input-output pairs.
Example:
function memoize(fn) {
    const cache = new Map();
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) return cache.get(key);
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}
const slowFn = x => x * 2;
const memoized = memoize(slowFn);
console.log(memoized(5)); // 10
console.log(memoized(5)); // 10 (cached)

JavaScriptPolyfillsCompatibility
Polyfills are scripts that provide modern JavaScript features in older browsers that lack support. They implement missing APIs like Promise or Array.includes.
Example:
if (!Array.prototype.includes) {
    Array.prototype.includes = function(value) {
        return this.indexOf(value) !== -1;
    };
}
console.log([1, 2, 3].includes(2)); // true

JavaScriptProxyMetaprogramming
A Proxy wraps an object to intercept and customize operations like property access or assignment. It uses a handler with traps (e.g., get, set) for custom behavior, useful for validation or logging.
Example:
const target = { name: 'Alice' };
const proxy = new Proxy(target, {
    get(target, prop) {
        console.log(`Accessing ${prop}`);
        return target[prop];
    }
});
console.log(proxy.name); // Accessing name, Alice

JavaScriptAsync Error HandlingPromisesasync/await
Handle promise errors with .catch() or try/catch in async/await. Ensure all promises in a chain have a .catch() to avoid unhandled rejections. Use try/catch for async functions to manage errors cleanly.
Example:
async function fetchData() {
    try {
        const data = await new Promise((_, reject) => setTimeout(() => reject('Error'), 1000));
        console.log(data);
    } catch (err) {
        console.log(err); // Error
    }
}
fetchData();

JavaScriptSymbolsES6
Symbols are unique, immutable primitives introduced in ES6, often used as object property keys to avoid naming collisions. They are created with Symbol() and can have optional descriptions.
Example:
const sym = Symbol('id');
const obj = { [sym]: 123 };
console.log(obj[sym]); // 123
console.log(Object.keys(obj)); // [] (symbols are not enumerable)

JavaScriptTail Call OptimizationRecursion
Tail call optimization (TCO) allows recursive functions to reuse the same stack frame for tail calls, preventing stack overflow. JavaScript supports TCO in strict mode, but not all engines implement it reliably.
Example:
function factorial(n, acc = 1) {
    'use strict';
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc); // Tail call
}
console.log(factorial(5)); // 120

JavaScriptPerformance OptimizationBrowser
Optimize JavaScript by minimizing DOM operations, using requestAnimationFrame for animations, debouncing/throttling event handlers, avoiding memory leaks, and leveraging lazy loading. Use performance.now() for profiling and bundle with tools like Webpack.
Example:
function expensiveOperation() {
    requestAnimationFrame(() => console.log('Optimized animation'));
}
document.addEventListener('scroll', _.throttle(expensiveOperation, 100));

JavaScriptObject.definePropertyObject Properties
Object.defineProperty defines or modifies a property on an object, allowing control over its value, writability, enumerability, and configurability. It’s used for fine-grained property management.
Example:
const obj = {};
Object.defineProperty(obj, 'name', {
    value: 'Alice',
    writable: false,
    enumerable: true
});
console.log(obj.name); // Alice
obj.name = 'Bob'; // Ignored (not writable)
console.log(obj.name); // Alice

JavaScriptCircular DependenciesModules
Handle circular dependencies by restructuring code to avoid them, using lazy evaluation, or deferring imports. Export functions or objects after their dependencies are resolved to prevent undefined references.
Example:
// a.js
export let a = () => b();
// b.js
import { a } from './a.js';
export let b = () => console.log('b');
a(); // b (works if exports are resolved)

JavaScriptReactivityFrameworks
Reactivity in frameworks like Vue or React enables automatic UI updates when data changes. It’s implemented with proxies (Vue) or state management (React), tracking dependencies and triggering re-renders when observed data mutates.
Example:
// Vue-like reactivity
const data = new Proxy({ count: 0 }, {
    set(target, prop, value) {
        target[prop] = value;
        console.log(`Updated ${prop}: ${value}`);
        return true;
    }
});
data.count = 1; // Updated count: 1

JavaScriptCustom IterableIterators
Implement a custom iterable by defining a [Symbol.iterator] method that returns an iterator object with a next() method, which yields { value, done } objects.
Example:
const myIterable = {
    data: [1, 2, 3],
    [Symbol.iterator]() {
        let index = 0;
        return {
            next: () => {
                if (index < this.data.length) {
                    return { value: this.data[index++], done: false };
                }
                return { done: true };
            }
        };
    }
};
for (let x of myIterable) console.log(x); // 1, 2, 3