Adaptive skill practice — find your level, get sharper.
JavaScript practice
The language fundamentals every interview returns to
Master JavaScript itself — closures and scope, hoisting and the temporal dead zone, the event loop with microtasks and promises, this-binding and prototypes, coercion, and the classic traps (0.1 + 0.2, map(parseInt), the var-in-a-loop bug). Real "what does this log, and in what order?" questions with explanations that build genuine fluency for senior-engineer interviews. Part of the Developer track.
• Meets you at your level — never too easy, never too hard.
• Adapts every question to how you’re doing.
• Tracks your level over time. Skill practice, not a test.
What you’ll master
Follow the curriculum from foundations through advanced practice. Each line is a concept the adaptive question bank can teach and test.
A. Grammar, declarations and types
•
var is function-scoped; let and const are block-scoped
•
a let binding exists from block entry but throws until its declaration runs
•
const forbids reassignment, not mutation of the referenced object
•
a function declaration hoists with its body; var hoists initialized to undefined
•
absent by default versus deliberately empty, and why typeof null is "object"
•
seven primitive types, everything else a reference; primitives are boxed on property access
•
the fixed list typeof can return, including "function" and the null bug
•
a newline after return ends the statement and returns undefined
•
what "use strict" changes: silent write failures throw, this is no longer boxed
•
sloppy-mode assignment to an undeclared name creates a global; globalThis names it portably
B. Operators, coercion and equality
•
== runs the abstract-equality coercion steps; === compares type and value directly
•
null == undefined is true and null == 0 is not; the pair is equal only to each other
•
Object.is distinguishes +0 from -0 and reports NaN as equal to itself
•
==, ===, SameValue and SameValueZero, and which built-in reaches for which
•
+ concatenates if either operand becomes a string; every other arithmetic operator coerces to number
•
Symbol.toPrimitive first, then valueOf/toString in an order set by the hint
•
exactly false, 0, -0, 0n, "", null, undefined, NaN; "0" and [] are truthy
•
&& and || evaluate to one of their operands, not to a boolean
•
?? falls through only on null or undefined, so 0 and "" survive
•
&&=, ||= and ??= short-circuit the assignment itself, skipping the setter
•
?. abandons the rest of the chain and yields undefined rather than throwing
•
** groups right to left and rejects an unparenthesized unary minus on its left
•
a ?? b || c will not parse; the grammar demands parentheses
•
operands are truncated to int32, so >>> and >> disagree on negatives and | 0 floors
•
< and > coerce each operand to a primitive and then a number, so 3 > 2 > 1 evaluates true > 1 and is false
•
the comma operator evaluates every expression left to right and yields the LAST one, which is why (1, 2, 3) is 3
C. Control flow, errors and cleanup
•
anything can be thrown, which is why a caught value has no guaranteed shape
•
TypeError, RangeError, ReferenceError and SyntaxError each signal a different failure class
•
extends Error, setting name, and keeping the stack and message intact
•
the { cause } option preserves the original failure instead of flattening it (ES2022)
•
including on return and on rethrow, and a return inside finally discards the pending result
•
catch {} when the error value is genuinely unused
•
Error.isError identifies errors across realms where instanceof fails (ES2026)
•
a case without break continues into the next, and switch matches with ===
•
using disposes at scope exit via Symbol.dispose, LIFO, even when the block throws (stage 4)
D. Loops and iteration statements
•
enumerable string-keyed properties, inherited ones included
•
requires an iterable and never sees prototype properties
•
indices arrive as strings, which is why for...in is the wrong array loop
•
let in a for head creates a fresh binding each pass; var creates one for all of them
•
a label is how you leave a nested loop without a flag variable
•
forEach ignores return and has no early exit; some/every/for...of do
•
consumes an async iterable and is sequential by construction
•
the condition is tested after the body, so the body always executes at least once
E. Functions and parameters
•
hoisting, the named-function-expression scope, and inferred names
•
evaluated at call time, left to right, and only when the argument is undefined
•
...args is a real array; arguments is array-like and absent in arrow functions
•
spreading any iterable into an argument list, and the array and object forms
•
object patterns, inner defaults, and why a missing argument throws without = {}
•
fn.length counts parameters before the first default or rest parameter
•
no own this, arguments, super or new.target, and not constructible
•
an immediately invoked expression as the pre-module isolation trick
•
engines do not implement tail calls, so deep recursion is a RangeError
•
the reference is copied, so reassigning a parameter never affects the caller
F. Binding, `this` and closures
•
this is resolved when the function is called, not where it was written
•
passing obj.m as a callback strips the receiver
•
an explicit receiver, a spread argument list, and a permanently bound copy
•
an arrow closes over the enclosing this and cannot be rebound
•
new allocates, links the prototype and returns the object unless the body returns another object
•
an undecorated call gets undefined in strict mode and the global object otherwise
•
a closure sees the variable's later value, not a snapshot taken at creation
•
var leaves every callback in a loop sharing one variable
•
data no caller can reach, paid for with per-instance memory
•
fixing some arguments now and taking the rest later
G. Objects and properties
•
any other key is coerced to a string, so obj[1] and obj["1"] are one property
•
{ [expr]: v } builds the key at runtime; { x } is { x: x }
•
value, writable, enumerable, configurable, and how defineProperty defaults them to false
•
accessor properties run code on read or write, and are invisible at the call site
•
Object.hasOwn ignores the prototype chain; the in operator walks it
•
Object.freeze protects one level, so nested objects remain mutable
•
which of adding, deleting and writing each one still permits
•
both copy only own enumerable properties, and copy references not values
•
Object.assign triggers an inherited setter; spread creates an own data property
•
integer-like keys ascend first, then string keys in insertion order, then symbols
•
round-tripping an object through an array of pairs to filter or transform it
•
removes the property entirely, unlike setting it to undefined, and leaves an array hole
•
Object.assign applies its sources left to right, so a later source overwrites an earlier one key by key
H. Prototypes and inheritance
•
a missing property walks [[Prototype]] links until it reaches null
•
an instance's link versus the object a constructor hands to its instances
•
constructing an object with a chosen prototype, including no prototype at all
•
writing an inherited property creates an own property instead of updating the parent
•
if the prototype defines a setter, the write runs it and no own property appears
•
it tests the chain, not the constructor, and breaks across realms
•
inherited and writable, so it lies after you replace prototype wholesale
•
a safe dictionary with no inherited methods, and what stops working on it
•
one function object on the prototype serves every instance
I. Classes
•
methods land on prototype, but the body is always strict and the binding is not usable before evaluation
•
a derived constructor must call super() before it may touch this
•
instance fields are created per instance, in declaration order, before the constructor body
•
a class field holding an arrow is per-instance and survives detachment
•
#x is a hard-private slot enforced by the engine, and #x in obj is the brand check
•
statics live on the constructor; a static block runs once when the class is defined
•
get/set install accessor properties on the prototype, not on the instance
•
subclassing Array or Error, and how Symbol.species decides what methods return
•
distinguishing construction from a plain call inside the same function
•
super.m() resolves against the home object while this stays the current instance
•
a function returning class extends Base to compose behavior without multiple inheritance
J. Numbers and arithmetic
•
every Number is a 64-bit binary float, which is why 0.1 + 0.2 !== 0.3
•
beyond Number.MAX_SAFE_INTEGER integer arithmetic silently returns the wrong answer
•
the only value failing x === x; test it with Number.isNaN
•
global isNaN("foo") is true because it converts first; Number.isNaN does not
•
prefix parsing with an explicit radix versus whole-string conversion that rejects trailing junk
•
and it rounds the stored binary value, so (1.005).toFixed(2) is not "1.01"
•
where -0 is observable and where === hides it
•
-7 % 3 is -1; a true modulo needs ((n % m) + m) % m
•
Math.round breaks ties toward +Infinity, which differs from trunc, floor and ceil on negatives
•
arbitrary-precision integers throw when combined with a Number in arithmetic
K. Strings and text
•
no method mutates in place; every operation returns a new string
•
length counts code units, so an emoji or an astral character counts as two
•
spread and for...of walk code points; bracket indexing splits surrogate pairs
•
NFC versus NFD, and why two identical-looking strings can be unequal
•
slice accepts negative indices; substring clamps and silently swaps its arguments
•
a string pattern replaces only the first match unless the regex carries /g
•
$1, $& and $$ in the string form, and the argument list of the function form
•
interpolation, embedded expressions, and literal newlines
•
a function receiving the literal chunks and the interpolated values separately, plus .raw
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.