“Hoisting” doesn’t really exist in JavaScript! according to ECMAScript
Tl;dr — I’ve heard about hoisting and that the Javascript engine moves code to the top. I’ve also heard that the JavaScript engine works in phases. But it’s not really cool to say “I’ve heard that…”, So I decided to go through the ECMAScript Specification and find some proofs so that I could know how it really works.
Intro🍿
First of all, No, “Hoisting” doesn’t really exist in JavaScript. Wait, What do you mean doesn’t exist?! You all probably have heard a conversation like this: You know what “Hoisting” is? Yeah, it’s when variable and function declarations are physically moved to the top of your code!
Well, I’ve heard that as well and I’ve also heard that the JavaScript engine works in phases and that is how what is explained as “Hoisting” happens. But saying “I’ve heard that…” wasn’t enough anymore, so I decided to go through the ECMAScript Specification and finally understand what’s going on.
Let’s Dive 🤓
First of all, let’s see an example of “Hoisting” to be sure we all understand what we’re talking about.
console.log(myVariable) // undefined
var myVariable = 2
This is the most simple example of that behavior that is always explained as “Hoisting”. In short, what is really happening? The engine runs in two phases (at least). Creation Phase and Execution Phase. That’s why you can access myVariable before you assign some value. Note that you can access it but it’s undefined. Ok, stop ✋! Let’s back it up.
Everything started when I found this mention in MDN web docs, which says that “Hoisting” doesn’t really happen (code is not moved to the top).
After reading this I decided to dive into the ECMAScript Specification and find some proofs. Well, I found this section 10.4, which actually talks about steps (phases) to create the execution context.
As you can see in section 10.4.1 before the engine executes your code, it has to go through a special step Declaration Binding Instantiation, which describes how “Hoisting” really happens.
In general, On entering every execution context (before the code execution), there is a special object for each execution — VariableEnvironment, which is filled with the following properties: — ( VE = VariableEnvironment )
1for function parameter (formal parameters) — set a property on the VE object with a name and value of the parameter. For parameters that were not given a value — a property of VE with the name of the parameters will be created with the value undefined.
2for function declaration — set a property on the VE object with a name and value of the function-object. If the VE already contains a property with the same name, its value and attributes are replaced.
3for variable declaration (var) — set a property on the VE object with a variable name and its value to undefined. If the variable name is the same as the name of the already declared formal parameter or a function, the variable declaration does not change the existing property.
Example
We can take the previous example again and explain step by step based on the phases we talked about before.
1. console.log(myVariable) // undefined
2. var myVariable = 2
- Creation Phase (Declaration Binding Instantiation): as we said before, for each execution (global or function execution) the engine creates a special object VariableEnvironment, which holds all variables and functions for this execution. In line 1 we can see an execution operation so the engine continues to line 2 and sees a variable declaration so it binds the variable name “myVariable” to the VariableEnvironment object and sets its value to undefined following the ECMAScript instructions.
- Execution Phase: In this phase, the engine goes through the code again only this time it executes the code. In line 1 we can see the console.log(myVariable) operation so the engine executes this line. Because the engine created and filled the VariableEnvironment before, the variable “myVariable” exists. That’s why we can access and log this variable before we assign some value. In line 2 the engine assigns the value 2 to “myVariable” which is defined in the VariableEnvironment object.
Conclusion
Well, It’s a simple concept and I’m pretty sure that a lot of you already knew the concept of phases, but I think it is interesting to find some proofs for these phases and what they do by digging into the ECMAScript Specification.
Now you can also stop saying that you’ve heard how “Hoisting” and start saying I know how it works.
Note: The engine phases I described above are simplified for the purpose of the explanation. Engine implementations can change from browser to browser and can have more steps and complexity.
If you’re wondering about “Hoisting” in context of Let and Const, you can read that article: Advanced JavaScript ES6 — Temporal Dead Zone, Default Parameters And Let vs Var — Deep dive!
Please post any feedback, questions, or requests for topics. I would also appreciate 👏 if you like the post, so others can find this too.
you can follow me on Twitter — Github— @Lidor Avitan
Thanks, and see you next time!💛