completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Execution contexts

Understand the global execution context, creation and execution phases, variable environments, and how globalThis and top-level this differ in scripts and modules.

By the end, you can
  • 01
    Describe the global contextExplain the first workspace JavaScript makes before your file runs.
  • 02
    Tell setup from runningPredict what the creation phase sets up and what the execution phase changes.
  • 03
    Compare scripts and modulesTell when a top-level name goes on globalThis, and what top-level this is.

Every run starts with a workspace

Before your code does any work, JavaScript gets ready to run it. It makes a workspace for that code. This workspace is called an execution context. JavaScript makes one for the whole script, and one more for each function call.

An execution context keeps three things together:

  • The names the code can use, like its variables and functions, with their values.
  • An outer link: where to look next when a name isn't found here.
  • The value of this for the code that is running.

This lesson uses a simple model of contexts. The model tells you correctly what your code will do. Real engines store things in faster, more complex ways, and later lessons show the exact rules from the specification. You already know variables, local variables inside functions, closures, and strict mode. Execution contexts show how those ideas fit together while code runs.

Real-life analogyAn execution context is a desk for one job

In an office, every job gets its own desk. Before the work starts, someone puts three things on the desk. There is a notepad with the names the job needs. There is a note saying which room to ask if a name isn't on the notepad. And there is a name tag saying who the job is for. Then a person sits down and does the work, writing new values on the notepad as they go.

In real life: A desk set up for one job
In JavaScript: One execution context
In real life: A notepad of names and values
In JavaScript: The names the code can use, with their values
In real life: A door to the room outside
In JavaScript: The outer link: where to look next
In real life: A name tag that says who the job is for
In JavaScript: The value of this
The short version

An execution context is the workspace JavaScript makes to run the whole script or one function call. It holds the code's names and values, an outer link, and a this value.

The global execution context

FIRST CONTEXT

The first context JavaScript makes is the global execution context. Code that is not inside any function is called top-level code, and it runs here. Functions written at the top level are also set up here.

When you call a function, JavaScript makes a new context for that call and puts it on top of the global one. When the function finishes, that context is removed. The next lesson, The call stack, looks closely at this adding and removing.

The global context comes with the global object. This is an object that holds things every script can use, like console and setTimeout. Browsers call it window. Browsers, Node.js, and every other place that runs JavaScript also call it globalThis. The global object is only part of the global context. In a normal script, a top-level let or const is a global variable, but it does not become a property of globalThis.

Real-life analogyThe global context is the front office

A company has one front office that everyone can find. Some names are on a public list at reception. Other notes stay private inside the office. When a visitor comes for a meeting, someone sets up a desk for that visitor. When the meeting ends, the desk is cleared.

In real life: The building's front office
In JavaScript: The global execution context
In real life: The public list at reception
In JavaScript: Properties of the global object, such as a top-level var in a normal script
In real life: Private notes kept in the office
In JavaScript: Top-level let and const
In real life: A short-term desk for one visitor
In JavaScript: The context for one function call
A script that starts in the global contextPop out in the code editor (opens in a new tab)JavaScript
function announce(label) {  let message = label + " #" + count;  console.log(message);} var status = "draft";let count = 1;count = count + 1;announce("Article");

This code starts in the global context. Lines 1 to 3 write the function announce, but its body does not run yet. It runs only when line 9 calls the function. That call makes a new context, with its own parameter label and its own local variable message.

A function call gets its own local variablesPop out in the code editor (opens in a new tab)JavaScript
function greet(user) {  const message = "Hi, " + user;  return message;} console.log(greet("Asha"));

Line 6 calls greet("Asha"). JavaScript makes a context for this call. In it, the parameter user holds "Asha". Line 2 makes a local variable, message, that joins the greeting and the name. The function returns it, so line 6 prints Hi, Asha.

