DEV Community

Cover image for JavaScript: Execution Context and Lexical Scope
Christina Gorton
Christina Gorton

Posted on • Updated on

JavaScript: Execution Context and Lexical Scope

When I can't fully explain something I try to go back and understand it better and often create notes. These are my notes from trying to further explain both JavaScript's execution context and lexical scope.

Global Execution Context

When your code initially runs, JavaScript creates what is called a Global Execution Context.

This context gives us access to two things right off the bat.

  • First is the global this
  • Second is a global object. In the browser this global object is the window.

Example of global this and global window object in JavaScript

In the above image I have opened a web page that only has html. There is a single paragraph element.
Yet, in the console I can type in both this and window and see that they are available to me.
The other thing to note is that currently they are the same thing.

this === window

Global Variables

In JavaScript(JS), if we create a variable like var aNewVariable = "Hello world"

this variable will now be globally available.
Let's look at the variable in the console.

Inside my JS panel I add the new variable.

Adding a variable to my JavaScript called aNewVariable

In the console I can call that variable by its name or with the global window object.
If we type in window and open that up we will also see our new variable.

calling a variable by its name and with the global window object

We are now getting in to what is referred as the Lexical Environment or Lexical Scope.

Lexical Environment

Right now our variable is scoped to the global window object. If we created extra functions or variables those would also be scoped to our global object.

The lexical scope refers to where the code is written.

Let's look at an example of where a function would not be globally scoped.

I've created a silly function called myName() that returns another function called firstName(). If I were to go to the console and type firstName() what do you think would happen?

Alt Text

We get undefined.

This function is scoped to the myName() function and is not available on the global object.

myName() is available on the global window object and when we type in myName() we now can see our firstName function and what myName returns.
Viewing our scoped function by looking at our global function myName

In this case firstName is executed inside our myName function and returns "Christina" .

More on function execution context in a bit.

Hoisting

"Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution." - Mabishi Wakio

If you have a variable declared with var or a function declaration, JavaScript will hoist it or allocate memory for it after the first run through of your code.

So if your code looked something like this:

console.log(perfectMatch)
austenCharacter();

var perfectMatch = "Willoughby"

function austenCharacter() {
  console.log("Colonel Brandon")
}
Enter fullscreen mode Exit fullscreen mode

What would you expect to see in the console?

In the console we get undefined and Colonel Brandon.

What is going on here?

When the JS engine had a first pass at our code it looked for all of the var variables and functions and allocated memory to them.

So in the case of perfectMatch when the code runs the first time it stores our variable perfectMatch as undefined. We do not actually define the variable until later on in the code but we do store the actual variable in memory.

Our function is also hoisted or stored in memory but because it is a complete function we can execute the code inside even if austenCharacter(); is called before the function is defined in our code.

Because it has been hoisted JavaScript has kept this function in memory and wherever we then place the function call austenCharacter(); no longer matters.

Local execution context

Another type of execution context happens within functions.

When a function is called a new execution context is created.

Below is a common Javascript interview question surrounding local execution context.

After looking at scope and hoisting a bit what do you think will happen when this code is run?

var perfectMatch = "Willoughby"

var newMatch = function () {
  console.log(perfectMatch + " is the perfect match") // what do we expect?

  var perfectMatch = "Colonel Brandon"

  console.log(perfectMatch + " is the perfect match") // what do we expect?
};

newMatch()
Enter fullscreen mode Exit fullscreen mode

You might expect the first console.log to be "Willoughby is the perfect match" and the second to be "Colonel Brandon is the perfect match".

What we actually get is similar to what happened in our previous example.

First we get undefined and then we get

"Colonel Brandon is the perfect match".

When our function is called it is looking inside itself for its variables.

A new execution context, in this case a function or local execution context, executed.

So within the function JavaScript looks for the var variables and then runs the console.logs.

It allocates perfectMatch to undefined initially so when we run the first
console.log(perfectMatch + " is the perfect match")

it returns undefined.

We then define it with var perfectMatch = "Colonel Brandon"

And can then see "Colonel Brandon is the perfect match" with the second console.log.

Our code:

var newMatch = function () {
  console.log(perfectMatch + " is the perfect match") // what do we expect?

  var perfectMatch = "Colonel Brandon"

  console.log(perfectMatch + " is the perfect match") // what do we expect?
};
Enter fullscreen mode Exit fullscreen mode

A representation of our code after hoisting:

var newMatch = function () {
  var perfectMatch = undefined // our hoisted variable
  console.log(perfectMatch + " is the perfect match") // So now this console.log is undefined

  var perfectMatch = "Colonel Brandon" // we now define our variable as "Colonel Brandon"

  console.log(perfectMatch + " is the perfect match")
// Now we can console.log our newly defined variable: 
// "Colonel Brandon is the perfect match"
};
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
aleksandar15 profile image
Aleksandar15

Very well, except that I thought you were also gonna touch upon the let (and/or const) keyword for declaring a variable, thats also if we, in a given interview, mentioned it they would think more highly of ourselves that we know the difference. That is let variable won't let you access it before its initialization. Although hoisted, let is not initialized to a default value (of undefined).

As in your example, it would work similarly if we were to declare let perfectMatch; (gets initialized to undefined) and if we define (initialize) it after the function console.log(), it would still be undefined. But, declaring it after its call -> let throws an ReferenceError and that's why let is more helpful in debugging our code.

Collapse
 
bhatnagardivyanshu profile image
Divyanshu Bhatnagar

Very well explained. Surprised to see no comments so adding one!

Collapse
 
coffeecraftcode profile image
Christina Gorton

Thank you 💜