Skip to content
Skilled.dev

Language preparation

JavaScript coding interview preparation

Practice the data structures, language behavior, asynchronous utilities, and testing habits that support clear JavaScript interview solutions.

Published · 7 min read

Prepare for a JavaScript coding interview by practicing two kinds of work: solving a problem and explaining the JavaScript behavior your solution depends on. A correct graph traversal can fail because of a queue implementation mistake. A debounce can look plausible while losing its receiver or firing twice for a single event.

Use this guide to build a small set of reliable implementation habits. The course supports several languages, but choosing JavaScript should be a deliberate choice you can explain and use comfortably under time pressure. If you already solve problems in TypeScript, the TypeScript preparation guide adds the type decisions that matter without changing the underlying algorithms.

Build a practical data structure toolkit

Be comfortable with arrays, Map, and Set before memorizing more patterns. Use an array for indexed sequences and a stack with push and pop. Use a map when keys and associated values both matter, such as a value-to-index lookup or a frequency count. Use a set when you need membership without a separate value.

A plain object can be useful for a constrained record, but an arbitrary input string used as a property name deserves care. Map avoids inherited object properties and makes key membership explicit. It also distinguishes keys by its own equality rules; two separate object values are separate keys even when their fields look identical.

For a queue, a head index is a simple interview implementation. Repeatedly shifting a long array can add element-moving work in common array models. Keep a head pointer, append newly discovered items, and process while the head remains below the array length. The BFS guide includes a complete example.

Do not assume that a priority queue is a built-in general-purpose JavaScript collection. If the interview environment supplies one, learn its API. Otherwise, prepare a heap implementation or discuss an alternative whose complexity you can state accurately.

Practice numeric sorting and mutation

A default array sort compares values after string conversion. Numeric problems normally need an explicit comparator such as (a, b) => a - b. Sorting [2, 10, 3] without that comparator does not produce numeric order.

Also remember that sort changes the array it is called on. If the caller's input must be preserved, make an appropriate copy first and include its memory cost in the analysis. The sorting algorithm and its resource usage depend on the implementation; do not claim a specific engine's behavior as a language-wide time or space guarantee. See the Array.sort reference for the API contract.

Stable ordering, comparator consistency, and equal values can matter when sorting structured records. A comparator should express the requested order and return zero for equivalent entries under that order. Returning only a boolean does not correctly represent all three comparison outcomes.

Use presence checks that preserve valid values

Index zero, numeric zero, false, and an empty string can all be valid data. A truthiness check does not establish whether a map key is absent.

function firstRepeatedIndex(values) {
  const firstIndex = new Map();
  for (let i = 0; i < values.length; i += 1) {
    if (firstIndex.has(values[i])) return firstIndex.get(values[i]);
    firstIndex.set(values[i], i);
  }
  return null;
}

For [8, 3, 8], this returns zero. A version that checked if (firstIndex.get(values[i])) would miss that earlier entry. This error appears in caches and dynamic-programming tables too, where a computed answer of zero must remain distinguishable from “not computed.”

Choose a return convention and keep it explicit. If null means no answer, a successful index zero is easy to distinguish. If undefined can itself be a stored value, use has for presence rather than relying on get alone.

Know the string and number model

JavaScript string indexing exposes UTF-16 code units. Iteration with for...of or spread operates on code points. Neither automatically treats every displayed character as one unit. An ASCII interview problem can avoid much of that complexity by stating its alphabet; a general text problem needs a more careful contract.

String concatenation and slicing may allocate or copy data depending on the operation and implementation. A loop that repeatedly constructs a growing result deserves analysis beyond counting loop iterations. For large output, collecting pieces and joining them can be easier to reason about, but measure or qualify runtime-specific claims.

For numbers, confirm whether the input consists of finite safely representable integers. JavaScript's Number does not exactly represent every possible integer magnitude. BigInt provides integer arithmetic with a different type and operation costs, and mixing it casually with Number arithmetic is an error. Do not switch numeric models halfway through a solution without revisiting inputs, comparisons, and output serialization.

Separate general algorithms from JavaScript utilities

Array search, tree traversal, graph traversal, and dynamic programming are general algorithm skills. Debounce, throttle, promise aggregation, array-method behavior, and event emitters also exercise JavaScript-specific contracts.

For utilities, start from observable behavior. A debounce needs a decision about leading/trailing calls and cancellation. A promise aggregator needs input-order preservation, rejection handling, and an empty-input result. An event emitter needs rules for duplicate subscriptions, listener removal, and changes made while publication is in progress.

Those details produce better tests than a vague claim that the implementation “works like the native version.” Use debounce versus throttle, Promise.all, and the event-loop guide to practice behavior you can trace with specific examples.