Two contexts in a pile. At the bottom is the global context, made before line 1 runs. It holds greet. On top is the context for the call greet with Asha. It holds user and message, and it is removed when greet returns.newest on topgreet("Asha")holds user and messageremoved when greet returnsglobal contextholds greetmade before line 1 runs
The global context is made first. When line 6 calls greet("Asha"), JavaScript makes a new context for that call and puts it on top. The new context holds user and message. When greet returns, its context is removed.

Creation phase vs execution phase

STEP THROUGH

Every context is used in two steps, called phases:

  • The creation phase comes first. JavaScript reads the code and sets up its declarations. A declaration is a line that makes a name, like var x, let x, or function f() {}. No line runs yet.
  • The execution phase comes next. JavaScript runs the lines one by one, from top to bottom, and changes values.

In the creation phase, each kind of declaration is set up in its own way:

  • A function declaration is ready to call right away.
  • A var name is made and given the value undefined.
  • A let or const name is made, but it has no value yet. Reading it before its line runs throws a ReferenceError.

The table below uses the script from above. Read each row as: this name, made this way, holds this before any line runs, and this after the script finishes.

What the global context holds before and after the script runs
NameHow is it declared?What does it hold before any line runs?What does it hold at the end?
announcefunctionready to callfunction announce
statusvarundefined"draft"
countletexists, can't be read yet2
A function declaration is ready before its linePop out in the code editor (opens in a new tab)JavaScript
console.log(greet()); function greet() {  return "ready";}

Line 1 calls greet, but the function is written below it, on line 3. This still works. The creation phase already made the function ready. Its body runs only because line 1 calls it, so the output is ready.

Now step through the script from above. The first step shows what the creation phase set up. The later steps are recorded from real JavaScript that runs the same code and prints the same output.

Step through the two phases of a small script
Step 0 of 9Ready
Your turn: follow the blue line

The first step shows what the creation phase set up. Then step through the execution phase, and watch the function call add a new context.

Running in
  1. script
Next: line 1
  let message = label + " #" + count;  console.log(message);} var status = "draft";let count = 1;count = count + 1;announce("Article");
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

A later lesson, Hoisting & the temporal dead zone, goes deeper. For now, remember one rule: all declarations are set up before the code runs, but only some of them can be used before their own line.

Reading early: var vs letPop out in the code editor (opens in a new tab)JavaScript
console.log(ready);var ready = "yes"; console.log(count);let count = 1;

Line 1 reads ready before line 2 gives it a value. The creation phase already made ready with undefined, so line 1 prints undefined. Line 4 reads count before line 5. The name count exists, but it has no value yet, so line 4 throws a ReferenceError. The time between the start of the code and the let line is called the temporal dead zone, or TDZ. During the TDZ, the name exists but can't be read.

Left: console.log(ready). The var box ready already holds undefined, so the log prints undefined. Right: console.log(count). The let box count is empty and not ready until line 5, so reading it throws ReferenceError.console.log(ready)undefinedreadyprints undefinedconsole.log(count)empty until line 5countReferenceError
Both boxes are made before any line runs. The var box starts with undefined in it, so an early read prints undefined. The let box stays locked until its own line runs, so an early read throws a ReferenceError.
Tested on real JavaScript

The lesson tests run this code as a real script. It prints undefined for the early var read. Then it throws a ReferenceError on the early let read.

Creation phase or execution phase?
  • A function declaration becomes ready to call before its line runs
  • var count exists and holds undefined
  • let count exists but has no value yet
  • count = count + 1 changes the stored value
  • console.log(message) prints
  • Calling a function adds a new context
Try it yourself
0 of 6 correct

Sort each event by when it happens. Use the step-through above if you're not sure.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Variable environments

LOOKUP

A context keeps its names in lists. You will hear two words for these lists. In this lesson they mean:

  • The variable environment is the list that var names go into. There is one for each function call, and one for the global context.
  • A lexical environment is a list for let, const, and parameters. "Lexical" means "decided by where the code is written". Each block, like the inside of an if, can get its own lexical environment.

The specification has a more exact version of these lists, called Environment Records. A later lesson covers them.

