JavaScript hosting

Reference: http://www.kenneth-truyers.net/2013/04/20/javascript-hoisting-explained/

What is hoisting?

In Javascript, you can have multiple var-statements in a function. All of these statements act as if they were declared at the top of the function. Hoisting is the act of moving the declarations to the top of the function.

Hoisting versus block-scope

In most programming languages there’s something called block-scope. Basically what it means is that every variable declared within a block is only visible within that block. The following code snippet in c# shows this behavior:

In the snippet above, the variable myvar is only available inside the for-loop. That’s because c# has block-scope (just as VB.NET, Java and most other languages).

If we look at Javascript however, there are only two scopes (in ES5): global and function scope. So if we write an equivalent method in Javascript the result is quite different:

Because Javascript “hoists” the declaration of myvar to the top of the function this will actually alert ‘5’. The above code is functionally equivalent to this, which explains the behavior:

Unexpected behavior due to hoisting

As said, if you expect your variables to be block-scoped, you will get unexpected behavior and unexplainable errors. Let’s look at a few examples:

Variable hoisting

Normally you would expect the first alert to display the value of the global variable since you haven’t overwritten it yet. However, since the declaration is actually hoisted to the top it overwrites it at the start of the function.

The following code is functionally equivalent:

Function hoisting

When we are declaring functions inside a function there are two patterns available: function expressions and function declarations:

As you can see, both implementations are valid and you can call them using the same syntax. This might lead you to believe that they are in fact equivalent. However, there is a small difference when it comes to hoisting: function declarations get hoisted completely (name AND implementation) whereas function expressions get only the variable declaration hoisted and not the implementation. Let’s look at an example:

In this example you can see that, as with normal variables, just the presence of mymethod and mysecondmethod inside the function moves them to the top. However, mymethod (a function declaration) gets hoisted completely and therefore is available everywhere inside the function. On the other hand, mysecondmethod (a function expression) has only the var declaration hoisted, which prevents the global method from being seen but is not usable as a function until it’s assigned.

Best practices

From the examples, it’s obvious that the hoisting behavior Javascript exhibits can lead to confusing results and make it difficult to read code. Therefore you should follow some best practices.

What you really want when you read code, is that it clearly states what it does. Since you know it’s moving the variable declarations to the top, you should do this explicitly, so it’s obvious that they’re there. Consider the following examples (the first one is the “wrong” one, the second example show a better version):

Sidenote

For completeness sake, I need to mention that behind the scenes things are actually implemented a little bit different. The ECMAScript standard does not define hoisting as “the declaration gets moved to the top of the function”. Handling code happens in two steps: the first one is parsing and entering the context and the second one is runtime code execution. In the first step variables, function declarations and formal parameters are created. In the second stage function expressions and unqualified identifiers (undeclared variables) are created. However for practical purposes we can adopt the concept of hoisting.

Conclusion

With this post I tried to demystify some of the strange behavior you sometimes may experience. I hope by reading this and applying the concept you can avoid some frustration in the future. Following the best practices will get you a long way, and remember: it’s Javascript, it IS different.

Some examples

Question: Can you please tell me why following throws error:

Answer: Because x is not hoisted. If you don’t declare x with a var-keyword, it will not be hoisted. When it reaches the second line (assuming it does) it just assigns it to a global variable. But because in the first line it tries to print something that doesn’t yet exist, it throws an error. To clarify this, you can rewrite your example like this:

Or similar, but instead of a global variable with a variable on an object. (global variables are just that, a variable on the window object, which is global):

Question: Can you explain this:

Answer: Remember “function declarations get hoisted completely (name AND implementation)” so after hoisting the function actually looks something like this:

The functions have been hoisted to the top and by effectively declaring the function twice the last one wins and overwrites the implemenation of the first.

Question: Does this mean things like variable declarations in for loops get hoisted, too? So declaring i in a for loop once will result in it getting hoisted to the top and any subsequent call to i in a for loop under the first one (but not inside) will have the same final value as i? Does that make sense? Guess I could try it out in dev tools, but this is more fun.

Answer: Yes, variables only have function scope, so in a loop they are shared as well:

发表评论

电子邮件地址不会被公开。 必填项已用*标注