API reference
JS Puzzles API reference
tricky JavaScript puzzles and gotchas
- Identifiers
- 100
- With full guides
- 95
- Types
- 1
Topics covered
- Hoisting traps
- this binding puzzles
- Type coercion quirks
- Closures in loops
- Array holes & sort
- NaN & equality
- Floating-point math
- Async ordering
Const
| Name | What it does |
|---|---|
Hoisting | Hoisting is the observable behavior in which JavaScript makes certain declarations available before their source position executes. Its effects differ: var reads as undefined, function declarations are callable, and let/const remain inaccessible in the temporal dead zone. |
Closures | Closures let functions retain access to variables from the lexical environment where they were created. This supports persistent state, private data, and callbacks that run after their surrounding function has finished. |
this binding | JavaScript’s this for regular functions comes from the call form, not the definition. Use bind to create a callable function with a fixed this and optional initial arguments. |
Type coercion | Type coercion is JavaScript's automatic or implicit conversion from one data type to another. It explains why operations such as addition may produce a string or number depending on the operands. |
Event loop | The event loop coordinates JavaScript's asynchronous work: it runs one macrotask, drains all microtasks, may render, and then continues with the next macrotask. This ordering explains why promise callbacks commonly precede zero-delay timers. |
Prototype chain | JavaScript resolves a missing property by searching the object's prototype, then that prototype's prototype, until it finds a match or reaches null. This linked lookup structure is the prototype chain. |
Temporal dead zone | The temporal dead zone is the interval from entering a block until execution initializes a let, const, or class binding. Accessing the binding during that interval throws a ReferenceError. |
NaN equality | NaN is unequal to every value, including itself, under == and ===. Detect it with Number.isNaN() rather than an equality comparison. |
Temporal Dead Zone | The temporal dead zone is the interval after a lexical binding's scope starts but before execution initializes its let, const, or class declaration. Reading the binding during that interval throws a ReferenceError. |
Type Coercion | JavaScript automatically converts values when an operation expects another type. The conversion path depends on the context and can involve primitive, numeric, BigInt, string, or object-to-primitive rules. |
NaN behavior | NaN represents an invalid or undefined numerical result. It is unequal to every value, usually propagates through arithmetic, and should be tested with Number.isNaN() rather than equality. |
Event Loop | The JavaScript event loop processes macrotasks one at a time, then drains all pending microtasks before rendering and moving to the next macrotask. This ordering governs scripts, timers, user events, and Promise callbacks. |
Microtask Queue | The microtask queue is the internal PromiseJobs queue used for promise handlers. Handlers are queued asynchronously and run in FIFO order after the current synchronous code finishes. |
Prototype Chain | JavaScript resolves missing properties by walking an object's prototype chain. The chain ends at null; if no object provides the property, the result is undefined. |
Autoboxing | JavaScript primitives can use methods because the language temporarily wraps them in corresponding objects. The primitive remains unchanged; null and undefined are exceptions. |
Floating Point Precision | JavaScript numbers use IEEE 754 double-precision binary floating point, so decimal arithmetic can contain small representation errors. Compare results with a tolerance suited to their magnitude instead of relying on strict equality. |
typeof null | typeof null returns "object", even though null is a separate JavaScript type and is not an object. This historical behavior is retained for compatibility. |
Array holes | Array holes are missing indexed properties, not stored undefined values. They make an array sparse, and different operations either skip the missing positions or expose them as undefined. |
Numeric Separators | Numeric separators are underscores placed between digits in a number literal, such as 1_000_000. They improve readability while leaving the represented numeric value unchanged. |
Optional Chaining | Optional chaining (?.) safely accesses nested properties, computed properties, or methods when a value may be null or undefined. It short-circuits that chain and returns undefined instead of throwing. |
Tagged Templates | |
Label Statements | A label names a loop so nested break or continue can target that loop. It provides structured control flow, not an arbitrary jump mechanism. |
Comma Operator | The comma operator evaluates several expressions from left to right and returns only the last expression's value. Parentheses are often needed because comma has very low precedence. |
void operator | The void operator evaluates an expression, discards its result, and returns undefined. Parenthesize complex operands to make operator precedence explicit. |
Bitwise Tricks | JavaScript bitwise operators manipulate the binary representation of 32-bit integer operands. They include logical bit operations and left or right shifts, and are mainly useful for specialized low-level tasks. |
parseInt radix | parseInt(str, radix) reads an integer prefix using the specified base, from 2 through 36. It stops at the first character that is not valid in that base and returns the digits read so far. |
Number.MAX_SAFE_INTEGER | Number.MAX_SAFE_INTEGER is the Number static property equal to 9007199254740991 (2^53 − 1). It is the greatest integer JavaScript can represent exactly and compare reliably as a Number. |
Equality Table | JavaScript has three main value-comparison operations: ==, ===, and Object.is(). Their results differ mainly in coercion, NaN handling, and the treatment of signed zero. |
Truthy and Falsy | JavaScript converts values to Boolean in conditions and logical expressions. Most values are truthy; false, zero, the empty string, null, undefined, and NaN are falsy. |
Spread vs Rest | Rest parameters gather remaining function arguments into an array, while spread syntax expands an iterable into separate arguments or elements. Both use ..., but their syntactic position determines which behavior applies. |
Destructuring Defaults | |
Generator return value | A generator's return(value) ends generator execution and supplies a final value. The caller sees it in the result of next() with done: true, unlike ordinary yielded values. |
Symbol.toPrimitive | Symbol.toPrimitive is a well-known symbol used to define an object's conversion to a primitive. JavaScript calls the associated method with a hint before falling back to valueOf() and toString(). |
IIFE | |
Currying | Currying transforms a multi-argument function so it can be called through successive calls such as f(a)(b)(c). The wrapper collects arguments and invokes the original function when enough have been supplied. |
Memoization | Memoization caches a function's result for an input so repeated calls can avoid recalculation. In JavaScript, it is commonly implemented as a decorator backed by a Map. |
Debounce | debounce(f, ms) returns a wrapper that waits until calls stop for ms milliseconds, then invokes f once with the latest arguments. New calls cancel the previous scheduled invocation. |
Throttle | throttle(f, ms) creates a wrapper that invokes f at most once per interval. It runs the first call immediately and later forwards the latest call after the cooldown. |
Deep vs Shallow Copy | A shallow copy creates a new outer object but keeps references to nested objects. A deep copy recursively duplicates nested properties; structuredClone is the documented built-in for this purpose. |
Pass by Reference | JavaScript passes arguments by value, not by reference. For object arguments, the copied value still identifies the same object, so property mutations are visible to the caller while parameter reassignment is local. |
Variable Shadowing | Variable shadowing happens when a nested scope declares a name already used outside it. With const, the inner binding takes precedence throughout the block and is inaccessible before initialization. |
Block Scope | const creates a block-scoped binding that must be initialized immediately and cannot be reassigned. The referenced object or array may still be mutated. |
Function Scope | Function scope limits a function's local declarations to that function. Nested functions can access enclosing scopes, creating a scope chain where nearer bindings shadow outer ones. |
Lexical Scope | Lexical scope determines where JavaScript resolves names: from the current lexical environment outward through its enclosing environments. Blocks, functions, and scripts form environments, with let and const providing block-scoped bindings. |
Dynamic this | JavaScript's regular-function this is determined by how the function is called, not where it is defined. Method calls provide the receiver, while standalone calls, explicit binding, constructors, and arrow functions follow different rules. |
Arrow function this | Arrow functions do not have their own this; they read it from the surrounding context. This makes them useful as callbacks inside methods when the callback must preserve the method's object context. |
new.target | new.target reveals whether the current function was called with new. It is undefined for regular calls and refers to the function during construction. |
Tail Call | A tail call is a final function call with no work left for the caller. Tail-call optimization can reduce stack usage for suitable recursion, but JavaScript engine support is not universal. |
Short Circuit Evaluation | Short-circuit evaluation makes JavaScript stop evaluating a logical expression as soon as its result is determined. The || operator stops at a truthy value, while && stops at a falsy value, and each returns the operand where it stopped. |
Nullish Coalescing | The ?? operator returns its left value unless it is null or undefined, in which case it returns the right value. It is commonly used for defaults while preserving meaningful falsy values such as 0, false, and ''. |
Logical Assignment | Logical assignment operators conditionally assign values using ||=, &&=, or ??=. They short-circuit, so the right-hand expression runs only when assignment is needed. |
Array.prototype.sort default | Array.prototype.sort() rearranges an array in place and returns the same array. Without a comparator, it compares string representations by UTF-16 code units, so numeric values require an explicit numeric comparator. |
Map vs Object | Map is a keyed collection like Object, but it accepts keys of any type, including objects, and provides convenient methods plus size. Use it for dynamic keyed data; use Object when the shape and string-like keys are known in advance. |
Set uniqueness | A Set is a collection of values without keys where each value appears at most once. It is useful for membership checks and removing duplicates. |
Object key order | JavaScript objects use a special key order: integer property names come first in ascending numeric order, while other string keys follow creation order. Thus, object enumeration is not always simple insertion order. |
JSON pitfalls | JSON.stringify converts JavaScript values to JSON text, and JSON.parse converts JSON text back into JavaScript values. JSON supports fewer types than JavaScript, so serialization can omit unsupported data. |
Date parsing | Date.parse(str) reads a date from a string and returns its timestamp in milliseconds since 1 January 1970 UTC. Invalid formats produce NaN. |
Regex lastIndex | RegExp.lastIndex records where the next search starts for global and sticky regular expressions. Successful matches advance it, while failed exec() calls reset it to 0; assigning it manually selects a starting offset. |
String immutability | JavaScript strings are immutable: once created, their contents cannot be changed. String operations return new strings, so retain the result when you need transformed text. |
Implicit globals | |
Strict Mode | Strict mode is enabled with the "use strict" directive at the top of a script or function. It changes JavaScript to a stricter execution mode; classes and modules enable it automatically. |
getter and setter | Getters and setters create accessor properties that look like ordinary fields to callers. Reads invoke the getter, while assignments invoke the setter. |
Property descriptors | Property descriptors describe an object's property value and its writable, enumerable, and configurable flags. JavaScript provides methods to inspect descriptors and create or update properties with precise behavior. |
Object.freeze | Object.freeze() prevents extensions and makes an object's existing properties non-writable and non-configurable. It returns the same object, while nested objects remain mutable unless frozen separately. |
Object.seal | Object.seal(obj) prevents adding or removing properties and makes existing properties non-configurable. Writable existing values can still change. |
Symbol uniqueness | JavaScript symbols are primitive unique identifiers. Separate Symbol() calls stay distinct even with the same description, while Symbol.for(key) reuses a symbol from the global registry. |
WeakMap GC | WeakMap associates values with object keys without preventing those keys from being garbage-collected. Its entries cannot be enumerated, and cleanup timing is controlled by the JavaScript engine. |
Promise resolution order | Promise handlers are deferred to the microtask queue, even when a promise is already settled. They run after current synchronous code and in FIFO order, before the event loop advances to later macrotasks. |
async await ordering | async functions return promises, and await pauses only the surrounding function. After the awaited promise settles, execution continues asynchronously through the microtask queue, so caller code and other microtasks can run first. |
setTimeout 0 | setTimeout(func, 0) schedules a callback as soon as possible, but only after the current script completes. It does not mean the callback runs immediately or exactly zero milliseconds later. |
requestAnimationFrame | requestAnimationFrame schedules an animation callback for the browser's next suitable animation opportunity. The callback receives elapsed time in milliseconds, and its returned request ID can be used to cancel the scheduled call. |
Recursion limits | JavaScript engines cap the number of nested recursive calls because each call uses stack space. The exact limit varies by engine, and exceeding it can raise a maximum-stack-size error. |
Stack vs Heap | |
Garbage Collection | JavaScript automatically reclaims objects that are no longer reachable from roots. The page explains reachability and the mark-and-sweep algorithm, including how cycles can be collected. |
Function.length | Function.length is a numeric property that indicates how many formal parameters a function expects. Defaults stop the count, destructuring counts once, and rest parameters are excluded. |
arguments object | The arguments object is an array-like, iterable collection of every value supplied to a traditional function. It supports indexes and length, but not Array methods, and arrow functions inherit arguments from an enclosing normal function. |
rest parameters | Rest parameters let a function accept any number of arguments by gathering the remaining arguments into an array. They are written with ... before the final parameter name. |
default parameter scope | Default parameter initializers run in their own scope, outside the function body. Earlier parameters are visible, but declarations in the body are not and can cause a runtime ReferenceError. |
computed property names | Computed property names let object literals derive keys from evaluated expressions. Put the expression in square brackets to create a dynamic property name. |
getter memoization | A memoized getter computes a property value on first access and caches it for subsequent reads. It combines lazy evaluation with property-level memoization. |
Array.from iterables | Array.from turns an iterable or array-like value into a real array. It can also transform each item while building that array. |
Iterator protocol | The iterator protocol standardizes objects that produce sequence values through next(). Each call returns an IteratorResult with value and done, while the iterable protocol lets language constructs obtain the iterator. |
Generator delegation | yield* composes generators by delegating iteration from an outer generator to an inner generator. The inner generator’s yielded values pass through transparently, and its final return value becomes the yield* expression’s result. |
async generators | Async generators produce sequences lazily while allowing asynchronous work between values. Declare them with async function* and consume them using for await...of. |
for-await-of | for await...of processes values from an async iterable as they become available. It is the asynchronous counterpart of for...of and is commonly paired with async generators. |
Proxy traps | JavaScript Proxy traps let a handler intercept fundamental operations on a target, such as property access, assignment, deletion, calls, and construction. Traps can customize behavior or delegate transparently to the target. |
Reflect methods | Reflect exposes function-based forms of interceptable object operations, with method names matching Proxy traps. It is commonly used in proxy handlers to forward the default operation after adding custom behavior. |
BigInt arithmetic | BigInt is JavaScript’s integer type for values of arbitrary length. Its arithmetic resembles Number arithmetic, but operands cannot be mixed with regular numbers and division truncates toward zero. |
Number coercion table | Number coercion converts JavaScript values to numbers using type-specific rules. Numbers pass through, primitive values map predictably, strings are parsed, and some values produce NaN or throw. |
String coercion | String coercion converts a value to text when JavaScript needs its string form. It happens implicitly in suitable operations or explicitly with String(value). |
Boolean coercion | JavaScript coerces values to booleans in conditions and other Boolean contexts. Only a small set of values is falsy; every other value is truthy. |
Array flattening | Use flat() to create a new array with nested arrays concatenated to a chosen depth. Use flatMap() when transforming elements and flattening the results by one level in one operation. |
Chained optional calls | Optional chaining safely accesses nested properties and optionally calls methods when an intermediate value may be null or undefined. A nullish value before ?. short-circuits the remaining chain and returns undefined. |
Ternary associativity | JavaScript’s conditional (ternary) operator selects one of two values from a condition. Chained ? expressions are grouped right-to-left, enabling compact multi-condition selection. |
Operator precedence | Operator precedence determines the default order for applying operators in a JavaScript expression. Higher precedence runs first, equal precedence proceeds left to right, and parentheses can override the order. |
Exponentiation operator | The exponentiation operator ** raises a base to an exponent, including fractional exponents. It is right-associative, so chained expressions group from the right. |
Unary plus | Unary plus (+) is the one-operand form of the plus operator. It leaves numbers unchanged and converts non-number operands to numbers, like Number(...). |
Double negation | Double negation (!!) converts any JavaScript value to a boolean primitive using its truthiness. It is equivalent to Boolean(value). |
Comma in arrays | A trailing comma after the last item in an array literal is allowed and ignored. It keeps multiline array edits consistent, but additional commas create sparse-array holes. |
Trailing commas | Trailing commas may follow the final item in many JavaScript comma-separated lists. They are optional, preserve semantics, and make multiline edits cleaner, but JSON and rest parameters have different rules. |
Search across every package and filter by type in your dashboard.
Start learning