Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

cclintris
cclintris

Posted on

#"/t/javascript">#javascript#frontend#hoisting#webdev

Intro

One of the biggest reasons for writing this article is that I once heard several co-workers talking about hoisting in JS, which is, state lifting. It was really difficult for me to understand their perspectives at that time, so I started to search for related articles on the Internet. It turned out that this does not seem to be something that can be figured out in a moment, so I started this blog, hoping to crack another knowledge point of JS.

What is Hoisting actually?

Without further ado, let's look at an example. What happens if you try to access a variable that hasn't been declared?

console.log(a);// ReferenceError: a is not defined
Enter fullscreen modeExit fullscreen mode

We find that the browser will prompt you with areference error, telling you thata is not yet defined. But what if?

console.log(a);// undefinedvara;
Enter fullscreen modeExit fullscreen mode

How can this happen? Logically speaking, the code runs line by line, and the error should beReferenceError? Why is itundefined?

This is because of the hoisting of JS, state lifting. Lifting is reflected in the fact that the linevar a is "lifted" to the top. So in fact, when the JS engine(V8) parses this code, it actually works like this:

vara;console.log(a);// undefined
Enter fullscreen modeExit fullscreen mode

But note! It is just imagination, not that the JS engine physically moved the code!

So what if I change the code to look like this?

console.log(a);// undefinedvara=10;
Enter fullscreen modeExit fullscreen mode

The is stillundefined, but why? Shouldn'tvar a = 10 be lifted to the top, thus logginga out should be 10?

Well another concept is introduced here:only the declaration of variables will be promoted, and the assignment will not.

So actually the code above looks like this in the eyes of JS engines:

vara;console.log(a);// undefineda=10;
Enter fullscreen modeExit fullscreen mode

So in fact,var a = 10 can be decomposed into two steps. First,var = a will be lifted first, which is thedeclaration part, and the second stepa = 10 will stay and not participate in the hoisting process.

For now, everything is easy, but then the chaos may start. Take a look at the following example:

functionfunc(h){console.log(h);varh=3;}func(10);
Enter fullscreen modeExit fullscreen mode

I thought it was ridiculous at first, what is there to say, isn't it?

functionfunc(h){varh;console.log(h);h=3;}func(10);
Enter fullscreen modeExit fullscreen mode

So of course the output will beundefined! Well, the actual result immediately slapped in the face, and10 is the output.

In fact, the process of conversion and hoisting is correct, but the only thing is we forgot to call the function. So it's actually like this:

functionfunc(h){varh=10;varh;console.log(h);h=3;}func(10);
Enter fullscreen modeExit fullscreen mode

But it's still a bit strange, because even so,var h is called again before taking the value ofh without assignment. So it should still beundefined?

Well, let's try a more straightforward example:

vara=10;vara;console.log(a);// 10
Enter fullscreen modeExit fullscreen mode

Using the above decomposition method, it can actually be seen as this:

vara;vara;a=10;console.log(a);
Enter fullscreen modeExit fullscreen mode

This outputs10, which is acceptable. At the time, my feeling was, what the hell is going on! Where did so many unreasonable rules just come from? Who can figure it out? Just first bear with it and look at one last example:

console.log(a);vara;functiona(){}
Enter fullscreen modeExit fullscreen mode

According to the decomposition method above, we want to decompose it into this:

vara;console.log(a);functiona(){}
Enter fullscreen modeExit fullscreen mode

The output should beundefined right? The result is another big slap in the face, outputting[Function: a]. At this point I was already hitting myself. It turns out that in addition to the concept of state lifting invariable declaration assignments,function declaration also applies. Moreover, the matching priority of state promotion of function declaration is higher than that of variable declaration. So, in fact, the above code should be imagined like this:

// Prioritizedfunctiona(){}vara;console.log(a);
Enter fullscreen modeExit fullscreen mode

Having said so much, let’s sort it out a little:

1. Both variable declarations and function declarations will participate hoisting processes2. Variables are promoted only by declaration, not by assignment3. Don't forget that there are also passed parameters in the function
Enter fullscreen modeExit fullscreen mode

let const & hoisting

Have you noticed that when the concept of hoisting was introduced above, the keywordvar was used to declare variables. However, everyone knows that ES6 introducedlet andconst, and the mainstream no longer recommends the use of var, but instead uses let with const.

Forlet and const, in fact, the concept of hoisting is similar. Let's take a look at some examples here.

console.log(a);leta;// ReferenceError: a is not defined
Enter fullscreen modeExit fullscreen mode

Magic! The output isReferenceError: a is not defined. Well, this is actually more common sense, right? But does that mean that there is no such thing as hoisting when usinglet to declare functions? If so, it would be great, but unfortunately, it is not. Take a look at the example below:

vara=10;functionfunc(){console.log(a);leta;}
Enter fullscreen modeExit fullscreen mode

Iflet has no state hoisting, then the output should be 10, right? Because there isvar a = 10 outside, and thenlet a has no hoisting. Wrong again, the output isReferenceError: a is not defined. So in fact,let is also hoisted, but the process behavior oflet promotion may be different from that ofvar, so it seems that there is no hoisting at first glance. As for how it works, we will discuss it later.

Pause here. If you just want to know just a little bit about what hoisting is, you can actually stop here, because in fact, make good use and take advantage oflet const, and then declare assign your variables properly, you'll be just fine. But if you want to understand more thoroughly and deeper, just stick with me and keep reading. Next, we first discuss two important questions about hoisting.

  1. Why hoisting?
  2. How exactly does hoisting work?

