GUIDE · JAVASCRIPT CONCEPTS
9 JavaScript Concepts That Get You To Senior
Everyone tells you to master the fundamentals to get to senior. Nobody tells you what those fundamentals are. This guide lists them: the nine JavaScript concepts interviewers actually probe, from the **event loop** to **generator functions**.
Each one shows up in real interviews for senior frontend and senior **Node.js** backend roles. Knowing the definition is not enough. You need to trace real code with it. That is what separates a senior answer from a naive one.
THE CONCEPTS
The mental models, one by one
- 01
The Event Loop
The event loop is the component that executes your JavaScript. Three parts: the call stack runs synchronous code top to bottom, plus two queues with different priorities — the microtask queue and the macrotask queue. Queues are first-in-first-out: the first task in is the first one processed.
In the browser, event listeners on DOM elements, the timer API, and the promise API all push tasks into those queues. When a task reaches the call stack it gets broken into function calls, and each function call gets its own stack frame.
The key rule: when the call stack empties, the engine drains the microtask queue before finishing the current tick. Then the browser renders, and the next iteration starts. Promises resolving does not mean their callbacks run immediately — they get queued as microtasks first.
event loopcall stackmicrotask queuemacrotask queue - 02
The Classic Interview Question: console.log Ordering
A very common question: given a mix of `console.log`, a resolved promise, and a `setTimeout(0)`, in what order do the logs print? The naive answer is source order: 1, 2, 3, 4. It is wrong.
Trace it with the event loop. Synchronous logs run first: 1, then 4. The resolved promise callback went to the microtask queue, so it runs before the tick ends: 2. The setTimeout callback is a macrotask, even at zero milliseconds — it runs on the next tick: 3. Final order: 1, 4, 2, 3.
This exact question shows up in interviews for senior frontend positions and senior backend roles with Node.js. Review it until you can trace it cold.
setTimeoutpromisesmicrotasksinterview questions - 03
Execution Context (The Stack Frame)
A function needs more than its own code to run. The execution context — the stack frame — bundles the function code with the value of `this`, the arguments, and the variable mapping: the addresses of every variable in the function's scope chain, pointing into heap memory.
Every time a function is pushed to the call stack, a new execution context is created for that invocation. As the code runs, the engine follows those addresses into the heap to read actual values.
There is also the global context: in the browser that means `fetch`, `alert`, and every global variable injected when the JavaScript environment is created — basically when you open a new tab.
execution contextstack frameheapthis - 04
Scope and the Scope Chain
In JavaScript, everything between curly brackets is a scope. A function declaration creates a scope. An `if` inside it creates another. A `for` loop inside that creates a third. An `else` is a sibling scope of its `if`.
The scope chain is every scope accessible from a point in the code, and it only works inside out. Code inside the `for` loop can read variables from the `if`, the function, and the global scope. The parent cannot go the other way: an outer function has no access to variables declared in an inner one. Child scopes are isolated from their parents.
If you understand scope well, you read code like Neo reads the Matrix. Next time you touch production code, name the scopes you are looking at. It is the art of the masters.
scopescope chainlexical scope - 05
Closures
A closure means a function remembers the variables that were in its lexical scope when it was declared — everything up its scope chain. As long as the function is still in use, those variables cannot be garbage collected. That memory stays taken.
Enclosing the whole chain would waste memory, so modern JavaScript engines only enclose variables that are actually used by the function. That distinction is exactly what interviewers test: given a handler attached to a DOM element, which variables stay alive?
In the video's example, the handler lives as long as the page, so everything it references — the helper function it calls and the tax rates that helper reads — stays in memory. The one variable nothing references is the only one that can be collected.
closureslexical scopememory - 06
Garbage Collection
The garbage collection algorithm periodically scans the heap — the memory your program uses, usually sitting in RAM — looking for variables that are no longer referenced.
It does not actually delete anything. It tags that memory space as free, so new things can be placed on top. That is a soft delete, not a hard wipe.
This is why closures matter for memory: a variable referenced by a live closure can never be tagged as free, no matter how long ago it was declared.
garbage collectionheapmemory management - 07
Promises (and the Callback Hell They Replaced)
Before promises, the only way to react to async work was the callback pattern: hand your code to the network thread and it calls you back when the request finishes. By convention the first callback argument was the error, the rest was data. Nested callbacks became unreadable fast — callback hell. You will still find it in legacy Node.js codebases.
Promises flipped the control: instead of passing a callback and hoping, `fetch` hands you a promise object and you attach callbacks to it with `.then` and `.catch`. A promise starts `pending`, then becomes `fulfilled` on resolve or `rejected` on rejection.
Both outcomes push the attached callbacks into the microtask queue, to run at the end of the current event loop tick — never immediately. Promise chains are cleaner than callback hell and far easier to debug.
promisescallbackscallback hellmicrotask queue - 08
Async/Await
async/await is syntax sugar over promises. Anywhere you write `async` or `await`, the engine translates it to promises under the hood. You get promise behavior without constructing promises explicitly.
The migration path is readability: from callback hell, to promise chains, to `async` functions where you just put `await` in front of every promise. In a bigger codebase the difference is dramatic.
Under the hood it combines two things: an overarching promise created for the function, and a generator that pauses at every `await` and resumes when the value arrives. Every async function returns a promise that resolves with the function's return value once all the awaits finish.
async/awaitpromisessyntax sugar - 09
Generator Functions
Generator functions are functions with state. A normal function runs to completion once. A generator pauses and can be resumed. The `function*` star tells the compiler it is a generator: calling it does not run the body — it creates an iterator object with the function paused.
Calling `next()` on that iterator starts execution. When the code hits a yield, it returns that value and pauses, waiting to be called again. Call `next()` again and it picks up where it left off. A counter generator can yield 1, 2, 3 forever, keeping its count between calls. Add a stop condition and the generator gets tagged as done.
This is the machinery behind async/await: roughly, every `await` in an async function is a `yield` in a generator created under the hood, driven recursively inside a promise until the function completes.
generatorsiteratorsyield

Free Senior Training.
One session, the whole system: the fundamentals, the interview strategy, and the plan to the offer. Watch it free.
MORE GUIDES