Make recursion and mutation visible

Recursive code uses the call stack. A chain-shaped tree or deep graph can exceed a runtime's recursion limit even when the asymptotic algorithm is correct. Learn an explicit-stack version of traversal and explain when it is appropriate.

When backtracking, restore every shared-state change before trying the next branch. Push a choice, recurse, then pop it. If you save a result that should remain independent, copy the current path at the right point; saving the same mutable array reference repeatedly does not preserve different paths.

For in-place algorithms, explain which part of the array is finalized and which part is unread. For linked structures, preserve the next reference before redirecting a link. Naming that state is more useful than narrating syntax.

Function calls and this

A regular method called as counter.increment() receives counter as its receiver. Saving that method in a variable and calling increment() loses that receiver. A strict-mode regular function then receives undefined as this; a non-strict function can substitute the global object. Passing the method to an API delegates the call to that API, so inspect how the callback will be invoked. MDN's this reference explains the call and binding rules.

Bind a method when the callback must keep a particular receiver: counter.increment.bind(counter) creates a bound function. Alternatively, an arrow callback such as () => counter.increment() makes the intended method call explicitly. Keep the same callback reference if an unsubscribe operation will need it later.

An arrow function captures the enclosing this instead of receiving a new one from its call site. Replacing every method with an arrow can therefore change behavior you meant to preserve. For Fix 'this', identify the caller, receiver, and callback boundary before choosing a repair. Test both a direct method call and the callback path.

Practice with a repeatable sequence

Start with arrays, hash tables, and strings. Solve Two Sum with a map and explain why the lookup happens before insertion. Follow with bracket validation, linked-list reversal, and a binary-tree traversal. Then move into graphs and dynamic programming.

Alongside that sequence, implement one JavaScript utility at a time. Test it with a small deterministic input trace before adding options. A single carefully tested debounce teaches more than copying several unexamined variations.

If you have a month, use the 30-day plan. If the fundamentals are already familiar and the interview is close, use the seven-day review. Reduce the number of new topics when necessary, while preserving time to revisit and explain earlier solutions.

Check readiness with a timed rehearsal

Choose a problem you have not just read. Clarify the contract, describe a baseline, implement a solution, and test the boundaries while explaining your decisions. Record which mistakes came from the algorithm and which came from language behavior.

Review whether you can use a map without losing index zero, sort numbers correctly, preserve or disclose mutation, and analyze the actual structures your code allocates. For an asynchronous utility, check the callback order and error path as carefully as the successful case.

Turn each failed case into a small regression test. On the next rehearsal, solve the problem again without the old implementation open. That gives you evidence of what you can reproduce when the code is no longer in short-term memory.

Worked lesson walkthroughs

66 reading lessons with code examples in this language.

Concepts

Concept lesson

Arrays in JavaScript

Learn array insertion in JavaScript. Trace a copied result, explain shifting costs, and check empty inputs and endpoint positions without changing the original.

Concept lesson

Big O time and space complexity in JavaScript

Analyze time and space complexity in JavaScript with a suffix-copy example. Count nested work, returned storage, and allocation instead of counting loops alone.

Concept lesson

Hash Tables in JavaScript

Count frequencies with a hash map in JavaScript. Preserve input order, distinguish missing keys from zero, and explain expected lookup time and retained storage.

Concept lesson

Strings in JavaScript

Practice string construction in JavaScript with a repeat-text example. Explain builders, copies, Unicode representation, output size, and boundary cases.

Concept lesson

Palindromes, permutations, and anagrams in JavaScript

Check an ASCII-normalized palindrome in JavaScript. Trace two-pointer comparisons, case handling, ignored characters, and the limits of the text contract.

Concept lesson

Recursion in JavaScript

Trace recursive factorial in JavaScript from the base case through returning calls. Explain termination, stack space, integer limits, and boundary tests.

Concept lesson

Logarithmic time in JavaScript: repeated halving and search

Understand logarithmic loops in JavaScript by counting integer halvings. Test powers of two, explain rounding, and derive the exact step count.

Concept lesson

Binary search in JavaScript: invariants and boundary tests

Write first-occurrence binary search in JavaScript. Trace half-open bounds, duplicate values, missing targets, and the random-access assumption behind O(log n).

Concept lesson

Sorting in JavaScript

Return a numerically sorted copy in JavaScript. Preserve duplicates and the original input, choose the right library operation, and account for copied storage.

Concept lesson

Linked Lists in JavaScript

Find a linked list's middle in JavaScript with slow and fast runners. Define the even-length result, handle an empty list, and separate construction from traversal.

Concept lesson

Pointers in JavaScript

