JavaScript Skills Test for Hiring (2026): What to Test and Sample Questions

Practical guidance for assessing JavaScript developers, with original questions on the language, async code and the browser.

This JavaScript skills test guide is for recruiters and engineering managers hiring frontend, full-stack or Node.js developers. A useful JavaScript assessment checks reasoning about the language, async code and the browser, not memorized trivia. Here you'll find what to measure by level, 11 sample questions with what a good answer covers, a comparison of test formats and a scoring rubric. The questions don't overlap with our JavaScript interview questions.

What a JavaScript skills test should measure

AreaJuniorMidSenior
Language coreTypes, functions, array methodsClosures, scope, coercion edge casesExplains engine behavior and designs module APIs
Async codePromises and basic async and awaitError flow, cancellation, orderingConcurrency limits and race conditions
DOM and browserSelects and updates elementsEvents, delegation, forms and accessibility basicsMain-thread budget, rendering performance, workers
Data structuresArrays and objectsMap, Set and WeakMap where they fitMemory and caching design
Tooling and qualityRuns a linterTests, bundler basics, reads TypeScriptBuild and type strategy for a team
DebuggingConsole and breakpointsDevTools profiling and the network panelProduction error triage with source maps

For a Node.js backend role, swap the browser row for server topics and see the Node.js skills test. For framework roles, add the React or Angular test after this one.

Sample JavaScript test questions

Each question shows the level and format it suits. Choose five to seven, and include at least one debugging question.

1. What does [10, 9, 1].sort() return, and how do you sort numbers correctly?

Junior, reasoning

What good looks like: it returns [1, 10, 9]. Without a compare function, sort converts elements to strings and compares them as text. Pass (a, b) => a - b for ascending numbers. A good answer notes that sort changes the array in place and returns that same array.

2. Why does ["1", "2", "3"].map(parseInt) return [1, NaN, NaN]?

Junior, reasoning

What good looks like: map calls its callback with the element, the index and the array, so parseInt receives the index as its radix. parseInt("1", 0) treats 0 as "use the default" and returns 1. Radix 1 is invalid, so parseInt("2", 1) is NaN, and "3" isn't a binary digit, so parseInt("3", 2) is NaN. Use map(Number) or map(s => parseInt(s, 10)).

3. Show two ways to give an object private state.

Junior to mid, concept

What good looks like: a closure: function makeCounter() { let count = 0; return { next: () => ++count }; }, where nothing outside can reach count. Or a class private field such as #count, which the language enforces (touching it from outside the class is a SyntaxError). An underscore prefix is only a naming convention.

4. if (isAllowed(user)) always passes, even for blocked users. isAllowed is an async function. Why?

Junior, debugging

What good looks like: an async function always returns a Promise, and every Promise object is truthy. The caller needs await isAllowed(user) inside an async function. Strong candidates mention that TypeScript with the typescript-eslint rule no-misused-promises catches this.

5. A list has 2,000 rows, each with its own click listener. What would you change?

Mid, practical

What good looks like: use event delegation: one listener on the container, with event.target.closest("li") or a data attribute to find the row. It means fewer listeners, and rows added later work without extra wiring. A strong answer notes that some events, like focus, don't bubble (use focusin), and that clickable rows should be real buttons or links for keyboard users.

6. Should a cache keyed by user-supplied strings be a Map or a plain object?

Mid, concept

What good looks like: a Map. On a plain object, keys like "__proto__", "constructor" or "toString" collide with Object.prototype, and assigning obj["__proto__"] changes the prototype instead of storing a value. A Map accepts any key type, keeps insertion order, has size and is built for frequent adds and deletes. If an object is required, Object.create(null) removes the prototype.

7. How do you make your own object work with for...of and the spread operator?

Mid, practical

What good looks like: implement [Symbol.iterator], a method that returns an iterator whose next() gives { value, done }. The simplest version is a generator method, *[Symbol.iterator]() { yield ... }. Then for...of, spread, Array.from and array destructuring all work.