Each list also has an outer link. It points to the list around the place where the function was written. When code uses a name, JavaScript looks in the current context first. If the name isn't there, JavaScript follows the outer link and looks there, and so on. The Closures lesson uses this same outer link: a function remembers the place it was written.

Context inspector: pick a moment
The code being inspectedPop out in the code editor (opens in a new tab)JavaScript
const room = "global";function outer() {  const room = "outer";  inner();}function inner() {  const task = "inspect";  console.log(room, task);}outer();
Contextsnewest on top

Global execution context

outernone
thisglobalThis in a classic script
outerfunction
innerfunction
roomno value until line 1
Step 1 of 3Global context ready

The global context is made first. The functions outer and inner are ready to call. room exists, but it gets its value only when line 1 runs.

A teaching model of the contexts at each moment. It shows the rules your code follows, not the engine's real memory.
An inner name can hide an outer namePop out in the code editor (opens in a new tab)JavaScript
const room = "home";function showRoom() {  const room = "kitchen";  console.log(room);} showRoom();console.log(room);

Line 3 makes a second room, inside showRoom. When line 7 calls the function, line 4 finds the inner room first and prints kitchen. The inner name hides the outer one. This is called shadowing. Line 8 runs in the global context, so it prints the outer home. JavaScript follows the outer link only when the current context has no name that matches.

Lookup follows where a function was writtenPop out in the code editor (opens in a new tab)JavaScript
const place = "home"; function makeGreeting() {  const user = "Asha";  function greeting() {    return user + " at " + place;  }  return greeting();} console.log(makeGreeting());

Line 6 is inside greeting, and it needs user and place. Neither is made inside greeting. So JavaScript follows the outer link to makeGreeting, where greeting was written, and finds user on line 4. It follows the next outer link to the global context and finds place on line 1. Line 11 prints Asha at home.

Three contexts in a row, joined by arrows called outer. greeting() needs user and place. It follows its outer link to makeGreeting(), where it finds user is Asha. Then it follows the next outer link to the global context, where it finds place is home.greeting()needs user, placeoutermakeGreeting()user = "Asha"found userouterglobalplace = "home"found place
greeting() has no user or place of its own. It follows its outer link to makeGreeting(), where it was written, and finds user. The next outer link leads to the global context, where it finds place.

The outer link depends on where a function is written, not on who calls it. Calling a function from inside another function does not change where it looks.

The table below sums up the three words. Read each row as: this word, what it means, which names go there, and where you will notice it.

Variable environment, lexical environment, and outer link
WordWhat is it?Which names go there?Where do you notice it?
Variable environmentIt is the list of names for one function call, or for the global context.Names made with var go there.You notice it in function scope and in global var names.
Lexical environmentIt is a list of names tied to the place the code is written.Names made with let and const, and parameters, go there.You notice it in block scope, the TDZ, closures, and shadowing.
Outer linkIt points to where JavaScript looks next.No names go there. It is a link, not a list.You notice it in nested functions and closures.

globalThis & this at the top level

INTERACTIVE

globalThis is the standard name for the global object. In a browser, globalThis === window is true in normal code. But the value of this at the top level depends on how the file is loaded. There are two ways to load a file:

  • A classic script is a normal <script> tag. Its code is not in strict mode unless it asks for it. Code that is not in strict mode is sometimes called sloppy mode.
  • An ES module is a file loaded with <script type="module"> or with import. A module is always in strict mode.

Here is what changes between them:

  • In a classic script in sloppy mode, top-level this is globalThis.
  • In an ES module, top-level this is undefined.
  • The top-level names in a module stay inside that module. They never go on globalThis.

Inside a normal function, this works differently. It is decided by how the function is called, each time it is called. A function does not copy this from the code around it.

