We know what are the compiled and interpreted languages, as discussed in the previous article in the series. Let's look into it in more detail.
Now let's dive deeper for you, my beautiful explorers of the JS wilderness!
Let's quickly get an overview of what these types of languages mean based on the traditional translations -
A compiled language is translated directly into native machine code (imagine a file that only contains 0s and 1s) that the processor can execute. Examples: C, C++, Rust, and Go.
An interpreted language, on the other hand, is read line by line and executed by an interpreter. Examples: PHP, Python, and Ruby.
But before we dive any deeper…
Programming languages can have both - compiled and interpreted implementations. It is not part of the language's specification but an implementation decision.
Yes, you read that right! If a language is compiled or interpreted, it's not really the language's call but the way that it has been implemented.
Let's Start The Game, Watson!
Once you eliminate the impossible, whatever remains, however improbable, must be the truth. - Sherlock Holmes
Let's take a peek into the popular compiled language C. So we write some C code in an editor (a
"sourcecode".c file) and after four steps of the compilation process- preprocessing, compiling, assembling, and linking (ignore the fancy terms for today), the compiler will create an output native code (in a
"compiledcode".out file). This compiled file is what we run to see the result of our program.
Verdict: Not Compiled!
As we know, web developers need to code stuff and run things quickly. Interpreters fit the job description perfectly here. We write a line and hit refresh - voila! Having compiler in this equation, no thanks.
Many definitions on the Internet seem to suggest its Interpreted. So let's try this example code:
Here, it works if you try. So from what we know about the interpreter, it will read line 3 and execute the function
foo and pri… wait how does the Interpreter know of the function foo which is down at line number 5? Huh.
This can't be an Interpreter job! How could it know what's coming in the next line? We can rule that out now. There is some aspect of compilation involved here, Watson!
Verdict: Not Interpreted!
Blistering Barnacles, Watson! We are out of suspects!
Wait… what if we are not in a Sherlock Holmes novel but an Agatha Christie story. Like the plot of the book censored to avoid giving out spoilers, when we are out of suspects, that means everyone is a suspect!
It's both! * gasps *
The interpreter is amazing if you need to run the code once but when it happens multiple times, it becomes significantly slower in execution as compared to the compiled native code. So there was a need to shrink this gap - enter Just-In-Time compilation over traditional static compilation!
Criminal: Just-In-Time Compiler - the best of both worlds!
It used to be just interpreters back in the day. But in order to overcome the significant inefficiency gap, the browsers started bringing in the compilers for optimizing the process better.
So in a nutshell, the compilation, in this case, happens during the execution.
Every browser has its own way of handling this but generally, a new component called a Monitor (or Profiler) is added to the translator, JS Engine. This Profiler monitors the code for redundant code and the object types.
(A Very) Basic Workflow
First, the Profiler goes over everything. It monitors the process. If a code block runs a lot of times, it is marked "hot".
In the initial run, a non-optimized Abstract Syntax Tree is generated and handed over to the interpreter to execute (will cover in future posts, for now, it's a tree representation of your code). This is to avoid any more delay in execution.
Meanwhile there is an optimization step. The "hot" code block the profiler marked earlier is sent to another optimizing compiler that converts it to a faster and more optimized version of itself after proper checks.
If all things look fine, this optimized version is sent and we have a better code. However, if there are some problems with this optimized code, JIT will deoptimize and trash this optimization, the non-optimized machine code is executed instead. FYI this has a small performance hit.
So the profiler and the compiler are working together to give you the best possible performance.
The JIT compilation makes your code faster through monitoring the code and optimizing the recurring similar code blocks. And yes, this has its own overhead added to the process but the performance gains outweigh those.
In the words of Ed Sheeran, "My, my, my, my, oh give me love" 💜