A junior JavaScript interview isn't a vocabulary test. It's 'here's a snippet, tell me what it prints and why.' Here are the questions that actually come up, attempt each one, then reveal a full answer.
NK
Nicanor Korir
Author
July 15, 2026
14 min read
JavaScriptInterviewBeginnerCareerFrontendWeb Development
I've sat on both sides of the junior JavaScript interview, sweating through them early in my career, and now running them. And the single biggest misconception I see is this: candidates prepare like it's a vocabulary test. They memorise "a closure is a function inside a function" and hope the interviewer asks for the definition.
They almost never do.
What actually happens is: the interviewer pastes a five-line snippet and says "what does this print, and why?" They're not checking whether you memorised a sentence. They're watching how you reason about the language out loud, can you trace scope, predict coercion, and explain the async model without hand-waving.
This article is built to train exactly that. Below are the questions that genuinely come up in junior interviews, grouped by topic. Each one is an interactive card: read it, try to answer out loud first, then reveal the full answer. That "attempt before you peek" habit is the whole point, it's the difference between recognising an answer and being able to produce one under pressure.
When you're ready to drill these like flashcards against a timer, the interactive practice hub turns this whole series into an exam room.
What a junior JavaScript interview is really testing
Before the questions, the frame. A beginner round, usually a 30-45 minute call with a recruiter or an engineer, is screening for four things:
Core language fundamentals, types, coercion, scope, hoisting.
Whether you can predict what a snippet prints and explain why, not just what.
Comfort with everyday tools, array methods, the DOM, events, Promises.
Communication, thinking out loud, and admitting what you don't know cleanly instead of bluffing.
Two habits separate strong juniors from the rest: they narrate their reasoning instead of guessing one word and going quiet, and when they hit something they don't know, they say "I'm not sure, here's how I'd find out," which reads far better than a confident wrong answer.
Types & coercion
This is where the classic "gotcha" snippets live. Interviewers love them because coercion rules are where guessing falls apart and real understanding shows.
Q1
Types★★★Phone screen
What's the difference between null and undefined? And what does typeof null return?
#null
#undefined
#typeof
undefined means 'this has not been assigned a value', it's what you get from an uninitialised variable, a missing object property, a missing function argument, or a function with no return. null is an intentional 'no value here', something you assign deliberately to say 'empty on purpose'.
typeof undefined is 'undefined', but typeof null is 'object', a famous, un-fixable bug preserved for backwards compatibility. To check for null specifically you compare value === null.
Follow-ups they may ask
How would you check a value is either null or undefined in one go?
Q2
Operators & coercion★★★Phone screen
What's the difference between == and ===? What will this log?
JavaScript
javascript
1
2
3
4
5
#equality
#coercion
=== is strict equality: no type conversion, different types are never equal. == is loose equality: it coerces the operands toward a common type first, which produces famously non-transitive results.
The output:
0 == '' → true ('' coerces to 0)
0 == '0' → true ('0' coerces to 0)
'' == '0' → false (both strings, compared as-is)
null == undefined → true (a special case in the spec)
null === undefined → false (different types)
That first trio, 0 == '' and 0 == '0' are true but '' == '0' is false, is why the rule is: always use `===`, and treat == null as the one deliberate exception (it matches both null and undefined).
Follow-ups they may ask
When is `== null` actually useful?
Q3
Operators & coercion★★★Phone screen
Which values are falsy in JavaScript? And what's the difference between || and ?? for defaults?
#truthy
#falsy
#short-circuit
There are exactly eight falsy values: false, 0, -0, 0n (BigInt zero), '', null, undefined, and NaN. Everything else, including '0', 'false', [], and {}, is truthy.
|| returns the right side when the left is falsy, so count || 10 replaces 0 with 10, often a bug. ?? (nullish coalescing) only falls through when the left is null or undefined, so count ?? 10 keeps a legitimate 0. Use ?? when 0, '', or false are valid values you want to preserve.
Follow-ups they may ask
What does `??=` do?
Q4
Operators & coercion★★★Live coding
What does each line print?
JavaScript
javascript
1
2
3
4
5
#coercion
#operators
1 + '2' → '12'. + with a string means concatenation, so 1 becomes '1'.
'5' - 1 → 4. - has no string meaning, so '5' is coerced to the number 5.
'5' + + '5' → '55'. The unary + turns the second '5' into number 5, then + concatenates '5' and 5 → '55'.
[] + [] → ''. Arrays coerce to strings for +; an empty array becomes ''.
typeof NaN → 'number'. NaN is a numeric value meaning 'not a valid number', and it's the only value not equal to itself.
The lesson: + is overloaded (concat vs add), while the other arithmetic operators always coerce to number.
Follow-ups they may ask
How do you reliably check if a value is `NaN`?
Scope, hoisting & declarations
If types are the most common gotcha, scope is the most common concept. Nearly every junior interview probes whether you understand var vs let/const and what hoisting really does.
Q5
Scope & declarations★★★Phone screen
What's the difference between var, let, and const? When would you reach for each?
#var
#let
#const
#scope
Three axes: scope, re-assignment, and hoisting.
var is function-scoped and hoisted, initialised to undefined before its line runs. It can be re-declared and re-assigned, and it leaks out of blocks.
let is block-scoped, can be re-assigned but not re-declared in the same scope, and lives in the temporal dead zone until its declaration, reading it early throws.
const is block-scoped like let but can't be re-assigned. Note it doesn't freeze the value: a const object's properties can still change.
In modern code I default to const, switch to let only when I genuinely re-assign, and avoid var entirely, its function scoping causes the classic loop bugs.
Follow-ups they may ask
Does `const` make an object immutable? How would you actually freeze one?
What is the temporal dead zone?
Q6
Hoisting★★★Live coding
What does this print, and why?
JavaScript
javascript
1
2
3
4
5
6
7
8
#hoisting
#scope
First line prints undefined. var a is hoisted, the declaration moves to the top of the scope, but the assignment (= 1) stays put, so a exists but is undefined when logged.
foo() prints 'hi'. Function declarations are hoisted whole, body and all, so you can call them before they appear.
The console.log(b) line throws a ReferenceError. let and const are hoisted too, but they sit in the temporal dead zone until their declaration executes, so accessing b early is an error, not undefined. That TDZ is a feature, it turns a silent bug into a loud one.
Follow-ups they may ask
Are function *expressions* hoisted the same way as declarations?
Q7
Closures★★★Technical screen
What is a closure? Give a simple, practical example of why you'd want one.
#closures
#scope
A closure is a function bundled together with the variables from the scope where it was defined, it keeps access to those variables even after the outer function has returned. Functions in JS 'remember' their birthplace.
A practical use is a private counter: the inner functions close over count, which nothing outside can read or corrupt directly, that's encapsulation without a class:
js
js
1
2
3
4
5
6
7
8
9
10
Follow-ups they may ask
Where does `count` live after `makeCounter` returns?
Can you name another everyday use of closures?
Functions, this & modern syntax
Arrow functions and this trip up a lot of juniors, because the behaviour depends on how a function is called, not where it's written. Destructuring and spread/rest are the ES6 features you'll use every day, so expect them too.
Q8
this★★★Live coding
What is this in JavaScript? Why do people use arrow functions for callbacks?
#this
#arrow-functions
this is determined by how a function is called, not where it's defined. Call it as obj.method() and this is obj; call the same function bare (fn()) and in a regular function this is undefined in strict mode (or the global object otherwise). That's why a method passed as a callback 'loses' its this.
Arrow functions don't have their own `this`, they capture it lexically from the surrounding scope at definition time. So inside a class method, an arrow callback (arr.forEach(x => this.handle(x))) keeps pointing at the instance, which is exactly what you want. The trade-off: never use an arrow function as an object method or constructor, because it won't get the calling object as this.
Follow-ups they may ask
So when should you NOT use an arrow function?
How do call/apply/bind change `this`?
Q9
ES6 syntax★★★Live coding
Explain destructuring, and the difference between the spread and rest operators (both ...).
#destructuring
#spread
#rest
Destructuring unpacks values from arrays or objects into variables: const { name, age } = user or const [first, second] = arr. You can rename ({ name: n }), set defaults ({ age = 0 }), and pull nested values.
Both spread and rest use ...; direction is what differs:
Spreadexpands an iterable into individual elements, [...a, ...b] to merge arrays, { ...obj, x: 1 } to copy-and-override, or fn(...args) to pass an array as arguments.
Restcollects the remainder into one variable, function f(first, ...others) gathers extra args into an array, and const { a, ...others } = obj collects the leftover properties.
Rule of thumb: spread on the right-hand side (spreading out), rest on the left-hand side or in a parameter list (gathering in).
Follow-ups they may ask
Is `{ ...obj }` a deep copy or a shallow copy?
Values, references & arrays
The moment your code touches objects and arrays, the value-vs-reference distinction starts causing bugs. Interviewers test it with a small mutation snippet, then check that you know your array methods.
Q10
Types★★★Live coding
What does this print, and why?
JavaScript
javascript
1
2
3
4
5
6
7
8
#reference
#value
#mutation
It prints 2, then 2.
Objects are held by reference. const b = a copies the reference, not the object, so a and b point at the same object, mutating through b is visible through a. Hence a.count is 2.
Inside reset, obj is a copy of the reference. Re-assigning obj = { count: 0 } just repoints the local variable at a new object; it doesn't touch what a points to. If instead we'd written obj.count = 0 (mutating the shared object), a.count would become 0. Primitives (numbers, strings, booleans) are copied by value and don't have this behaviour.
Follow-ups they may ask
How would you make a genuine copy of `a` so `b` is independent?
Q11
Arrays★★★Live coding
Which common array methods mutate the original array, and which return a new one? Why does it matter?
Non-mutating (return a new array or value, leave the original alone): map, filter, reduce, slice, concat, flat, and the newer toSorted/toReversed/with.
It matters because mutation causes bugs when data is shared, especially in React/Redux, where state must be treated as immutable so change detection works. Reaching for the non-mutating variants (or copying first with [...arr].sort()) keeps side effects out of shared state.
Follow-ups they may ask
`sort` has a gotcha with numbers, what is it?
Q12
Array methods★★★Live coding
Given an array of numbers, use array methods to get the sum of the squares of the even numbers.
JavaScript
javascript
1
2
#map
#filter
#reduce
Chain the three workhorse methods, each doing one job, filter to keep evens, map to square, reduce to sum:
js
js
1
2
3
4
Each step returns a new array (or value) and never mutates nums. You could fold it into a single reduce for one pass instead of three, the chain just reads more clearly, which is usually the right call unless the array is huge.
Follow-ups they may ask
What's the difference between `map` and `forEach`?
Does `reduce` need an initial value? What happens on an empty array without one?
The DOM & events
For any frontend-leaning role, expect at least one DOM question. The two that come up most: how you select elements, and how event bubbling enables delegation.
Q13
DOM & events★★★Technical screen
How do you select elements in the DOM, and what's the difference between querySelectorAll and getElementsByClassName?
#dom
#selectors
querySelector/querySelectorAll take any CSS selector and are what I reach for by default. The key subtlety: querySelectorAll returns a static `NodeList`, a snapshot that doesn't update if the DOM changes, whereas getElementsByClassName/getElementsByTagName return a live `HTMLCollection` that does auto-update.
The live collection surprises people: iterating it while adding/removing matching elements can loop forever or skip items. NodeList from querySelectorAll also supports forEach directly; HTMLCollection doesn't, so you spread it ([...coll]) first.
Follow-ups they may ask
Why can iterating a live HTMLCollection while mutating the DOM go wrong?
Q14
DOM & events★★★Technical screen
What is event bubbling, and what is event delegation? Why is delegation useful?
#events
#delegation
#bubbling
Bubbling: when an event fires on an element, it then fires on that element's ancestors in turn, up to document. A click on a <button> inside a <li> inside a <ul> triggers handlers on the button, then the li, then the ul.
Event delegation uses that: instead of attaching a listener to every child, you attach one listener to the parent and check event.target to see what was actually clicked:
js
js
1
2
3
4
5
It's useful because (1) it's far fewer listeners, better memory and setup cost, and (2) it automatically covers elements added after you wired it up, which per-element listeners don't.
Follow-ups they may ask
How do you stop an event from bubbling? And what does `preventDefault` do differently?
Q15
Browser★★★Technical screen
What's the difference between <script>, <script async>, and <script defer>?
#html
#performance
#loading
It's about when the script downloads and executes relative to HTML parsing:
Plain `<script>`: parsing stops, the script downloads and runs, then parsing resumes. It blocks rendering, worst for perf if placed in <head>.
`async`: downloads in parallel with parsing, then runs as soon as it's ready, pausing the parser at that moment. Order between multiple async scripts isn't guaranteed. Good for independent third-party scripts (analytics).
`defer`: downloads in parallel, but waits to run until parsing is finished, and runs deferred scripts in order. Best default for your own app scripts that touch the DOM.
Follow-ups they may ask
If two scripts depend on each other, which attribute do you use?
Async foundations
You won't get grilled on the event loop at this level, that's the intermediate round, but you will be asked how single-threaded JavaScript handles slow work without freezing, and to show basic comfort with Promises and async/await.
Q16
Async★★★Technical screen
JavaScript is single-threaded. So how does it do things like network requests without freezing the page?
#async
#promises
#callbacks
The JS engine runs your code on a single call stack, one thing at a time. But the browser (or Node) provides extra capabilities, timers, network, the DOM, that run outside that thread. When you call fetch, JS hands the request off to the browser and immediately continues; it doesn't sit and wait.
When the response is ready, the browser queues your callback. The event loop waits until the call stack is empty, then pulls queued work onto the stack to run. So the main thread is never blocked waiting on I/O, it's free to keep handling clicks and rendering. We express that queued work with callbacks, Promises, or async/await.
Follow-ups they may ask
What's the difference between a callback and a Promise?
What does `async/await` give you over `.then()` chains?
Q17
Async★★★Live coding
What is a Promise? Rewrite this callback code with async/await and handle errors.
JavaScript
javascript
1
2
3
4
5
6
7
#promises
#async-await
A Promise is an object representing a value that isn't ready yet, it's in one of three states: pending, fulfilled, or rejected. You attach reactions with .then/.catch, or consume it with await. It fixes 'callback hell' by making async steps chainable and giving errors a single path.
With async/await the nested version flattens out, and one try/catch covers both steps:
js
js
1
2
3
4
5
6
7
8
9
(This assumes getUser/getPosts return promises; a callback-only API would be wrapped in new Promise first.)
Follow-ups they may ask
If `getUser` and `getPosts` were independent, how would you run them in parallel?
How to prepare (in the two weeks before)
You don't need months. For a junior role, 2-3 focused weeks covering the topics above is enough. What actually moves the needle:
Run the snippets yourself. Don't just read the answers, open a console, type each snippet, predict the output, then check. The prediction step is where learning happens.
Explain out loud, to a rubber duck or a friend. If you can't say why0 == '' is true in a sentence, you don't know it yet.
Build one small thing that touches the DOM, events, and a fetch. Interviewers love asking "walk me through something you built."
The green flags interviewers are quietly scoring
You narrate your reasoning instead of blurting a single word.
You reach for const, ===, and array methods by instinct.
You say "I'm not sure, here's how I'd figure it out" rather than bluffing.
The red flags that sink candidates
Reciting a memorised definition but unable to apply it to a snippet.
Silence, the interviewer can't tell how you think, so they assume the worst.
Confusing null/undefined or ==/=== under light pressure.
Where to go next
Nail these and you're ready for the intermediate JavaScript interview, where the questions shift from "what prints" to "implement debounce" and "predict the microtask order." When you're ready for the deep end, the advanced / principal guide covers system design and performance at scale.
And to drill everything in this series as timed flashcards with progress tracking, head to the interview practice hub. Attempt, reveal, self-grade, repeat, until the answers are muscle memory.
Stay in the Loop
Get occasional updates on AI engineering, robotics projects, and lessons from building startups. No spam, unsubscribe anytime.