Cheat sheet · JavaScript Core Mastery
JavaScript Core cheat sheet
How JavaScript really runs: execution contexts, the call stack, hoisting, scope, closures, the engine and the event loop. Built for the night before an interview. The same model explains most traps.
blogs · jp-blogs.vercel.app/cheatsheets/series/javascript
Execution contextLesson 1 →
- Everything runs inside an execution context: a memory half (variable environment, key to value pairs) and a code half (thread of execution).
- The global context is created first. Every function call makes a new context, deleted when the call ends.
- Phase 1, memory creation:
vargetsundefined, a function declaration gets its whole body. Phase 2, code execution: line by line.
console.log(a); // undefined
console.log(greet()); // "hello"
var a = 10;
function greet() { return "hello"; }Memory phase explains why both lines run without errors.
Tip: JavaScript is synchronous and single-threaded: one stack, one command at a time. Async work happens outside the engine (Lesson 14).
Call stackLesson 2 →
- A stack (LIFO): push a context on a call, pop it when the function finishes (last line or
return). - The global context is at the bottom and is popped once, at the very end.
- Same function called 3 times = 3 separate contexts that share nothing.
- Stack trace: read top to bottom. The top is the deepest, most recent call.
function f() { return f(); }
f(); // no stop condition
// RangeError: Maximum call stack
// size exceededA stack overflow: recursion with no stopping point.
var, let, const and the TDZLesson 3 →
| Topic | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisted | Yes, as undefined | Yes, but locked (TDZ) | Yes, but locked (TDZ) |
| Redeclare | Allowed | SyntaxError | SyntaxError |
| Reassign | Yes | Yes | No (TypeError) |
On window | Yes | No | No |
- TDZ (temporal dead zone): from the top of the scope until the declaration runs. Reading throws
ReferenceError: Cannot access before initialization. constlocks the binding, not the value:obj.x = 1still works.Object.freezeis shallow.letandconstlive in a declarative record,varin the global object. That is whyletstopped global name clashes.
Function scope vs block scopeLesson 4 →
- Each call gets its own variable environment: two functions with
var xnever clash. - Access goes outward through the outer reference, never sideways. Parameters are set in the memory phase.
- After a function returns, its environment is freed, unless a closure still holds it.
for (var i = 0; i < 3; i++) {}
console.log(i); // 3, var ignores block
for (let j = 0; j < 3; j++) {}
console.log(j); // ReferenceErrorlet makes a fresh binding per iteration and discards them.
The global object and thisLesson 5 →
| Where | Global object |
|---|---|
| Browser | window |
| Node.js | global |
| Web Worker | self |
| Anywhere | globalThis |
- Even an empty file gets a global context, a global object and
this. At top level of a classic scriptthis === window(undefinedin an ES module). - Two script tags share one
window: a secondvar countoverwrites the first. 'use strict'does not change top-levelthis. It only makesthisundefinedin a plain function call.
undefined, null and not definedLesson 6 →
| Term | Meaning |
|---|---|
undefined | Memory reserved, no value yet (set by the engine) |
null | Empty on purpose (set by you) |
| not defined | Never declared: ReferenceError |
- Falsy values:
undefined,null,0(and-0),0n,"",false,NaN. typeof undeclaredreturns"undefined"without throwing.- JavaScript is loosely (dynamically) typed: the type belongs to the value, not the variable.
- Never assign
undefinedyourself. Usenullfor "intentionally empty".
Scope chain and shadowingLesson 7 →
- A lexical environment = local memory + a link to its parent. Global links to
null. - Lookup goes inside out until found. Not found anywhere:
ReferenceError. - JavaScript is lexically (statically) scoped: scope depends on where code is written, not who calls it.
- Shadowing: a local name hides an outer one. The outer value is unchanged.
- Global can never reach into a function's locals.
const x = "global";
function a() {
const x = "local"; // shadows
function b() { return x; }
return b(); // "local"
}TDZ details and the five errorsLesson 8 →
- The TDZ is about time, not position: a function above the declaration works if it is called after the declaration ran.
- Shadowing: an inner
let xhas its own TDZ, so the outerxis never reached. function f(a = b, b = 1)throws: defaults run left to right likelet.typeofis safe on undeclared names but throws in the TDZ.- A
SyntaxErroris found at parse time, so nothing in the file runs.
| Message | Cause |
|---|---|
x is not defined | Never declared |
Cannot access 'x' before initialization | Inside the TDZ |
Identifier 'x' has already been declared | Redeclared let or const |
Missing initializer in const declaration | const x; |
Assignment to constant variable | Reassigned const |
Blocks and shadowing rulesLesson 9 →
- A block (
if, loop, bare braces) creates a new lexical environment only if it holdslet,const,classor a function declaration. varignores blocks, so avar"shadow" is the same variable reassigned.switchhas one block for all cases: twolet ratein different cases throw. Wrap each case body in braces.
| Outer | Inner | Result |
|---|---|---|
let x | let x in a block | Legal shadowing |
let x | var x in a block | SyntaxError |
let x | var x in a function | Legal |
Parameter a | let a at top of body | SyntaxError |
ClosuresLesson 10 →
- A closure = a function plus a live reference to the lexical environment where it was created.
- It holds the variable (a reference), not a copy of the value.
- Each call of the outer function makes a separate closure with its own variables.
- Uses: private state, factories, callbacks, once and memo helpers.
- A leak happens if something (a never-removed listener) keeps the closure alive.
function counter() {
let n = 0;
return () => ++n; // keeps n alive
}
const c = counter();
c(); c(); // 1, 2The loop and setTimeout bugLesson 11 →
for (var i = 1; i <= 3; i++) {
setTimeout(() => log(i), i * 1000);
}
// 4 4 4 (one shared i, loop already done)All callbacks close over one variable.
| Fix | How |
|---|---|
let | Fresh binding per iteration |
| IIFE | (function (j) { ... })(i) copies the value |
| Helper function | Call it with i: own environment per call |
bind | log.bind(null, i) fixes the argument |
Watch out: let only fixes the loop variable. A var inside the loop body is still shared. setTimeout(fn, 1000) means at least 1000 ms.
First-class functions and callbacksLesson 12 →
- First-class: functions can be assigned, stored, passed and returned like any value.
- Higher-order function: takes a function, returns one, or both.
- Sync callback runs before the outer call returns. Async callback runs later (timers, events).
- A regular-function listener gets
this= the element. An arrow function keeps the outerthis.
const off = pct => price =>
price * (100 - pct) / 100;
const d20 = off(20); // keeps pct
el.addEventListener("click", onClick);
el.removeEventListener("click", onClick);
el.addEventListener("click", f,
{ once: true });An anonymous listener needs an AbortController signal or once.
Engine and runtimeLesson 13 →
- Parse: source to tokens to an AST.
- Compile (JIT): Ignition (interpreter) makes bytecode; hot code goes to TurboFan machine code.
- Execute: call stack plus memory heap. Orinoco collects garbage.
- The engine only parses, compiles and runs.
setTimeoutandfetchcome from the runtime (browser or Node). - Objects with the same properties in the same order share a hidden class, so access is fast. Different shapes make it slower.
- Deoptimization: if a function gets a new type or shape, optimized code is dropped and bytecode runs.
- Mark-and-sweep: mark all reachable objects, free the rest. Cycles are fine. Leaks are things still reachable by accident.
Event loop and queuesLesson 14 →
- Players: call stack, Web APIs, microtask queue (promises,
await), callback queue (timers, events). - When the stack is empty, the whole microtask queue drains (including new ones) before one callback task runs. Rendering comes after.
- Code after
awaitis a microtask. A microtask that keeps queuing microtasks starves timers and painting.
log(1); // log = console.log
setTimeout(() => log(2));
Promise.resolve().then(() => log(3));
(async () => {
log(4); await null; log(5);
})();
log(6);
// 1 4 6 3 5 2Sync first, then microtasks in order, then the timer.
Interview one-linersLesson 1 →
| Question | Short answer |
|---|---|
Why does var print undefined? | The memory phase reserved it as undefined before any code ran. |
Is let hoisted? | Yes, but locked in the TDZ until its declaration runs. |
undefined vs not defined? | Reserved but empty vs never declared (ReferenceError). |
| What is a closure? | A function with a live reference to its creation scope. |
| Lexical or dynamic scope? | Lexical: fixed by where the code is written. |
setTimeout 0 vs Promise.then? | The promise runs first: microtasks drain before timers. |
Does const make a value immutable? | No, only the binding. Use Object.freeze (shallow). |
A cheat sheet is a reminder, not a teacher. Each card links to the lesson that explains it step by step.