Left: a classic script. Its top-level var scriptVar lands in the globalThis cart, and top-level this is globalThis. Right: an ES module. The globalThis cart has no moduleVar in it, and top-level this is undefined.classic scriptscriptVarglobalThisthis = globalThisES moduleno moduleVar hereglobalThisthis = undefined
In a classic script, a top-level var becomes a property of globalThis, and top-level this is globalThis. In an ES module, a top-level var stays in the module, and top-level this is undefined.
Compare top-level and function this
Code for the chosen kindPop out in the code editor (opens in a new tab)JavaScript
var scriptVar = "var property";function scriptFunction() {  return "function property";}let scriptLet = "lexical only";const scriptConst = "lexical only"; console.log(this === globalThis);console.log(globalThis.scriptVar);console.log(globalThis.scriptFunction());console.log(globalThis.scriptLet);console.log(globalThis.scriptConst);
Classic scriptresult
Top-level thisglobalThis, in a classic script in sloppy mode
var scriptVarmakes globalThis.scriptVar
function scriptFunctionmakes globalThis.scriptFunction
let / constglobal names, but not on globalThis
Try it yourself

A classic script is a normal <script> tag. Its top-level var and function declarations become properties of the global object. Its let and const names do not.

The lesson tests check the script results by running real scripts, and the module results by loading a real ES module.

The table below compares four places. Read each row as: code running here has this this, and its top-level names go here.

this in scripts, modules, and plain function calls
Where does the code run?What is this?Do its names go on globalThis?
In a classic script, sloppy modeTop-level this is globalThis.Top-level var and functions do. let and const do not.
In an ES moduleTop-level this is undefined.No. Every top-level name stays in the module.
In a plain function call, sloppy modethis is globalThis.No. A var inside the function stays in the function.
In a plain function call, strict modethis is undefined.No. A var inside the function stays in the function.
A saved method loses its objectPop out in the code editor (opens in a new tab)JavaScript
const cart = {  label: "tea",  readLabel() {    return this.label;  },}; const read = cart.readLabel;console.log(cart.readLabel());console.log(read());

Line 9 calls cart.readLabel(). The object before the dot, cart, becomes this, so it prints tea. Line 8 saved the same function into read, without its object. Line 10 calls read() with nothing before it. This code is in sloppy mode, so this becomes globalThis. There is no globalThis.label, so it prints undefined.

What a classic script doesPop out in the code editor (opens in a new tab)JavaScript
var scriptVar = "var property";function scriptFunction() {  return "function property";}let scriptLet = "lexical only";const scriptConst = "lexical only"; console.log(this === globalThis);console.log(globalThis.scriptVar);console.log(globalThis.scriptFunction());console.log(globalThis.scriptLet);console.log(globalThis.scriptConst);

In this classic script, line 8 prints true: top-level this is globalThis. Lines 9 and 10 find scriptVar and scriptFunction on globalThis. Lines 11 and 12 print undefined, because let and const names don't go there.

What an ES module doesPop out in the code editor (opens in a new tab)JavaScript
var moduleVar = "not a global property";let moduleLet = "module lexical";const moduleConst = "module lexical";function moduleFunction() {  return "module function";} console.log(this === undefined);console.log(globalThis.moduleVar);console.log(globalThis.moduleFunction);console.log(typeof moduleLet);console.log(typeof moduleConst);

In this module, line 8 prints true: top-level this is undefined. Lines 9 and 10 print undefined, because even var and function names stay in the module. Lines 11 and 12 print string, because the module itself can still use its own names.

Which names go on the global object?
  • Classic script: `var topic = "contexts";`
  • Classic script: `function start() {}`
  • Classic script: `let topic = "contexts";`
  • Classic script: `const mode = "learn";`
  • ES module: `var topic = "contexts";`
  • ES module: `function start() {}`
Try it yourself
0 of 6 correct

Sort each declaration by what happens to it in a classic script or an ES module.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Where you'll use this

Execution contexts explain several bugs you will meet in real code:

  • A var prints undefined before the line that gives it a value. The creation phase made it with undefined.
  • A let read throws before its line. That read is in the temporal dead zone.
  • A helper function reads or changes a global variable by accident. Now it depends on the global context, which every script on the page shares.
