How JavaScript works? – Behind the scenes

In this blog post we’ll be covering and understanding how JavaScript works under the hood.

Understanding how javascript works is key to becoming a good javascript developer.

In the next very first lines of this article, we’ll be having a look at the big picture. We will be answering the most obvious question that someone whose never heard about JavaScript would ask.

I know it might sound ridiculous, but yes, bear with me for a while…

What is JavaScript in the first place?

As I said, you might already know the answer to this question, but let’s have this quick recap.

JavaScript is a high-level programming language. We mean by high-level programming language that it has a high level of abstraction. Thus, it makes it easy to understand and use by humans.

Learn JavaScript from Scratch for FREE

This course will teach you modern core JavaScript from the basic fundamentals to advanced topics. It is intended to make you learn JavaScript from scratch apart from its use for frontend/backend development or whatever. It aims mainly to give you an opportunity to build a solid foundation to become a better JavaScript developer no matter

Look at the following example. Assuming that you are someone whose never worked with JavaScript, you could easily identify at the first sight that this line of code would print “Hello World”.

console.log('Hello World');

JavaScript is a single-threaded language. This means that one command gets executed at a given time. 

JavaScript is synchronous and blocking in nature. In other words, the code is executed in the order it appears, and every piece of code must finish executing before moving onto the next one. Therefore, still, asynchronous and non-blocking execution can be somehow achieved too. We’ll be having a look at this concept later on in another blog post.

JavaScript is a dynamically typed language where type-checking is done during run-time. So, programmers don’t have to care about the type of their variables while coding.

var myRandomVariable = 123;
let anotherRandomVariable = "Hi";
const two = 2;

Just a simple var or let or maybe const to declare their variables and they are all done! 

One more last thing, javascript is an interpreted language. Really? is it? Well, keep on reading…We’ll figure out by the end of this article…

Now that we have had a quick refresher about JavaScript features, the next logical question that we might ask would be,

Where does javascript run? 

Back then, JavaScript was meant to be a client-side programming language. This means that it runs only inside an internet browser, however, it’s not anymore the case these days, as JavaScript can also run on the server-side using Node.js.

So, long story short! Javascript can run anywhere as the appropriate environment to do so is there!

And talking about the appropriate environment, we mean by the so-called “JavaScript Engine”

The JavaScript Engine

A JavaScript engine, simply put, is a program which executes JavaScript code.

It has two main components, The call stack and the Heap memory.

JavaScript Engine

The call stack as the name suggests, it stacks all the functions that need be executed in your program.

On the other hand, the heap memory is the place where variables in your program are stored.

There are plenty of javascript engines out there, like V8 for google chrome and nodejs, SpiderMonkey for Mozilla and Chakra for IE just to name a few of them…

They all have been created to do one single thing which is going to be the answer to the following question…

How the JavaScript Engine executes your code?

If we want to understand how JavaScript runs, there’s one essential concept that every JavaScript developer should be aware of and that is…

The Execution context

An execution context in JavaScript is an abstraction of the environment where a particular piece of code is being executed.

Our friend, the JavaScript engine, can create two different types of execution contexts: global execution context and functional execution context.

JavaScript execution context

When the JavaScript engine starts reading the code, the global execution context gets established first. This happens in two phases: the creation phase and the the execution phase.

During the creation phase of the global execution context, four main things happen:

  1. A global object gets created first.
  2. Another object called “this” is created and bound to that global object.
  3. A variable environment space is allocated in memory for variables and functions.
  4. “undefined” gets assigned to variables declared using the “var” keyword and function declarations get also stored in memory.

The third and fourth steps are often referred to be what we call “Hoisting” in JavaScript.

As stated by the MDN Web Docs,

Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

So, the concept of hoisting allows you to do something similar to this,

console.log(number); // output : undefined
sayHello(); // output : Hello there!
function sayHello(){
   console.log('Hello there!');	
}
var number = '123';

As you might have noticed, because of hoisting in JavaScript we were able to use the function sayHello before its actual declaration as memory has been already allocated to the function declaration and initialized during the phase of “creation” of the execution context before it gets run by the JavaScript engine.

And so it was the case for the variable number. Notice that it has been used before its declaration. Therefore, we could still access it without getting any errors. Yet, its value has been initialized with the value “undefined” at line 1 where we were trying to access it.

So, this being said…One piece of advice, you should be careful when declaring your variables using the keyword “var“.

Then the next step would be the phase of execution where the JavaScript engine executes the code line by line.

Now, the same processing goes when it comes to the establishment of a functional execution context with the only difference being that during the creation phase, an “arguments” object is created instead of a “global” object. Moreover, the value of “this” is defined according to how the function is called ( Don’t bother yourself with this right now. We’ll be having a separate blog post to explain this concept).

Be aware that the global execution context is created at the beginning of the execution of a JavaScript script whereas a functional execution context gets created whenever a function is invoked.

How the JavaScript engine tracks execution contexts?

As we’ve mentioned previously, the main component of the JavaScript engine that is responsible for the execution of functions is the call stack. In other words, it is the mechanism that allows the JavaScript engine to ensure the execution of all executions contexts. Only one execution context at a time and in order because, remember, JavaScript is synchronous and single-threaded.

Check this following short video that illustrates the simulation of the execution of a simple JavaScript code to have a better understanding of how JavaScript works behind the scenes…

This video is part of my premium course on Udemy “Learn JavaScript from the Ground up

Is JavaScript an interpreted language? 

First thing first and before we dive into answering the question. Let’s talk a bit about the concept of interpreters and compilers.

Despite the fact that both of them serve a similar purpose which is helping your machine to run your programs. They are different in the way they operate and deal with the code.

Interpreter vs compiler

The interpreter goes through the source code line by line, translates each line into machine code and executes it on the fly. This makes the execution slow, but easy to debug.

On the other hand, the compiler translates the entire code into an optimized machine code that is ready to be executed. And this makes the execution faster compared to interpreters, however, debugging can be really tough.

Now, comeback to the topic of JavaScript and how does it relate to all of that.

Back in the time, JavaScript used to be interpreted. Until 2008, when google released the first version of the V8 JavaScript engine.  They implemented a JIT compiler in order to make of the JavaScript execution faster. 

JIT compiler

JIT compilation, which combines the best parts of both interpreters and compilers, stands for Just-In-Time compilation. Basically, all modern JavaScript engines nowadays implement JIT compilers.

So these modern engines take the source code, parse it and send it to the interpreter.

The interpreter then generates the bytecode ( which is an abstraction of the machine code) and starts executing it right away with no delay. Till now, nothing really fancy!

But here’s the thing, during runtime information from the previous execution of code is gathered, and as a result, optimizations might be required relying on the collected information to enhance performance and gradually increase the speed of execution,  the one responsible for this kind of tasks is the JIT optimizing compiler which is used only when needed. This is why it’s called Just-in-Time compilation.

The take away from the above lines is that we can tell, as a general rule, that a programming language and its implementation are two totally separate things.

Now, let me recall the question, is javascript interpreted? 

Waiting for your answers in the comment section below…

And by the way, if you are still intrested in learning JavaScript, I have released an entire course for you. You can check it out from here.

Thanks for your time, and see you around in another great tutorial.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *