Fundamentals 11 min read

Understanding V8 JavaScript Engine: Scopes, Execution Contexts, and Runtime Process

This article explains how the V8 engine parses JavaScript source code into an AST, establishes scope rules, creates isolates, handle scopes and contexts, and manages the execution context stack during interpretation and execution, illustrated with detailed code examples and diagrams.

ByteDance ADFE Team
ByteDance ADFE Team
ByteDance ADFE Team
Understanding V8 JavaScript Engine: Scopes, Execution Contexts, and Runtime Process

1. Introduction For any programming language, variable storage and access are fundamental. This article explores how a JavaScript program locates variables at runtime, governed by a set of scope rules.

2. V8 JavaScript Execution Steps Before discussing scopes, the V8 execution pipeline is introduced. After receiving JavaScript source, V8 first parses it into an Abstract Syntax Tree (AST); subsequent compilation steps (Ignition bytecode, TurboFan optimizer) generate machine code, but scope determination occurs during the AST parsing phase.

Using the V8 source hello-world.cc as an example, the article walks through the process.

Key Concepts

Isolate

An isolate is an independent virtual machine instance, possibly backed by one or more threads, but only one thread can enter it at a time.

All isolates are completely isolated from each other with no shared resources.

If not explicitly created, a default isolate is automatically created.

HandleScope

Represents the lifetime range of a JavaScript object reference.

V8 allocates objects on its heap; a Handle is a reference to a heap object. The garbage collector tracks all objects via Handles, and HandleScope manages those Handles.

Handles come in two forms: Local (scoped to a HandleScope) and Persistent (global, not affected by HandleScope).

Context

Can be understood as an "execution context" or "environment".

Whenever program execution enters executable code, it enters a corresponding execution environment.

From the earlier source screenshot, the following steps are performed:

Define an isolate.

Within the isolate, create a HandleScope ; its lifetime determines the validity of all subsequent v8::Local objects.

Define a Context and switch into it.

Compile the JavaScript source into bytecode.

Execute the compiled bytecode within the current context.

3. Scope and Execution Context Relationship

JavaScript execution consists of two phases: the interpretation phase (lexical analysis, syntax analysis, scope rule determination) and the execution phase (creation of execution contexts, function code execution, garbage collection).

Differences

Scope is an abstract set of rules for locating variables based on identifiers; nested scopes form a scope chain.

Execution context is the runtime environment created before a function runs.

Scope is fixed during the AST parsing stage, while execution context is created during execution and may change.

var a = 10;
function fn() {
    var b = 20;
    function bar() {
        console.log(this.b); // 200
        console.log(a + b);   // 30
    }
    return bar;
}
var x = fn(),
    b = 200;
x();

The output (200, 30) shows that this.b depends on the this binding from the execution context, while b is resolved via the scope chain (bar → fn → global).

4. Detailed Function Execution Flow

Using another example, the article walks through each step of creating and managing execution contexts, the execution stack, variable objects (VO), and activity objects (AO).

var scope = "global scope";
function checkscope() {
    var scope = "local scope";
    function f() {
        return scope;
    }
    return f();
}
checkscope();

1. Global code execution creates the global execution context and pushes it onto the execution stack.

ECStack = [
    globalContext
];

2. Global context initialization sets up the variable object (VO) and binds this to the global object.

globalContext = {
    VO: {
        global: window,
        scope: undefined,
        checkscope: reference to function checkscope
    },
    Scope: [globalContext.VO],
    this: globalContext.VO
};

3. During the creation of checkscope , its [[scope]] is set to the global VO.

checkscope.[[scope]] = [
    globalContext.VO
];

4‑14. The article continues step‑by‑step, showing how each function call creates a new execution context, how the activity object is built, how the scope chain is formed, how LHS/RHS lookups work, and how contexts are popped from the stack until the program terminates.

JavaScriptRuntimeV8engineScopeExecutionContext
ByteDance ADFE Team
Written by

ByteDance ADFE Team

Official account of ByteDance Advertising Frontend Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.