Trace shared mutation and reference rebinding in JavaScript. Follow two aliases through one object update and a new allocation, with explicit ownership notes.

Concept lesson

Queues in JavaScript

Practice FIFO queue behavior in JavaScript. Compare efficient front removal with a head cursor, preserve repeated values, and count fill-and-drain costs.

Concept lesson

Stacks in JavaScript

Practice LIFO stack behavior in JavaScript. Trace push and pop, separate empty state from zero payloads, and explain node ownership and end-operation costs.

Concept lesson

Trees in JavaScript

Compute binary-tree edge height in JavaScript. Read heap-indexed nullable slots, handle an empty root, and account for reachable nodes and recursion depth.

Concept lesson

Tree traversal in JavaScript: in-order, pre-order, and post-order

Trace in-order binary-tree traversal in JavaScript. Explain nullable child positions, visit order, recursion space, and when the output is actually sorted.

Concept lesson

Binary Search Trees in JavaScript

Search a binary search tree in JavaScript. Follow one ordered path, handle absent children, and relate search cost to height without assuming balance.

Concept lesson

Graphs in JavaScript

Read graph adjacency lists in JavaScript and compute out-degrees. Define vertex IDs, parallel edges, self-loops, representation costs, and input assumptions.

Concept lesson

Depth-First Search in JavaScript

Implement iterative DFS in JavaScript. Preserve listed neighbor order, skip repeated visits, trace a shared descendant, and explain pending-stack costs.

Concept lesson

Breadth-First Search in JavaScript

Implement BFS in JavaScript with FIFO order and enqueue-time discovery. Trace graph layers, cycles, shared descendants, and visited-storage costs.

Concept lesson

Subproblems and Overlapping Subproblems in JavaScript

Count naive Fibonacci calls in JavaScript. Trace overlapping states, include base-case work, and identify the cache key before applying memoization.

Concept lesson

Memoization in JavaScript

Memoize Fibonacci in JavaScript and count distinct computed states. Preserve cached zero, define cache lifetime, and explain remaining recursion depth.

Concept lesson

Bottom-Up vs. Top-Down in JavaScript

Build a bottom-up Fibonacci table in JavaScript. Follow dependency order, test zero input, and compare full-table storage with rolling-state alternatives.

Concept lesson

Dynamic Programming in JavaScript

Solve minimum coin count in JavaScript. Trace the recurrence, represent unreachable states, test a greedy counterexample, and explain amount-based table costs.

Data structure implementations

Implementation tutorial

Implement a dynamic array in JavaScript

Implement a map-backed teaching array in JavaScript. Trace push, pop, insertion, and removal, including logical length, missing indices, and obsolete entries.

Implementation tutorial

Implement a hash table in JavaScript: collisions, updates, and removal

Build a hash table in JavaScript with separate chaining. Follow colliding keys through lookup, update, and deletion, and explain the limits of a fixed teaching hash.

Implementation tutorial

Implement a linked list in JavaScript: head, tail, and node identity

Build a doubly linked list in JavaScript. Trace prepend and append, verify reciprocal links and both endpoints, and explain ownership and retention.

Implementation tutorial

Implement a queue in JavaScript: FIFO links and empty transitions

Implement a linked queue in JavaScript. Trace FIFO order, the last removal, and refilling an empty queue while preserving first and last endpoint invariants.

Implementation tutorial

Implement a stack in JavaScript: push, pop, and ownership

Implement a linked stack in JavaScript. Follow the top link through push, peek, and pop, including empty state, returned nodes, and retained chains.

Implementation tutorial

Implement a binary search tree in JavaScript: insert and search

Build a binary search tree in JavaScript. Trace insertion and search, preserve duplicate keys on the right, and test skewed shapes without assuming balancing.

Implementation tutorial

Implement a graph in JavaScript: adjacency lists and edge removal

Build an adjacency-list graph in JavaScript. Trace directed and undirected edges, self-loops, parallel entries, and removal of incoming and outgoing edges.

Practice problems

Practice walkthrough

Two Sum in JavaScript: return every matching index pair

Solve the all-pairs Two Sum exercise with a JavaScript Map. Trace complement lookups, handle index zero, preserve indexes, and test numeric assumptions.

Practice walkthrough

Valid brackets in JavaScript: a stack that ignores ordinary text

Validate nested parentheses, brackets, and braces with a JavaScript array stack. Trace mismatched closers, ignore other characters, and test nesting and empty input.

Practice walkthrough

Product of array except self in JavaScript without division

Build the product array with prefix and suffix passes in JavaScript. Trace the output in place, handle zeros, avoid fill aliasing mistakes, and explain numeric limits.

Practice walkthrough

Counting sort for ages in JavaScript: worked solution