Keep a helper's variables inside the helperPop out in the code editor (opens in a new tab)JavaScript
function scoreLabel(score) {  var prefix = "Score";  return prefix + ": " + score;}console.log(scoreLabel(9));console.log(globalThis.prefix);

Line 2 makes prefix inside the function, so it lives in the function's own context. Line 5 prints Score: 9. After the call, line 6 shows that globalThis.prefix is still undefined. This is safer than a global var, which every script on the page can read and change.

A quick checklist for bugs
  • First ask which context is running.
  • Look for the name in that context. If it isn't there, follow the outer link.
  • For this, look at how the function is called: as a method, as a plain call, with new, or as an arrow function.
  • To share a value between files, export it from a module or set a globalThis property on purpose.

Common misconceptions

"The creation phase runs my function body."

No. The creation phase only makes a function declaration ready. The body runs when the function is called, during the execution phase.

"Hoisting moves declarations to the top of the file."

No. Your code stays in the order you wrote it. The creation phase sets up names before any line runs. That makes it look as if the declarations moved up. This effect is called hoisting.

"Top-level variables are always on globalThis."

Only some are. In a classic script, top-level var and functions go on globalThis, but let and const don't. In a module, no top-level name goes on globalThis.

"Top-level this is always the global object."

Only in a classic script in sloppy mode. In an ES module, top-level this is undefined.

"The step-through shows the engine's memory."

No. It replays values recorded from real JavaScript calls. It shows the values you need to learn the rules. It does not show how the engine really stores them in memory.

Practice: trace the contexts

5 EXERCISES
Exercise 1 · Warm-upPredict an early var read

What does line 1 print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log(topic);
var topic = "contexts";

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFind the global property

    Which declaration in the starter code does not become a property of the global object?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    var scriptVar = "var property";
    let scriptLet = "lexical only";
    console.log(globalThis.scriptVar);
    console.log(globalThis.scriptLet);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeTrace a nested call

      What does inner() print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      let label = "global";
      function outer() {
        let label = "outer";
        inner();
      }
      function inner() {
        console.log(label);
      }
      outer();

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFix a global var

        Move prefix inside the function, so it can't clash with a name from another script.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        var prefix = "Score";
        function scoreLabel(score) {
          return prefix + ": " + score;
        }
          Exercise 5 · ChallengeName top-level this in a module

          In one word, what is top-level this inside an ES module?

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Make a guess before you choose. Every answer explains the rule it uses, so a wrong guess still teaches you something.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is an execution context?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this classic script print before it throws?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(topic);
              var topic = "contexts";
              console.log(count);
              let count = 1;

              Choose an answer to see the explanation.

            3. Question 3 of 7Which of these happens in the creation phase?

              Choose an answer to see the explanation.

            4. Question 4 of 7In a classic script, which top-level declarations become globalThis properties?

              Choose an answer to see the explanation.

            5. Question 5 of 7What is top-level this in an ES module?

              Choose an answer to see the explanation.

            6. Question 6 of 7What does this program print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              let word = "global";
              function say() {
                let word = "local";
                console.log(word);
              }
              say();
              console.log(word);

              Choose an answer to see the explanation.

            7. Question 7 of 7Why does this lesson call its picture of contexts a model, and not the way engines really work?

              Choose an answer to see the explanation.

            Key takeaways

            • An execution context is the workspace JavaScript makes to run the whole script or one function call.
            • The global execution context is made first. Each function call makes one more context on top.
            • The creation phase sets up declarations. The execution phase runs the lines and changes values.
            • A var name starts as undefined. A let or const name can't be read before its line: that time is the TDZ.
            • Scripts and modules differ. In a module, top-level this is undefined, and top-level names don't go on globalThis.

            Execution context, in one line.
            An execution context is the workspace for running code: the names it can use, an outer link to look further out, and the value of this.

            Up next: The call stack. You will see how JavaScript adds a context each time a function is called, and removes it when the function returns.

            CompleteFrontend Clear concepts. Working examples.