# Runtime Engine, Callstack, Scope

## Runtime Engine, Call Stack, Scope

### JIT compilers

Modern JS engines (V8, JavaScriptCore, SpiderMonkey) parse your source into an AST, generate bytecode for a fast interpreter, then—based on runtime profiling—optimize hot paths with a JIT compiler. This mixed strategy balances quick startup with high peak performance.

![V8 JS engine compiling example](https://1172597814-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJ6Mj8gFbz9Ji6QL6Zi%2F-MKJnu4wgOGihR_niiXU%2F-MKK6dEdmR7hPJhO3HVM%2Fbytecode.svg?alt=media\&token=1baa00e2-7dd2-4e0a-8237-734861add706)

#### Hoisting (what actually happens)

Before executing your code, the engine creates **execution contexts** and their **environment records**. During this compile/setup phase, **declarations** are registered:

* **Function declarations** are hoisted with their full definition and can be called earlier in the file.
* **`var` declarations** are hoisted and initialized to `undefined` (the assignment happens later).
* **`let` / `const` declarations** are hoisted too, but remain in the **Temporal Dead Zone (TDZ)** until their declaration line runs—accessing them earlier throws a `ReferenceError`.
* **Function expressions** are *not* hoisted as callable functions; only the variable binding (e.g., with `var`) is hoisted.

```javascript
logToConsole();            // ✅ "Hello"
console.log(x);            // ✅ undefined (var is hoisted, assigned later)

function logToConsole() {
  console.log("Hello");
}

var x = 5;
```

> Common fix: don’t rely on hoisting for readability—declare before use.

***

### JavaScript runtimes (quick refresher)

A **runtime** = engine + Web/Host APIs + queues + event loop. The engine is single-threaded, but the runtime exposes asynchronous primitives (timers, network, DOM events) that schedule work back to JS.

![](https://1172597814-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJ6Mj8gFbz9Ji6QL6Zi%2F-MKJkhamQI4owCk2KpIA%2F-MKJnsxRbic184Wj9Ot1%2FJS%20runtime%20engine.png?alt=media\&token=72d456ae-cea3-4dd6-86ef-ef57ec9f237f)

***

### Execution context & the Call Stack

* The **Call Stack** is LIFO: a function call pushes a frame; returning pops it.
* “Blocking” synchronous code runs to completion before the runtime can process queued callbacks/microtasks.

```javascript
// Inspect order with DevTools
debugger; // step to observe the stack frames

console.log("1");
setTimeout(function () {
  console.log("2");
}, 3000);
console.log("3");
console.log("4");
console.log("5");
setTimeout(function () {
  console.log("6");
}, 0);
console.log("7");
console.log("8");
console.log("9");
```

#### Microtasks vs (macro)tasks

* **Microtask queue** runs **after** the current stack frame, **before** any timer/IO callbacks: Promise reactions, `MutationObserver`, queueMicrotask.
* **Task/callback queue** contains timers, IO, UI events, etc.\
  Result: Promise handlers run before `setTimeout(fn, 0)` callbacks.

```javascript
console.log("Start");

setTimeout(function () {
  console.log("Timeout callback");
}, 0);

Promise.resolve().then(function () {
  console.log("Promise microtask");
});

console.log("End");

// Output:
// Start
// End
// Promise microtask
// Timeout callback
```

> Note: `fetch()` completes in the host environment and resolves a **Promise**; its `.then/.catch` handlers run as **microtasks**.

{% hint style="danger" %}
In the JS runtime, the **microtask queue** has higher priority and is drained **immediately after the current call stack** (and before rendering), while the **callback (task/macro-task) queue** runs **after all pending microtasks** and typically yields to rendering between tasks; examples of **microtasks** include Promise reactions (`promise.then/catch/finally`), `queueMicrotask`, and `MutationObserver`, so a **network request with `fetch`** resolves to a Promise whose handlers execute as microtasks, often ahead of timers; by contrast, the **callback queue** holds tasks like `setTimeout`/`setInterval`, `MessageChannel`/`postMessage`, user **DOM events** (e.g., `click`, `input`, `submit`), and certain **network/DOM load events** like `img.onload` or `XMLHttpRequest.onload`, which will run only after the microtask queue has been fully emptied.
{% endhint %}

***

### Scope & the Scope Chain

**Scope** is where identifiers are visible. Child scopes can read parent bindings (via the **scope chain**), not vice-versa. JavaScript uses **lexical (static) scoping**: visibility is determined by where code is written.

```javascript
var x = "declared outside function";

exampleFunction();

function exampleFunction() {
  console.log("Inside function");
  console.log(x); // reads from outer scope
}
```

![Example of the scope chain](https://1172597814-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJ6Mj8gFbz9Ji6QL6Zi%2F-MKKOFHPdUCP2WmUhYw5%2F-MKKOSjup6K5rrckJJ2t%2F1_BwARRnm0-gFoh-Rq_ubbwQ.png?alt=media\&token=3ebc3e8d-f4da-41a7-9dff-20603eeee0db)

#### Hoisting meets scope

This classic example shows shadowing + `var` hoisting inside a function scope: the inner `var favouriteFood` is hoisted (set to `undefined`) and **shadows** the outer one until assignment.

```javascript
var favouriteFood = "grapes";

var foodThoughts = function () {
  console.log("Original favourite food: " + favouriteFood);
  var favouriteFood = "sushi";
  console.log("New favourite food: " + favouriteFood);
};

foodThoughts();

// Output:
// Original favourite food: undefined   <-- inner var is hoisted but uninitialized
// New favourite food: sushi
```

Additional notes:

* **Function parameters** are local to the function body.
* **Block scope** (`let`/`const`) confines bindings to `{ ... }` blocks and prevents use before initialization (TDZ). Prefer `const`, use `let` only when you must reassign; avoid `var` in modern code.

***

### Quick takeaways

* Don’t depend on hoisting for clarity—declare before use.
* Understand the event loop order: **stack → microtasks → tasks**.
* Scope is lexical; inner scopes shadow outer bindings. Keep variable lifetimes tight (block scope) to reduce bugs.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mihai-gheorghe.gitbook.io/tic/advanced-javascript/runtime-engine-callstack.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
