javascript-interview-questions Documentation

List of 1000 JavaScript Interview Questions

RAW Doc

File: package.json

{
"name": "javascript-interview-questions",
"version": "1.0.0",
"description": "List of 1000 JavaScript Interview Questions",
"author": "Sudheer Jonna",
"scripts": {
"gen": "node scripts/toc.mjs"
},
"dependencies": {
"github-slugger": "^2.0.0"
}
}


File: coding-exercise/debounce-function/README.md

Debounce Function

Challenge

Implement a debounce function that delays the execution of a callback until after a specified delay period has elapsed since the last time it was invoked.

Problem Description

Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often. It limits the rate at which a function can fire.

Real-World Use Cases

  • Search Input: Wait for the user to stop typing before making an API call
  • Window Resize: Wait for resize to finish before recalculating layout
  • Scroll Events: Reduce the number of scroll event handlers fired
  • Button Clicks: Prevent multiple form submissions

Example

Input

js
function handleSearch(query) {
    console.log(`Searching for: ${query}`);
}

const debouncedSearch = debounce(handleSearch, 500);

// User types rapidly
debouncedSearch('J');
debouncedSearch('Ja');
debouncedSearch('Jav');
debouncedSearch('JavaScript');

Output

text
// Only executes once after 500ms of the last call
Searching for: JavaScript

Requirements

  1. The debounce function should accept a function and a delay time
  2. It should return a new function that delays invoking the original function
  3. Each new call should reset the delay timer
  4. Only the last call should execute after the delay period
  5. The function should preserve the correct this context and arguments

Key Concepts

  • Closures: Maintaining state (timeoutId) across function calls
  • Higher-Order Functions: Returning a function from a function
  • setTimeout/clearTimeout: Managing asynchronous delays
  • Function Context: Using apply() to preserve this binding

Difference from Throttling

  • Debounce: Executes the function only after the calls have stopped for a specified period
  • Throttle: Executes the function at most once per specified time interval

Benefits

  • Improves performance by reducing unnecessary function calls
  • Reduces API calls and server load
  • Provides better user experience by preventing excessive updates

File: coding-exercise/deep-merge-nested-objects/README.md

Deep-Merge Two Nested Objects (with Circular References)
Challenge:
Implement a deep-merge function that combines two nested JavaScript objects while safely handling circular references. The function must be immutable and able to merge objects, arrays, and nested structures.

Problem Description:
Deep merging means recursively combining the properties of two objects. When both inputs contain nested objects, arrays, or shared references, this becomes more complex.

Your task is to:
Merge all properties from both objects
Recursively merge nested objects
Merge arrays by index
Avoid infinite recursion when circular references exist
Return a new, immutable object
Ensure values from the second object override the first when conflicts occur
Real-World Use Case
State management libraries (Redux, Zustand, Immer)
Complex configuration merging
Deep cloning with overrides
Normalizing API responses
Merging schema definitions

Example:
Input
const a = {
x: 1,
y: { z: 2 }
};
a.self = a; // circular reference

const b = {
y: { k: 20 },
m: 100
};
b.loop = b; // circular reference

const result = deepMerge(a, b);

Output
{
x: 1,
y: { z: 2, k: 20 },
m: 100,
self: [circular],
loop: [circular]
}

Requirements:
Must deep-merge nested objects and arrays
Must detect and safely handle circular references
Must not mutate inputs
When both objects contain the same key:
If both values are objects โ†’ recursively merge
If both are arrays โ†’ merge by index
Otherwise โ†’ value from second object overrides
Must create a brand-new object as the output

Key Concepts:
Recursion
WeakMap to track visited nodes
Object and array cloning
Circular reference detection
Immutability principles


File: coding-exercise/deep-merge-nested-objects/question.md

Exercise: Deep-Merge Two Nested Objects (with Circular References)

Problem Statement

Write a function deepMerge(objA, objB) that deeply merges two JavaScript objects without mutating either of them.
The function must correctly merge nested structures and safely handle circular references.


Rules

  1. If a key exists only in one object โ†’ copy that value.
  2. If a key exists in both objects:
    • If both values are plain objects, recursively merge them.
    • If both values are arrays, merge by index (element-wise).
    • Otherwise, the value from objB overrides the value from objA.
  3. Handle circular references using a tracking mechanism like WeakMap.
  4. Always return a new, immutable object.
  5. Must preserve structure even when circular references appear on both inputs.

Example

js
const a = {
    x: { y: 1 },
    arr: [1, 2]
};
a.self = a; // circular

const b = {
    x: { z: 2 },
    arr: [3],
    extra: true
};
b.loop = b; // circular

const result = deepMerge(a, b);

/*
Expected structure:

{
  x: { y: 1, z: 2 },
  arr: [1, 3],   // merged by index
  extra: true,
  self: <circular>,
  loop: <circular>
}
*/

---

## File: coding-exercise/superArrayOfObjects/README.md

### Count the occurrence of keys and convert the result into array of objects where each object belongs to one key and it's occurrence (count).

#### Example
```js
[
    { language: 'JavaScript' }, { language: 'JavaScript' }, { language: 'TypeScript' }, { language: 'C++' }
]

SHOULD BE CONVERTED TO =

js
[
{ language: 'JavaScript', count: 2 },
{ language: 'C++', count: 1 },
{ language: 'TypeScript', count: 1 }
]

The idea is to count the frequency of each unique key in an array of objects and then instead of making the result look like

js
{ key1: 2, key2: 1, key3: 7 }

The result should be an array of objects so that it can be map over and get rendered in React.JS or something like that.

js
[ { key1: 2 }, { key2: 1 }, { key3: 7 } ]

File: coding-exercise/throttle-function/README.md

Throttle Function

Challenge

Implement a throttle function that limits the rate at which a callback can execute, ensuring it runs at most once per specified time interval.

Problem Description

Throttling is a technique used to control the rate at which a function executes. Unlike debouncing (which delays execution until activity stops), throttling ensures a function executes at regular intervals during continuous activity.

Real-World Use Cases

  • Scroll Events: Update UI elements (like progress bars or lazy-loading) at a controlled rate while scrolling
  • Mouse Movement: Track cursor position without overwhelming the browser with updates
  • Window Resize: Recalculate layout at regular intervals during resize
  • API Rate Limiting: Ensure requests don't exceed API rate limits
  • Game Development: Limit action frequency (e.g., shooting, jumping) to prevent spam
  • Auto-save: Save user input at regular intervals while they're typing
  • Infinite Scroll: Load more content at controlled intervals while scrolling

Example

Input

js
function updateScrollPosition(position) {
    console.log(`Scroll position: ${position}px`);
}

const throttledUpdate = throttle(updateScrollPosition, 1000);

// User scrolls continuously
throttledUpdate(100);  // t=0ms
throttledUpdate(200);  // t=100ms
throttledUpdate(300);  // t=200ms
throttledUpdate(400);  // t=300ms
throttledUpdate(500);  // t=1100ms
throttledUpdate(600);  // t=1200ms

Output

text
// Executes at regular 1000ms intervals
Scroll position: 100px   // t=0ms (immediate)
Scroll position: 500px   // t=1100ms (after 1000ms)
Scroll position: 600px   // t=2200ms (after another 1000ms)

Requirements

  1. The throttle function should accept a function and a time limit
  2. It should return a new function that limits execution rate
  3. The function should execute immediately on the first call (leading edge)
  4. Subsequent calls within the time limit should be controlled
  5. The function should preserve the correct this context and arguments
  6. Should handle both leading and trailing edge execution options

Key Concepts

  • Closures: Maintaining state (lastExecuted, timeoutId) across function calls
  • Higher-Order Functions: Returning a function from a function
  • setTimeout/clearTimeout: Managing asynchronous delays
  • Function Context: Using apply() to preserve this binding
  • Timestamps: Using Date.now() to track execution timing

Implementation Approaches

1. Leading Edge Throttle

Executes immediately on first call, then ignores calls for the limit duration:

js
function throttleLeading(func, limit) {
    let inThrottle = false;
    
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

2. Leading + Trailing Edge Throttle

Executes immediately and also schedules a final execution:

js
function throttle(func, limit) {
    let lastExecuted = 0;
    let timeoutId = null;
    
    return function(...args) {
        const now = Date.now();
        const timeSinceLastExecution = now - lastExecuted;
        
        if (timeSinceLastExecution >= limit) {
            lastExecuted = now;
            func.apply(this, args);
        } else {
            clearTimeout(timeoutId);
            const remainingTime = limit - timeSinceLastExecution;
            timeoutId = setTimeout(() => {
                lastExecuted = Date.now();
                func.apply(this, args);
            }, remainingTime);
        }
    };
}

Throttle vs Debounce

Feature Throttle Debounce
Execution Pattern At regular intervals during activity Only after activity stops
Frequency Guaranteed execution every X ms Single execution after silence
Use Case Continuous updates (scroll, resize) Wait for completion (search input)
Example Update scroll position every 100ms Search API call 500ms after typing stops
Behavior Executes periodically while active Executes once when idle

Visual Comparison

text
User Activity:  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
                โ†“   โ†“   โ†“   โ†“   โ†“   โ†“   โ†“   โ†“

Throttle:       โœ“       โœ“       โœ“       โœ“
                (executes at regular intervals)

Debounce:                                   โœ“
                (executes only after activity stops)

Benefits

  • Performance: Reduces unnecessary function calls during high-frequency events
  • Consistency: Ensures predictable execution intervals
  • User Experience: Provides smooth, responsive updates without lag
  • Resource Management: Prevents overwhelming the browser or server
  • Battery Life: Reduces CPU usage on mobile devices

Common Pitfalls

1. Losing `this` Context

js
// โŒ Wrong: Arrow function loses context
function throttle(func, limit) {
    return () => func(); // 'this' is lost
}

// โœ“ Correct: Use regular function and apply
function throttle(func, limit) {
    return function(...args) {
        func.apply(this, args); // Preserves 'this'
    };
}

2. Not Clearing Previous Timeouts

js
// โŒ Wrong: Multiple timeouts can stack up
function throttle(func, limit) {
    return function() {
        setTimeout(() => func(), limit); // Creates new timeout every call
    };
}

// โœ“ Correct: Clear previous timeout
function throttle(func, limit) {
    let timeoutId;
    return function() {
        clearTimeout(timeoutId); // Clear previous
        timeoutId = setTimeout(() => func(), limit);
    };
}

3. Forgetting to Pass Arguments

js
// โŒ Wrong: Arguments are lost
function throttle(func, limit) {
    return function() {
        func(); // No arguments passed
    };
}

// โœ“ Correct: Collect and pass arguments
function throttle(func, limit) {
    return function(...args) {
        func.apply(this, args); // Pass all arguments
    };
}

Interview Tips

Questions You Might Be Asked

  1. "What's the difference between throttle and debounce?"

    • Throttle: Regular intervals during activity
    • Debounce: Single execution after activity stops
  2. "When would you use throttle over debounce?"

    • Use throttle for continuous updates (scroll, resize, mouse move)
    • Use debounce for completion-based actions (search, form validation)
  3. "How would you implement leading vs trailing edge?"

    • Leading: Execute immediately, ignore subsequent calls
    • Trailing: Schedule execution for end of interval
    • Both: Execute immediately and schedule final call
  4. "What are the performance benefits?"

    • Reduces function calls (e.g., from 1000/sec to 10/sec)
    • Prevents browser lag and jank
    • Reduces API calls and server load
  5. "How do you preserve the this context?"

    • Use regular function (not arrow function)
    • Use func.apply(this, args) to call original function

Code Review Points

  • โœ“ Preserves this context with apply()
  • โœ“ Passes all arguments with rest/spread operators
  • โœ“ Clears previous timeouts to prevent memory leaks
  • โœ“ Uses closures correctly to maintain state
  • โœ“ Handles edge cases (first call, rapid calls, etc.)

Advanced Variations

Throttle with Options

js
function throttle(func, limit, options = {}) {
    const { leading = true, trailing = true } = options;
    let lastExecuted = 0;
    let timeoutId = null;
    
    return function(...args) {
        const now = Date.now();
        
        if (!lastExecuted && !leading) {
            lastExecuted = now;
        }
        
        const remaining = limit - (now - lastExecuted);
        
        if (remaining <= 0) {
            if (timeoutId) {
                clearTimeout(timeoutId);
                timeoutId = null;
            }
            lastExecuted = now;
            func.apply(this, args);
        } else if (!timeoutId && trailing) {
            timeoutId = setTimeout(() => {
                lastExecuted = leading ? Date.now() : 0;
                timeoutId = null;
                func.apply(this, args);
            }, remaining);
        }
    };
}

Throttle with Cancel Method

js
function throttle(func, limit) {
    let timeoutId = null;
    let lastExecuted = 0;
    
    const throttled = function(...args) {
        const now = Date.now();
        const remaining = limit - (now - lastExecuted);
        
        if (remaining <= 0) {
            lastExecuted = now;
            func.apply(this, args);
        }
    };
    
    // Add cancel method
    throttled.cancel = function() {
        clearTimeout(timeoutId);
        timeoutId = null;
        lastExecuted = 0;
    };
    
    return throttled;
}

Testing Considerations

js
// Test basic throttling
const mockFn = jest.fn();
const throttled = throttle(mockFn, 1000);

throttled(); // Should execute
throttled(); // Should be ignored
jest.advanceTimersByTime(1000);
throttled(); // Should execute

expect(mockFn).toHaveBeenCalledTimes(2);
  • Debounce: Delays execution until activity stops
  • Rate Limiting: Restricts number of calls in a time window
  • Request Animation Frame: Browser-optimized throttling for animations
  • Web Workers: Offload heavy computations to background threads

Further Reading


File: .github/FUNDING.yml

github: [sudheerj]
custom: https://buymeacoffee.com/sudheerj


File: .github/workflows/gen-toc.yml

name: Generate table of contents

on:
pull_request:
push:
branches:
- master

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

jobs:
gen-toc:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- name: Install dependencies
run: npm install
- name: Generate table of contents
run: npm run gen
- name: 'Commit changes if required'
run: |
if ! git diff --quiet README.md; then
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "GitHub Actions"
git add README.md
git commit -m "[auto] regenerate table of contents"
git push
echo "[info] Table of contents updated and committed."
else
echo "[info] No changes to table of contents."
fi
working-directory: ${{ github.workspace }}