Is JavaScript really interpreted or compiled language?

Few days back a friend of mine, who is new to JavaScript was asking me if JavaScript is a compiled or an interpreted language. I was kind of surprised to receive such a question from a beginner, because generally all beginners knows JS as an interpreted language; especially when you previously worked in languages like Java, which she did.
When someone dives deep into JavaScript and started digging about V8, SpiderMonkey, JIT etc. then they start having more questions on the interpreted vs compiler part. But it was great to see that she was already in that stage.

What is the confusion?

Well, in the first place, the bible of JavaScript, MDN clearly says that JavaScript is an interpreted language (it also says JIT-compiled which I will address later in the article). Still there is a question that if JavaScript is really interpreted because of the following points.

  • If interpreted then how does hoisting takes place?
  • JIT (just-in-time compiler) makes code optimizations (also create compiled versions); interpreted languages can never do that.

Any quick answer ?

The confusions and the question is valid and can not be answered by just taking the side of one, because the JavaScript spec doesn’t say anything specific on this. So let’s try to find out what JavaScript is, basing on the theoretical definitions and the workflow of JavaScript.

Compiled language vs Interpreted language

The major problem is, there is no body or organization which regulates this; i.e. the definition of compiled and interpreted language and who belongs where. Rather these two are concepts.

So according to concepts, compiled language are those who compiles the human understandable language (programming languages) to machine readable language before you run the program.
However interpreted languages are also human readable languages (programming languages) and needs a translation down to machine languages to get executed, but this translation is done at runtime. So basically you always need the interpreter installed in your environment, before you run any interpreted language; but compiled language applications can run directly once they are compiled.

Many people think that interpreted language means it will hit line number xyz in the program and that will be directly passed to CPU and will get executed; but this is not the case. All programming languages are created for humans. They are human readable. You must translate them to machine language. Compilers gets the entire codebase, translate it, does proper optimizations and creates a runnable output. Interpreters translates expressions basing on context.

What is the story of hoisting?

I expect you already know what hoisting in JavaScript is. Any variable declaration inside a function scope is always pushed to the top with a value undefined.
So is it like JavaScript engine interprets the same script file twice? Once to do all these hoisting and these kind of sorting and then again to execute the code? Or it first compiles down the entire code and then runs it? None of these two are correct.
Below is the way how declarations are handled in JavaScript.

  • Whenever v8 enters the execution context of a certain code (function); it starts by lexing or tokenizing the code. Which mean it will split your code into atomic tokens like foo = 10.
  • After analyzing the entire current scope, it parses a translated version of into an AST (for Abstract Syntax Tree).
  • Each time it encounter a declaration, it sends it to the scope to create the binding. For each declaration it allocates memory for that variable. Just allocates memory, doesn’t modify the code to push the declaration up in the codebase. And as you know, in JS, allocating memory means setting the default value undefined.
  • After that, each time it encounters an assignment or an evaluation, it asks the scope for the binding. If not found in the current scope, it goes up into parent scopes until it finds it.
  • Then it generate the machine code that the CPU can execute.
  • Finally, the code is executed.

So hoisting is nothing but the game of execution context and not code modification, unlike many websites describe it. Before executing any expression, the interpreted has to find the value of the variables from the scope which was already there since execution context was created.

Explaining JIT in JavaScript

JIT or just in time compilers are not specific to JavaScript. Other languages like Java also has these kind of mechanism to compile the code just before the execution.
The modern JavaScript engines also has JIT. Yes, they have a compiler. Now let me explain you why they need JIT and how it works in JavaScript execution.

The most important differences between a compiled and an interpreted language is; the compiled one takes a longer time to prepare itself to start executing, as it has to take care of lexing the entire codebase, making awesome optimizations etc. In the other hand an interpreted language starts executing in no time but doesn’t do any optimization of code. So each expression is translated separately. Consider the code snippet below.

In case of compiled language the sum += i part was already compiled down to machine code and when the loop will run, the machine code will be executed 1000 times.
But, in case of interpreted language, it will translate the sum += i 1000 times to machine code and execute. So there’s a huge performance drop cause the same code is getting translated 1000 times.
This is why the Google and Mozilla people brought JIT into the picture in case of JavaScript.

Compilation

In JavaScript if a certain piece of code is run more than once, it’s called warm. When a function starts gets warmer, JIT sends it for compilation and saves the compiled code with a version. From next time if the same code is executed, it skips the translation and directly executes the compiled code.
This speeds up the performance. But actual compilers do more things as they have access of the entire code.

Optimization

If a warm code become hot and hotter, JIT tries to optimize it more and more and starts saving with versions. In this process the optimizing compiler makes some assumptions about the type of the variables and the environmental values; but any unmatched assumption reverts back the optimization attempt, but with right one, it makes the code way better and performant.

To know more about JIT you can read Lin Clarks’s course on JIT.

Conclusion

So now that we know how executions actually happens in JavaScript, I think we can try to label JavaScript as compiled or interpreted language. Below are few bullet points from the article.

  • JavaScript code needs a tool (JS engine) installed in your machine (node, browser) to get executed. This is what interpreted languages want. Compiled language products are free to be executed directly.
  • Hoisting etc are not like code modification. There is no intermediate code for that. It’s just the way JS interpreter handle things.
  • JIT is the only point which can raise questions on JavaScript being an interpreted language. But JIT is not a full fledged compiler, it also compiles just before the execution. And moreover JIT is introduced by Mozilla and Google people for performance benefits in their browsers. JavaScript or TC39 never asked to do that.

Thus, even though JavaScript execution looks complicated and kind of hybrid, but I am still in the side of calling it an interpreted language rather than a compiled one or even a hybrid one which many people are calling these days.

About This Author

Hello! I am Paul Shan, a JavaScript Expert, Full Stack and DevOps Engineer cum Consultant based out of Bengaluru, India.

  • Bigopon

    Awesome tutor job 🙂
    I enjoyed it.