← ALL GUIDES

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

  1. 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.

    The JavaScript event loop: call stack, macro-task queue, micro-task queue, timer API and promise API — theSeniorDev
    event loopcall stackmicrotask queuemacrotask queue
  2. 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.

    Senior JavaScript interview question: console.log ordering with a resolved promise and setTimeout zero — theSeniorDev
    setTimeoutpromisesmicrotasksinterview questions
  3. 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.

    JavaScript execution context (stack frame): this, function code, arguments value and variable mapping with the scope chain — theSeniorDev
    execution contextstack frameheapthis
  4. 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.

    JavaScript scope chain: nested for, if and function scopes resolving up to the global scope — theSeniorDev
    scopescope chainlexical scope
  5. 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.

    JavaScript closure memory example: which variables stay alive and which get garbage collected when a handler is referenced by the DOM — theSeniorDev
    closureslexical scopememory
  6. 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 collection in JavaScript: periodically scanning the heap and tagging unreferenced variables as free memory — theSeniorDev
    garbage collectionheapmemory management
  7. 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.

    JavaScript promise states diagram: pending, fulfilled and rejected mapped to .then and .catch callbacks in a promise-based fetch — theSeniorDev
    promisescallbackscallback hellmicrotask queue
  8. 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.

    Promise chain vs async/await: the same fetch flow written as .then chains and as an async function with await — theSeniorDev
    async/awaitpromisessyntax sugar
  9. 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.

    JavaScript generator function: function* createCounter pausing at yield and resumed with next() to output 1, 2, 3 — theSeniorDev
    generatorsiteratorsyield
A preview of the free Senior training

Free Senior Training.

One session, the whole system: the fundamentals, the interview strategy, and the plan to the offer. Watch it free.