Interview question01 / 100
Practice session
JavaScript
One hundred ECMAScript-focused questions: fundamentals, asynchronous programming, objects and modules, and runtime semantics — forty beginner, forty mid-level, and twenty expert.
100 curated questions10 coding exercisesQuestions tab: swipe cards or keyboard
Practice for JavaScript
Progress
1 / 100
Arrow keys to move · Space or Enter to revealOn touch: swipe the card right or left
Hands-on
Coding exercises
Read the scenario, sketch your solution in the editor, reveal hints only when you need them, then compare against our reference implementation. Exercises run from beginner through mid-level.
In this track
10
scenarios
Immutable shopping cart
Scenario
You are building checkout logic for a storefront. The cart is modeled as an array of line items: each item has a string id, display name, unit price (number), and quantity (positive integer).
Write small pure functions (no classes required) that:
• addItem(cart, item) — if the id already exists, increase qty by the incoming item's qty (default 1); otherwise append a new line.
• removeItem(cart, id) — drop that line entirely.
• getTotal(cart) — sum of price * qty for all lines.
Prefer immutable updates (return new arrays/objects) so React or other callers can detect changes cheaply. Do not mutate the input cart argument.
- Use plain modern JavaScript (ES2015+).
- Treat cart as read-only input; return new structures.
- Assume price and qty are valid numbers when grading your own solution.
Filter a todo list
Scenario
You have an in-memory list of todos: each item is `{ id: string, title: string, done: boolean }`.
Implement `filterTodos(todos, filter)` where `filter` is one of `"all"`, `"active"` (not done), or `"completed"` (done). Return a new array; do not mutate the input.
If `filter` is anything else, return a shallow copy of the original array so the UI does not break.
- Use Array.prototype.filter where appropriate.
- Return a new array reference even for the "all" case.
Safe nested property read
Scenario
APIs and config objects are often nested. Implement `getAt(object, path, defaultValue)` that returns the value at a path or `defaultValue` when any segment is missing.
`path` may be a dot-separated string like `"user.address.city"` or an array of keys like `["user", "address", "city"]`. Empty path should return the object itself (or default if object is null/undefined—your choice, but document mentally: returning the root is typical).
Do not throw; treat `null` or non-object values in the middle of the walk as "missing" and return `defaultValue`.
- No eval or Function constructor.
- Ignore empty segments in dot paths (e.g. "a..b" treats like "a.b").
Slugify a display string
Scenario
Blog posts and product names need URL-safe slugs. Implement `slugify(text)` that:
• Trims leading/trailing whitespace.
• Lowercases the string.
• Replaces any run of non-alphanumeric characters with a single hyphen `-`.
• Removes leading/trailing hyphens from the result.
If the result would be empty (e.g. input was `"***"`), return `"item"` as a fallback.
- Use String and RegExp methods only—no slug libraries.
- Assume typical Unicode letters count as non-alphanumeric for this exercise unless you want to allow them; ASCII letters and digits are enough for tests.
Group array items by a field
Scenario
Given an array of objects that share a property name (for example `department`), implement `groupByKey(items, key)` that returns a plain object whose keys are the distinct values of `item[key]` and whose values are arrays of items sharing that key.
Use only values that exist on the objects; if some items lack the key, you may group them under `"undefined"` as a string key, or skip them—pick one approach and stay consistent (reference solution uses String(value) so `undefined` becomes `"undefined"`).
Do not mutate the input array or its items.
- Return a new object and new inner arrays.
- Key values should be coerced to strings for object keys (e.g. numbers become "1").
Resilient fetch + memoized loader
Scenario
Your app loads user profiles from a REST API. Network flakes are common on mobile, so callers want automatic retries with a small delay between attempts.
Implement fetchWithRetry(url, options) where options may include maxRetries (default 3), delayMs (base delay, default 400), and fetchImpl (default global fetch). On each failure, wait longer before the next try (linear backoff: delayMs * attemptIndex is fine). If all attempts fail, rethrow the last error.
Also implement createUserCache(fetchUser) returning an async function loadUser(id) that calls fetchUser(id) at most once per id and caches fulfilled results in a Map. If fetchUser rejects, do not cache the failure so a later call can retry.
- Use async/await; avoid third-party retry libraries.
- fetchWithRetry should return the Response object on success.
- The cache should only store successful resolutions.
Debounce a function
Scenario
Search inputs and resize handlers should not fire on every keystroke or pixel. Implement `debounce(fn, waitMs)` returning a new function that waits until `waitMs` milliseconds have passed without further calls before invoking `fn` with the **last** arguments received.
If the debounced function is called again before the timer fires, reset the timer. Use `setTimeout` / `clearTimeout`; no lodash.
- Preserve `this` if you like by using a regular function for the wrapper, or document that callers should use arrow functions at bind sites.
- The returned function should accept any number of arguments.
Concurrent async map with a limit
Scenario
You have a list of IDs and an async mapper `async (id) => result`. Running hundreds of network calls at once can overwhelm the server. Implement `mapLimit(items, limit, mapper)` that returns a Promise resolving to an array of results **in the same order as `items`**, while having at most `limit` mapper calls in flight at any time.
When `limit` is greater than the list length, only spawn as many workers as needed. Empty input should resolve to `[]`.
- Use async/await and Promise—no external pool libraries.
- Preserve result order even though tasks finish out of order.
Parse and build query strings
Scenario
Implement `parseQuery(search)` that takes a string like `"?a=1&b=two"` or `"a=1&b=two"` and returns a plain object with string keys and **string** values (use `URLSearchParams`).
Implement `stringifyQuery(params)` that takes an object whose values are strings (or numbers coerced with String) and returns a query string **without** a leading `?`, suitable for appending to a URL yourself.
If the same key appears twice in the input, last value wins (match URLSearchParams behavior).
- Use the built-in URLSearchParams API.
- Do not add a ? prefix in stringifyQuery output.
Tiny synchronous event emitter
Scenario
Build a factory `createEmitter()` returning an object with:
• `on(eventName, handler)` — register a function for string `eventName`. Return an `unsubscribe` function that removes that handler.
• `off(eventName, handler)` — remove one handler (no-op if not registered).
• `emit(eventName, payload)` — call all handlers for that event synchronously, in registration order.
If a handler throws, let the error propagate (no try/catch required). Unsubscribing during emit should not skip handlers that were already scheduled for this emit—snapshot the handler list before looping.
- Handlers are synchronous; emit does not return a Promise.
- Use Map<string, Set<function>> or similar for storage.