Sort bounded ages with a frequency table, preserving duplicate values. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Best stock profit in JavaScript: one transaction

Find the best single chronological stock trade with one scan. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Most frequent email in JavaScript: hash-map solution

Count email occurrences and return the unique most frequent address. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

First Bad Version in JavaScript: boundary binary search

Find the first true value in a monotone version predicate. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Reverse a linked list in JavaScript: pointer walkthrough

Reverse a linked list by moving nodes from an untouched suffix to a reversed prefix. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Remove kth node from end in JavaScript: zero-based deletion

Delete the zero-based kth node from the end of a linked list. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge sorted linked lists in JavaScript: Combine Blockchains

Merge two sorted linked chains while preserving duplicate timestamps. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count Islands in JavaScript: DFS flood-fill walkthrough

Count side-connected land components with a destructive flood fill of the working grid. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate a binary search tree in JavaScript: inherited bounds

Validate every binary-tree node against bounds inherited from all its ancestors. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Binary tree maximum depth in JavaScript: count edges

Measure a binary tree’s longest root-to-leaf path in edges. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Lowest common ancestor in JavaScript: return the original node

Find the lowest common ancestor in a general binary tree and return the original node. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Generate string permutations in JavaScript: positional results

Generate positional string permutations, retaining repeated results for repeated characters. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Group palindrome permutations in JavaScript: counts and keys

Filter palindrome permutations, then group retained strings by character multiset. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge intervals in JavaScript: Interview Scheduler

Merge sorted interval coverage with an explicit touching-endpoint rule. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Min-max stack in JavaScript: constant-time extrema

Support stack extrema through histories that retain duplicate record values. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

BFS shortest path in JavaScript: Pilot a Rocket

Find an unweighted shortest route and reconstruct it from predecessor links. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Maze reachability in JavaScript: memoized down/right search

Memoize directed maze reachability while keeping unknown distinct from false. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a rotated sorted array in JavaScript: Shifted Search

Search a distinct rotated sorted array by identifying an ordered half. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a sorted matrix in JavaScript: staircase walkthrough

Eliminate a row or column at each step in a row-and-column sorted matrix. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

House Robber in JavaScript: maximum nonadjacent sum

Maximize a nonadjacent selection with two rolling prefix optima. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Sum decimal digits in JavaScript: recursive solution

Sum decimal digits through a decreasing recursive integer decomposition. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count 1, 2, or 3-step paths in JavaScript: Three Moves

Count ordered one-, two-, and three-step routes with rolling dynamic programming. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Coin change in JavaScript: minimum-coin dynamic programming

Minimize unlimited coin usage with a bottom-up amount table. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate Sudoku in JavaScript: rows, columns, and subsquares

Validate filled Sudoku entries against rows, columns, and subsquares. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Spiral matrix traversal in JavaScript: Walk a Matrix

Traverse a rectangle in clockwise spiral order using shrinking boundaries. Follow the JavaScript implementation, its correctness argument, worked example, complexity, and boundary tests.

Language behavior and callbacks

Language behavior

JavaScript runtime interview concepts for JavaScript

Practice runtime validation in JavaScript. Narrow an unknown score, reject coercion and nonfinite values, and distinguish executable checks from static types.

Language behavior

JavaScript interview questions: scope, identity, and runtime behavior

Practice JavaScript scope, identity, and equality questions with an executable quiz. Explain closure bindings, object mutation, reassignment, and NaN comparisons.

Language behavior

Fix this and closure bugs in JavaScript

Fix receiver and closure bugs in JavaScript. Bind a presenter callback, preserve per-iteration seat positions, and test what changes when captured objects mutate.

Language behavior

JavaScript array methods: map, filter, find, some, and every

Practice map, filter, find, includes, some, and every in JavaScript. Compare result shapes, empty-array behavior, short-circuiting, and input preservation.

Language behavior

Build a teaching promise in JavaScript: callbacks and scheduling

Build a teaching promise in JavaScript. Store one settlement, schedule early and late observers, catch executor errors, and distinguish it from native chaining.

Language behavior

Build a publish-subscribe dispatcher in JavaScript

Build a publish-subscribe dispatcher in JavaScript. Define duplicate subscriptions, disposal, snapshot delivery, callback errors, and retained-listener costs.

Language behavior

Implement debounce in JavaScript: latest calls, cancel, and flush

Implement trailing debounce in JavaScript with cancel and flush. Preserve the latest arguments and receiver, trace deadlines, and handle callback reentry.

Language behavior

Implement throttle in JavaScript: leading and trailing calls

Implement leading-and-trailing throttle in JavaScript. Keep fixed cooldown windows, deliver the latest pending call, preserve the receiver, and cancel safely.