Hoisting and variable scoping in JS

Also, what is Script scope in Chrome?

Sakshi Shreya
6 min readNov 2, 2019

This time, I am writing about a very common topic but still, I have seen people who don’t know about this. So, I am just trying to be helpful for the community.

A lot of people use the browser’s debugging tools which is great. We should, of course, use them. But the concern here is if you are using them, you should know certain things that you are wrong about.

Okay, let me be a little more clear. While development, we all know that the code changes very frequently throughout the application. Sometimes, we add debugging points (breakpoints) at some places in the code where they are required. But later we realize that since the code changed, the debugging points are not present where we placed them. A lot of times I have noticed that they start pointing on the line where we are declaring the function. Like one of these lines:

function a() {
OR
var a = function() {
OR
var a = () => {
OR something similar, I think you got my point

Let’s say we want to find out whether our code executed some function or not. For this, we had added breakpoints inside the function in past. But now, those breakpoints have moved to one of the above lines.

My observation is, there are two kinds of people:

  1. People who know what hoisting is — They simply change the position of these breakpoints. And then debug.
  2. People who don’t know about hoisting — They see that the debugger hit that line and either they get confused or they enjoy (depends on the situation).

I think, by this time I have described the problem properly. Now, let’s try to solve this problem.

What is hoisting?

You just type it on google and you will find the answer.

Fig-1: Definition of hoisting

And this is it. This is what hoisting is. But I am not here to describe a problem statement and define the solution using an image. I am here to give a practical example.

I created a very simple code that contains two files: index.html, main.js

This code is very easy to read. That is why I am not explaining anything here.

Before running the code, I added some breakpoints in the browser.

Fig-2: Breakpoints

I have added the breakpoint line 1 because I want to check which variables are present in the scope. And you can clearly see that on the right-hand side,

  1. a1, a2, a3 are in scope but a4 and a5 are not there.
  2. a1 is defined but a2 and a3 are not.

Okay! What is happening here?

Hoisting says that before running any function, JS will scan the whole function and look for these keywords: function and var.

There is a reason why function is emphasized above. Since JS is a functional programming language, nothing can run outside a function. If any statement is written globally, it is executed in an anonymous context. Please read the context name in ‘Call Stack’ in Fig-2.

When JS encounters ‘function’, it reads the function and stores it in the current scope (without executing it). This is the reason why, if the breakpoint is present on this line, JS will always hit it and that too in the beginning. Because it is currently reading that scope.

When it encounters ‘var’ it creates a variable in the current scope without its value. The value will remain undefined until the code is executed to the point where we actually define the value.

That should clarify why a1 is defined (because it is defined using ‘function’ keyword) and why a2 and a3 are undefined (because they are defined using ‘var’ keyword) and why a4 and a5 are not present in the scope (they are defined using ‘let’ and ‘const’ keywords).

WHAT IF WE RUN THIS CODE?

Code will break on line 4.

Fig-3: Error on line 4

As expected, a1 prints the function, a2 and a3 are undefined and a4 is not initialized.

If we comment line 4 and then run, we again get an error on line 5. Because a5 is also not defined. On commenting line 5, we hit the breakpoint on line 10 which is inside a1 function.

Fig-4: a1 scope

Please observe the ‘Call Stack’ at this point. Other than this, the global scope is still the same. There is a new local scope now which looks something like as shown above.

If we run again, we get an error.

Fig-5: Error on line 7

We have discussed the first three outputs. Fourth is a which is logged by the a1 function. Since a1 did not return anything, we get undefined on line 6.

Since a2 is undefined, as we saw earlier in fig-2 and 4, it is not a function right now. That is why we can’t call it right now. We will have to comment this line too.

Fig-6: After a2 is defined

At this point, we can see the difference in the scope. a2 is defined now. But it looks different from a1. Why? Simple. Because a1 is a named function and a2 is an anonymous function.

Fig-7: After all variables are defined

Observe the ‘Scope’. There is a new scope now. The ‘Script’ scope. Have you ever heard about this before? I just saw it right now for the first time. 😶😮

After some time… (What is Script Scope?)

Well, I did some research. Seems like there is nothing known as ‘Script Scope’. I could not find anything related to this.

According to my knowledge, the global scope is the scope that the window provides us. Whenever we create a global variable, it is stored in the window object. Can you see that ‘Window’ just to the right of ‘Global’? That is the name of the object of the current scope.

If you try to access window.a1 or any other variable in the global scope, you will be able to do that. But there is no window.a4 or window.a5.

Fig-8: Window variables

The variables defined using let or const are not stored in window object. But they are present in the global scope. Maybe that’s the reason, chrome is showing this new scope. And something totally different is going on in firefox. But there is no ‘Script Scope’ over there. That proves there is nothing known as a ‘Script Scope’.

Moving on.

Other than the Script Scope, now a3 is also defined. We can thus expect the output from all the logs now.

Fig-9: Complete output

Wait, it’s not over yet. I changed the JS code a little bit.

Fig-10: Local Scope

This time I have created a local function and some local variables in function a1. Observe the ‘Local Scope’ and ‘Global Scope’ now. Can you guess the answer?

Now that you know about hoisting and scoping, I hope you will now be in the first category of people who remove the breakpoints from the function definitions and use them on some other statements where they can prove themselves to be useful.

--

--