Forty-eight facts and snippets, six per topic, for a last review before a JavaScript round.
Types & equality
- Seven primitives (
string,number,bigint,boolean,undefined,symbol,null) plusobject. Primitives are immutable and compared by value. typeof null === 'object'is a historic bug; test withx === null.typeofa function is'function'.==coerces,===does not:'' == 0istrue,null == undefinedistrue, butnull == 0isfalse.NaN !== NaN. UseNumber.isNaN(x); the globalisNaN('abc')coerces first and returnstrue.Object.isequals===exceptObject.is(NaN, NaN)istrueandObject.is(0, -0)isfalse.- Falsy:
false,0,-0,0n,'',null,undefined,NaN(and the legacydocument.all). Everything else is truthy, including[],{}and'0'.
Scope, hoisting & closures
varis function-scoped;letandconstare block-scoped.let,constandclassare hoisted into the temporal dead zone: using them before the declaration throwsReferenceError.- Function declarations are hoisted with their body. With
var f = function () {}onlyfis hoisted (asundefined), so an early call throwsTypeError. - A closure is a function plus the variables it captured from its enclosing scope; they stay alive as long as the function does (closures note).
for (let i ...)creates a freshiper iteration, sosetTimeout(() => console.log(i))logs0 1 2; withvarit logs3 3 3.constblocks reassignment, not mutation:const a = []; a.push(1)is fine.Object.freezeis shallow.
this & functions
- The call site sets
this:obj.fn()givesobj; a plainfn()givesundefinedin strict mode (the global object in sloppy mode). - Arrow functions have no own
this,argumentsorprototype, and cannot be called withnew. fn.call(ctx, a, b)andfn.apply(ctx, [a, b])invoke immediately;fn.bind(ctx)returns a new function whosethiscannot be changed again, even bycall.- Precedence:
new> explicit (call,apply,bind) > implicit (obj.fn()) > default.neweven overridesbind(binding rules). - Passing a method loses
this:setTimeout(obj.fn). Fix withobj.fn.bind(obj)or() => obj.fn(). fn.lengthcounts parameters before the first default or rest parameter:((a, b = 1, c) => {}).lengthis1.
Objects & prototypes
- Property lookup walks the prototype chain:
obj→Ctor.prototype→Object.prototype→null. classis mostly sugar over constructor functions: methods live onClass.prototype, class bodies are strict, and calling a class withoutnewthrows.Object.create(proto)sets the prototype directly;Object.create(null)makes a dictionary with no inherited keys.{ ...obj }andObject.assign()copy own enumerable properties one level deep (a shallow copy).structuredClone()deep-copiesDate,Map,Setand cycles, but throws on functions and drops class prototypes.- Key order: integer-like keys ascending, then string keys in insertion order.
Object.keys({ b: 1, 2: 1, a: 1, 1: 1 })is['1', '2', 'b', 'a'].
Arrays & iteration
- In-place:
push,pop,shift,unshift,splice,sort,reverse,fill. Copying (ES2023):toSorted,toReversed,toSpliced,with. - Default
sortcompares strings:[10, 9, 1].sort()is[1, 10, 9]. Pass(a, b) => a - b. forEachcannotbreak;returnonly skips to the next item. Usefor...of,someoreveryto stop early.for...inloops over enumerable string keys, inherited ones included;for...ofloops over an iterable’s values.['1', '2', '3'].map(parseInt)is[1, NaN, NaN]:mappasses the index as the radix.[NaN].includes(NaN)istrue, but[NaN].indexOf(NaN)is-1.
Async & the event loop
- One call stack: the event loop picks the next task only when the stack is empty.
- After each task, all microtasks run (promise callbacks,
queueMicrotask,MutationObserver) before rendering or the next task (event loop note). - The executor in
new Promise(executor)runs synchronously; only the.thencallbacks are deferred. await xsuspends the function and resumes it in a microtask, even ifxis not a promise.asyncfunctions always return a promise.Promise.allfails fast,allSettlednever rejects,racefollows the first to settle,anytakes the first fulfillment or rejects withAggregateError(write them yourself).- The classic ordering puzzle logs
1 4 3 2: synchronous code, then microtasks, then timers.
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
console.log(4);
// 1, 4, 3, 2Modern syntax
a ?? bfalls back only onnullorundefined;a || bon any falsy value.0 ?? 5is0,0 || 5is5.a?.b?.()short-circuits toundefinedwhen the left side isnullorundefined.- Destructuring defaults apply only to
undefined:const { a = 1 } = { a: null }leavesaasnull. [...new Set(arr)]removes duplicates;...restcollects the remaining items or properties.Mapkeeps insertion order and takes any key type.WeakMapkeys must be objects (or non-registered symbols) and do not prevent garbage collection.- ES modules are strict by default, top-level
thisisundefined, and imports are live, read-only bindings.
Numbers, strings & JSON
0.1 + 0.2is0.30000000000000004. Compare with a tolerance, or work in integers (cents).Number.MAX_SAFE_INTEGERis2 ** 53 - 1. Beyond it useBigInt(10n ** 20n), and never mix:1n + 1throwsTypeError.+''is0,+'3px'isNaN, butparseInt('3px')is3.+concatenates if either side is a string; other arithmetic converts to numbers.'5' + 2is'52','5' - 2is3.- Strings are immutable: methods return new strings, and
str[0] = 'x'throwsTypeErrorin strict mode (it is silently ignored otherwise). JSON.stringifydropsundefined, functions and symbols in objects (they becomenullin arrays), writesNaNasnull, and throws onBigIntand cycles.