8. Does Object.freeze make an object immutable?

Mid, concept

What good looks like: only shallowly. Top-level properties can't be added, removed or changed, but a nested object or array inside is still mutable. In strict mode (including ES modules and classes) a write to a frozen property throws a TypeError, while in sloppy scripts it fails silently. Deep immutability needs a recursive freeze or a data structure built for it.

9. When do you reach for a WeakMap?

Mid to senior, concept

What good looks like: to attach data to objects you don't own, such as DOM nodes or library objects, without keeping them alive. When a key object is garbage collected, its entry goes with it. Keys must be objects (or non-registered symbols), and a WeakMap can't be iterated and has no size, by design. A plain Map keyed by DOM nodes keeps removed nodes in memory.

10. A CSV import freezes the page for 4 seconds while it parses. What are your options?

Senior, practical scenario

What good looks like: the parse runs on the main thread, which blocks input and rendering. Move it into a Web Worker and send results back with postMessage (transfer an ArrayBuffer instead of copying it), or split the work into chunks and yield to the browser between them. Confirm the fix with long tasks in the Performance panel.

11. Upload 100 files with no more than 5 in flight at once. Sketch the approach.

Senior, practical

What good looks like: start 5 workers that each take the next file from a shared queue, await its upload, and loop until the queue is empty, then await Promise.all(workers). Record failures per file instead of aborting the batch, and support cancel with an AbortController. A strong candidate explains why Promise.all(files.map(upload)) starts all 100 at once.

Test formats compared

FormatWhat it showsWeaknessBest for
Multiple choice quizRecall of language rules and output predictionEncourages trivia; answers are easy to searchShort filters for junior roles
Take-home projectStructure, tests and UI polish on a small appLong; heavy AI help is hard to spotMid frontend roles, kept to a few hours
Live codingReasoning, debugging and communicationStressful; costs engineer timeSenior and full-stack roles
Work sampleReal tasks: fix a bug in a small repo, make a slow page fasterNeeds maintenance as tools changeTeams with clear frontend or Node.js work
Adaptive AI interviewDepth through follow-ups and difficulty that moves with each answerNot a full app build; check topic coverageScreening many applicants consistently

If your test is unproctored, plan for AI-assisted answers. Our guide on how to detect AI interview cheating covers practical checks, and a short live follow-up on the candidate's own code is one of the best.

Scoring rubric

ScoreWhat you see
1Can't predict basic behavior like sort order or truthiness. Guesses instead of reasoning.
2Gets the answer with prompting but can't explain the cause or the fix.
3Reasons correctly about the language, async code and the browser, and picks sensible fixes.
4Adds performance, memory and accessibility angles unprompted, and explains clearly.

Running a JavaScript test with NTRVSTA

JavaScript is part of NTRVSTA's exam library. The live AI interviewer adapts in real time: when a candidate explains the parseInt trap with confidence, it moves on to async control or main-thread performance, and when they stall, it steps down or offers a hint. It follows up on reasoning, so a lucky guess on an output question doesn't score like a real explanation. For senior full-stack roles, add a system-design question on the interactive whiteboard.

Your team gets a score breakdown (technical skills, English proficiency, communication), video snippets and a shareable candidate report. Use our JavaScript interview questions for the next round, and compare code-execution platforms in the best coding assessment tools.

Questions

How long should a JavaScript skills test be?

Thirty to 45 minutes is enough for a first round. Keep any take-home to a few hours, and don't ask candidates to build a full app from scratch.

Are trick questions like [] + {} worth asking?

Rarely. Pure coercion puzzles reward memorization. Questions like the parseInt one are better because the candidate has to reason about how a real API is called, which is a skill they use every day.

What should a junior JavaScript test leave out?

Skip memory profiling, worker threads and build tooling. Focus on core language behavior, simple async code, DOM events and reading an error message, then use follow-ups to see how they learn.

See how an adaptive JavaScript screen works in practice. Book a 20-minute demo, or run a sample interview.

NTRVSTA
‍