JS Quirks

At Supermart.ng, my responsibilities include writing and managing front-end code. I’m talking HTML/CSS/SASS and JavaScript. Learning HTML/CSS/SASS was easy but JavaScript on the other hand was a bit daunting when I joined. I had little experience in its use.

Everything was going on smoothly until one fateful day when I had to fix a bug. I needed to perform some computation on a variable and have its value available to other functions.

The code I wrote is similar in structure to the following.

function b() {

console.log(num*2);

}

function a( ) {

var num = 2;

b( );

}

a();

var num = 1;

Let’s take a moment to break this down, following my thought process at the time.

- We have a function ‘b’ that logs the product of num and 2 to the console. (separation of concerns is good programming practice isn’t it).

- Another function ‘a’ , assigns 2 to the variable num and calls b().

- We call a().

- We create variable num globally and assign 1 to it.

When we run the code and function ‘a’ is called, we expect num to have the value of 2, and when function b executes from within function a, we expect the result to be 4, right? Wrong. We get 2 instead.

I had introduced a bug into the system. After several fruitless attempts at debugging, I started to feel feverish. I thought of all the catastrophic things that could happen if this bug slipped into production. A particularly scary thought crossed my mind…

Raphael is my boss by the way

Maybe I should just switch career paths to farming jeje, I cannot come and kill myself.

With no understanding of how JavaScript works under the hood, debugging this kind of errors could be near impossible.

So, let’s try to understand what happened here.

When a JavaScript program runs, a global execution context is created that sits at the base of an execution stack. Within every execution context is a memory space where variables and function definitions are stored during the creation phase.

Creation phase is the point when the program runs before it actually executes any code inside.

A link to the outer environment known as the scope chain is also created within every execution context.

In JavaScript every function invocation creates a new execution context with its own memory space (also known as variable object), as well as its own scope chain.

When function ‘a’ is invoked our stack looks like something of the following:

Since JavaScript is synchronous and single threaded in nature, it therefore means at this point only function ‘a’ is being executed. It has its own local variable within its memory space that the outer environment knows nothing about.

Makes perfect sense right? Yep, so far so good.

When function b is invoked, yet another execution context is created and executed. Now this is the part where it gets tricky.

This is what happens. When the console.log function within function b runs, it looks for the variable num within function b’s execution context. As we can see in the diagram above, there’s nothing in the memory space of its execution context. Since num cannot be found in this execution context, the JavaScript engine looks through the scope chain to find a reference to the outer environment.

Function b’s outer environment, surprisingly (at least to me at the time), happens to be the global environment and not function from whence it was called. Why? Because function b sits lexically in the global environment.

‘Lexically’ in this context means where code is defined i.e. where it sits physically.

When num wasn’t found in function b’s execution context, it checked the global execution context and… voila, there it sits.

Take another peek at our delightful execution stack above.

To resolve this problem we simply need to change the lexical location of function b.

After refactoring our code we should have the following.

function a( ) {

var num = 2;

function b() {

console.log(num);

}

b( );

}

a();

var num = 1;

Now our execution stack should look something like this

Function ‘a’ is now function b’s parent environment. When num is not found in function b’s execution context, the JS engine using the scope chain looks in function ‘a’ execution context and consequently finds var num. Problem solved.

Bottom line

Understanding JavaScript’s scope, context and execution context is fundamental to every programmer that intends to code actively with JavaScript. These concepts lend to some of the most powerful design patterns JavaScript has to offer but it is also a tremendous source of confusion among developers, and understandably so.

P.S. I haven’t switched to farming yet, but I have a friend who’s a pastor now. JavaScript must have thrust the fear of God upon him.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.