JavaScript/TypeScript Internals: Execution vs. Lexical vs. Variable Environment

Brennan D Baraban
7 min readMay 6, 2019

--

Forever committed to the elusive quest of becoming a true full-stack developer, of late I’ve been building up my front-end skill-set, learning the one and only JavaScript.

Naturally, initial work in a new coding tool revolves around adapting to different syntax, data types and methods. Yet, true mastery of a programming language requires a deeper understanding of the internals behind code you write. In this, my first blog post focusing on the language, I’d like to dive into an important fundamental concept driving how the JavaScript engine works — execution environment.

TERMINOLOGY

In referencing behavior of the JavaScript engine, I reference any of the major implementations, whether it be Firefox’s SpiderMonkey or Chrome’s V8 on the browser-side, or Node.js on the server-side. The internal behavior of the three is identical for the purposes of this article.

All information discussed in this article is specific to the ES6 standard.

Finally, note that while I will be describing behavior of the JavaScript engine, code will be exemplified in TypeScript. TypeScript is a static-typed superset language that compiles to JavaScript. Again, the internal behavior of the two is identical, but TypeScript is more verbose for both type safety and exemplary purposes.

EXECUTION ENVIRONMENT — WHERE CODE RUNS

In its most basic definition, the execution environment of a JavaScript script describes where the code runs. For instance, take a look at the following code that helps our man Cap save some breath:

When this script is run, the JavaScript engine creates a global execution environment. This environment is stored as the [[GlobalEnv]] property of a larger initial realm record. Do not worry too much about the realm record — for our purposes, simply know that that all JavaScript code is initially associated with a realm record where it is stored with the global environment as well as other associated state and resources. Do takeaway, however, that the global execution environment represents the overarching environment within which the the script will be executed.

https://imgur.com/o4ZfVz6

In addition to the global execution environment, the JavaScript engine instantiates a call stack. The call stack is a last-in-first-out data structure used to store and keep track of running functions, or processes.

Since JavaScript is a synchronous language (ie. only one function can be run at a time), it must be able to keep track of which function is running at any point in time. This is where the call stack comes in. When a function is run, it is pushed on top of the global call stack; once the function terminates, it is popped off. The JavaScript engine knows a script has finished when there are no remaining processes left on the call stack.

The first function pushed onto the global call stack is the global execution thread, global() (or main(), in other descriptions). This thread is analogous to main() functions in C programs, and refers to the overarching process that is the execution of the JavaScript script.

https://imgur.com/YFTiAdG

Finally, after instantiating the call stack, the JavaScript engine sets aside global memory, where it stores variables globally accessible across the entire script.

An important concept to keep in mind when thinking about global memory is the order in which it is stored relative to code execution. When executing a given script, the JavaScript engine reads the code top-to-bottom, in its entirety, first, before running any functions whatsoever. This initial read-through, a process referred to as hoisting, serves the purpose of moving variables to the top of their scope.

In our example script, the JavaScript engine initially hoists three variables in its global memory — the string group, the function assemble, and the result phrase. Note that phrase is currently stored as undefined because no functions have yet been executed — the engine has merely read through the initial script, and phrase, being the result of a function, is yet to be defined.

https://imgur.com/3dNAiqU

LEXICAL ENVIRONMENT — EXECUTION INCEPTION

The JavaScript engine has initially read through the entire script, creating a global execution environment with a corresponding call stack and hoisted global memory. Now it proceeds to determine the value of phrase through execution of the function assemble(group). In doing so, it pushes the call to assemble() onto the global call stack.

https://imgur.com/1qzyNLu

In making the call to this function within the overarching global() thread, the JavaScript engine generates a lexical execution environment. Do not let this new terminology intimidate you — the lexical is simply a new, or local environment created within the global context.

The lexical execution environment is an object consisting of two parts — an outer environment reference and an environment record.

The outer environment reference is just what it sounds like — a reference to the parent environment in which the lexical one was generated. In fact, our initial global execution environment features an outer reference of its own — it’s merely another execution context, after all!

The outer environment reference of the new lexical environment is the global one; the outer reference of global environments are null.

https://imgur.com/pcWtKtN

VARIABLE ENVIRONMENT — AKA. MEMORY

The second part of the lexical environment, the environment record, is an identifier (think, object) representing the variable environment. Again, do not let this new terminology confuse you — the variable environment is truly just a fancy representation of the lexical environment’s local memory. In the environment record, the lexical environment stores variables as well as other information such as the infamous this.

We’ve already worked with one variable environment, the global environment’s memory, which stores variables globally accessible across the entire script. While the lexical environment references this global one, within the context of the lexical environment, the variable environment exclusively refers to the variables defined within the scope of the given function. In our example, this includes the received parameter group.

https://imgur.com/nGzVmXW

I know, I slipped that term in there, so allow me to clarify — the variable environment maps the local scope of a given environment. In other words, the variable environment stores those variables defined within the given working code block ({}).

WRAP-UP

After setting up its local variable environment, the lexical environment completes the execution of the call to the function assemble, using the variable group to return the string Avengers, Assemble!.

https://imgur.com/YZ4SDtP

At this point, the lexical environment has terminated its process, and back in the global call stack, the function is popped off, with the returned string being stored in the global variable phrase.

https://imgur.com/UTtlA9M

Finally, the script is completed, the global() thread is popped off the call stack, and the global execution environment is cleared. Now, an epic battle awaits!

Source: https://tenor.com/view/avengers-group-scarlett-johansson-tom-hiddleston-loki-gif-5285228

TL;DR

  • The execution of JavaScript code occurs in a global execution environment, the overarching environment in which a script is run.
  • In its call stack, the global execution environment pushes and pops currently running functions.
  • In its variable environment (think, environment record), the global execution environment stores the values of globally accessible variables.
  • Before executing any functions, the JavaScript engine hoists global variables in the script top-to-bottom, in its entirety, first.
  • After hoisting global variables, the JavaScript engine executes functions within the script in their own lexical environments.
  • Lexical environments are nested execution environments. They contain references to their parent environments.
  • Once all lexical environments have been executed and the call stack has been entirely popped, the JavaScript script has finished and the global environment environment is cleared.

RESOURCES

For reference, here is the Imgur album containing the the graphic flowchart built in this article:

And the complete filled diagram, no steps removed:

--

--