Why hoisting?

Reviewing some of the rules and concepts of hoisting mentioned above, we can feel some of the benefits it brings. To answer this question, you can think from the opposite angle: "What would happen if there was no hoisting?"

  1. Without hoisting, we would have to declare a variable before using it. But this is actually very good. After all, everyone writes like this when programming. I suppose no one will code and at meantime have the thought of JS's hoisting mechanism, then don't declare variables and use them directly, right? So this is actually good.

  2. Without hoisting, it also stipulates that when we use a function, it must be declared and defined above. At first glance, it seems that there is nothing wrong with it, but it is actually a little troublesome. Because, this means that only by putting every function on top, can you completely ensure that any function called below can be executed normally. Now that's a little messy, isn't it?

  3. The last point is more interesting. Without hoisting, we wouldn't be able to call each other between functions. What does this mean? Take a look at the code below:

functionloop_1(){console.log("loop 1");loop_2();}functionloop_2(){console.log("loop 2");loop_1();}
Enter fullscreen modeExit fullscreen mode

This code is not difficult to understand.loop_1 andloop_2 call each other. But there is a problem, if there is no hoisting, how canloop_1 be aboveloop_2, and at the same time,loop_2 is also aboveloop_1. This code wouldn't work without hoisting.

As a conclusion, hoisting is to solve these problems!

How exactly does hoisting work?

First of all, we need to introduce a concept, theExecution Context of JavaScript, which is abbreviated asEC below. The concept ofEC is that every time a function is entered, the function will have anEC, and then theEC will be pushed onto the stack. When the function is executed, the EC will be popped out.

Image description

In general,EC stores the information of their respective functions. When the function needs something, it will go to its own EC to find it.

EachEC has a correspondingVO (Variable Object). ThisVO is what stores all the information, including the variables in the function, the function, and the parameters in the function. The mechanism for searching theVO means that:

Takevar a = 10 above as an example, the first step is to add a new attributea inVO, and then find the attribute nameda and set it to 10.

Step1: var aStep2: a = 10
Enter fullscreen modeExit fullscreen mode

Well, there are so many things in a function, how does it work to put everything into theVO of eachEC?

For parameters, it will be directly put into theVO, if some parameters aren't passed with a value, then its value will be initialized to becomeundefined. Consider the following example:

functionfunc(a,b,c){......}func(10)
Enter fullscreen modeExit fullscreen mode

The above function if called, theVO will look like this:

// VO{    a: 10,    b: undefined,    c: undefined}
Enter fullscreen modeExit fullscreen mode

If there is another function declaration in the function, it is also added to the VO, no problem. But what if the name of the function is coincidentally same as a variable name?

functionfunc(a){functiona(){......}}func(10)
Enter fullscreen modeExit fullscreen mode

TheVO looks like this:

{    a: function a}
Enter fullscreen modeExit fullscreen mode

So we can know thatfunction declarationswill take priority over thevariable declarations, just like the example above, the parameter a will be overwritten by the function a.

For the variable declaration inside the function, it will be put into the VO at the end. If there is already an attribute with the same name in the VO, the variable will be ignored directly, and the original value will not be modified.

To recap, we can think of the action of the VO mentioned above as a prerequisite work before executing a function. The order is as follows:

Step1: Put the parameters into VO, and then see if there are any incoming values respectively. The parameters are matched in the order in which they are declared. If they are not matched, they will be assigned the value undefined.Step2: Find the member methods in the function, in other words, other functions, and put it into the VO. If it has the same name as any property in the current VO, overwrite the old one.Step3: Finally, find the variable declaration in the function and put it in VO. If it has the same name as any property in the current VO, the current state will prevail.
Enter fullscreen modeExit fullscreen mode

Having said so much, let's come back to the example we mentioned above:

functionfunc(h){console.log(h);varh=3;}func(10);
Enter fullscreen modeExit fullscreen mode

So the execution of each function can actually be divided into two stages. First, it will enter the Execution Context of the function, and then start preparing its own VO. For the above example, first of all, because there are parameters in the call, a variable called h will be declared in VO first, and the value will be 10. Then because the member function is not found in the function, it remains unchanged. Finally find var h = 3, it is a variable declaration statement, so it should be added to VO, but because the VO at this time is already a variable called h, so VO does not change. So far, the VO of this function has been established.

// func() VO{    h: 10}
Enter fullscreen modeExit fullscreen mode

After creating the VO, start executing this function. When the code executes toconsole.log(h), it'll look up VO and find that there is a variable called h with a value of 10, so 10 it is. So the above question is answered, it is indeed output 10 Yes!

What if the code was changed to this?

functionfunc(h){console.log(h);varh=3;console.log(h);}func(10);
Enter fullscreen modeExit fullscreen mode

The first output will be 10 of course, and the second output will be 3.

In fact, the process of establishing VO is the same as above, so the first output will be 10 when executing, no problem. And because it changes the h in VO when it executes to line 3, the second output will of course be 3!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

cclintris
I’m currently interested about frontend web development, trying to develop full stack SDE skills. About to study security for Master's degree.
  • Education
    Carnegie Mellon University
  • Work
    Student, software developer
  • Joined

More fromcclintris

#"/cclintris/javascript-event-loop-4ha7"> #"/cclintris/css-all-about-flexbox-fg5"> CSS: All about Flexbox
#css#frontend
DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp