Is there a purpose to hoisting variables?
Asked Answered
K

3

10

I've been learning a lot of Javascript lately and I've been trying to understand the value (if there is any) of hoisting variables.

I understand (now) that JS is a two pass system, it compiles and then executes. Also, I understand that the var keyword 'exists' in the lexical scope it was declared, hence why it's 'undefined' if it's called before it's assigned a value by the engine.

The question is, why does that even matter? What use is there to hoisting variables that you can't do without hoisting? I feel like it just creates less-readable code with no gain ...

is there an example(s) of where hoisting variables is useful?

Kimikokimitri answered 18/10, 2018 at 17:8 Comment(7)
JS is a two pass system, it compiles and then executes that's sort of incorrect. The code could be compiled but that would be implementation dependent. What you are talking about is the JS interpreter parsing the code and then executing it. However, as for usefulness of hoisting...I really can't think of a reason you really need to have it for variables. If you don't hoist, then if you do x = 42; var x would be an error...which I think is fine.Enlistment
Possible duplicate of Why does JavaScript hoist variables?Kayak
@Kayak I'd agree with comments on that answer - it doesn't actually answer why you (as a language designer or even user) would want variables hoisted. It basically states that they are hoisted. It's with more words but it's what it boils down to. And the fact that they are hoisted...is readily apparent from the question. The rationale for variables hoisting mechanism isn't, though.Enlistment
If you mean "hoisting" as "seed a new scope with all identifiers found in it", then I'd ask, what is the alternative? To only add the binding when the declaration is "executed"? So would var foo = 42; function bar() { console.log(foo); var foo = 21; }; bar(); log 42? Some languages do that... Or should it throw an error? (you get that with let and const). In the end, it's just one of the decisions one has to make when designing a language. I think it's generally agreed upon that initializing variables with undefined is sub-optimal, which is why we have let and const.These
@vlaz While the other question might not have a satisfactory answer, I think the question itself is a duplicate. "Why does..." & "Is there a purpose to..." is equivalent, no?Kayak
@Kayak in defense of my question, I understand well enough 'what' hoisting aims to explain, I was just trying to actually think of a reason 'why' one would ever want to hoist variables and bergi provides some great examples.Kimikokimitri
@kevin, ok, cool. But I can't let it go! You explained that you asked the question "why" which is why I said it was a duplicate of "Why does JavaScript hoist variables?". :DKayak
E
11

"Hoisting" is necessary for mutually recursive functions (and everything else that uses variable references in a circular manner):

function even(n) { return n == 0 || !odd(n-1); }
function odd(n) { return !even(n-1); }

Without "hoisting", the odd function would not be in scope for the even function. Languages that don't support it require forward declarations instead, which don't fit into JavaScripts language design.

Situations that require them might arise more often that you'd think:

const a = {
    start(button) {
        …
        button.onclick = e => {
            …
            b.start(button);
        };
    }
};
const b = {
    start(button) {
        …
        button.onclick = e => {
            …
            a.start(button);
        };
    }
};
Endo answered 18/10, 2018 at 18:32 Comment(14)
@TylerRoper A function declaration introduces a variable just like var does. I could have written var even = n => n==0 || !odd(n-1); insteadEndo
Thank you, I really appreciate the examples!Kimikokimitri
Makes sense for recursive functions, but this does not explain why variables are hoisted. Is it related to cyclic variable reference(from your answer)? Can you add that to the answer as well. I am curious why JS does it and why do languages like Java don't , because java also allows recursive functions without forward declarations.Whaley
@Whaley Java "hoists" class attributes and methods (you can refer to them anywhere in the class). JS is not a class-based language, so it does the hoisting on all identifiers in a scope - and it does not distinguish between function declarations and others, because they all just declare variables which might or might not hold function values.Endo
I think i did not convey my query well. I meant it something like this - codebunk.com/b/1541100036830 . The 1st java snippet (titled with "Does not work") is something that would work in JS , i.e. variables dont give reference error if they are defined later. So , my question was why does JS do it differently?Whaley
@Whaley Actually, that does not work in JS either when you use let or const. Only var is initialised to undefined at the top of the scope - and that might have been a mistake in the language design, albeit making the interpreter implementation simpler.Endo
The use of use strict with this example doesn't throw an error. My guess is that the example would work even without hoisting. The two functions are only declared. By the time they're executed, both functions are declared.Pension
@Pension Why would "use strict" cause an error? "Hoisting" happens in strict mode as well.Endo
As I understand strict mode prohibits use of undeclared variables. Perhaps, it's allowed for functions, as you suggest.Pension
@Pension The whole point of hoisting is that the declared variables are declared in the whole scope, they are not "undeclared".Endo
Thanks for clarifying about scope. I agree. I however don't agree about the odd/even example. I don't think it has anything to do with hoisting. Even without hoisting it would work.Pension
@Pension Without hoisting, what would odd refer to inside even? How would it work, how should the compiler know?Endo
In the creation phase of the execution context, functions even/odd are placed in memory but their body is not evaluated (since JS is not a compiled language). During execution phases, body is executed. By that time, both functions are available. We know that hoisting doesn't work for func exp. But the following code runs without errors: function even(n) { return n == 0 || !abc(n-1); } abc = function (n) { return !even(n-1); } even(4). Evaluation happens at execution phase. abc is hoisted as a variable init with undefinedPension
@Pension Yes, the abc declaration is hoisted as well, so what's the point? (And btw, a function body is only evaluated when the function is called, that has nothing to do with creation/execution phases).Endo
A
2

There is no such thing as hoisting. Hoisting is merely a side effect of the compile phase that occurs and the fact that Javascript is lexically scoped. When the compiler comes to the compile phase it puts all variable and function declarations in memory as it figures out the lexical scopes that exists in the program. But there is no hoisting function or keyword or module. In fact it wasn't even reference in the Ecmascript spec before the es2015 release.

At the end of the day, hoisting is one of those million dollar words we all use, often because its easier to use rather than explain and discuss the compilation process that javascript goes through.

My suggestion would be to either read through the Ecmascript specs, work through a javascript engine source like v8, or read up on Kyle Simpson's work. He wrote a great series called You Don't Know JS.

Hope this helps!

Hoisting is a term you will not find used in any normative specification prose prior to ECMAScript® 2015 Language Specification. Hoisting was thought up as a general way of thinking about how execution contexts (specifically the creation and execution phases) work in JavaScript. However, the concept can be a little confusing at first. Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code. <- From the Mozilla docs

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

Accountable answered 18/10, 2018 at 18:41 Comment(15)
Albeit some useful knowledge, I don't believe that this answers OP's question at all.Amberambergris
Yes, the term "hoisting" is a horrible term as it implies that code is moved around which doesn't happen, but still the OP wants know why JS was designed with this behaviour.Endo
How does it matter when the term "hoisting" was officially used? It's just some term that describes an effect. This effect existed since v1: ecma-international.org/publications/files/ECMA-ST-ARCH/… (section 10.1.2)These
It informs his question in the sense of not propagating it as a language feature. It a consequence of the compilation phase. Anything else we try to grasp at is self-rationalization after the fact. Understanding the why makes people better developers, rather than packaging up stomach able bits of information all the while not understanding the why. But thats just my opinion, free to downvote and then my answer won't be seen at all. :)Accountable
@m.a.solano93 But it is a language feature. The compiler/interpreter was deliberately written to do exactly this, and not something else. It's not a side-effect of an uneducated implementation choice.Endo
That's actually what I am getting at @Felix Kling the reason why it behaves that way has less to do with Language architecture and more to do with the compilation process. Nothing is moved around. There is no magic. There is no secret keyword the compiler uses. It's literally just a side effect from the compilation process.Accountable
@Endo Perhaps I am just quibbling over semantics then, and I am wrong. No worries, regardless I wanted to voice a perspective that if nothing else can be proven wrong and people can read here and glean what ever they might choose.Accountable
But the specification explicitly says "create a binding for every variable declaration you find in the scope". How is it not part of the language then?These
So then you are talking about Lexical Scoping? If you term it lexical scoping I have no problem with the statement. Again, it could be a silly semantics argument that isn't even worth the time. That being said, I still don't really see a 'hoisting' functionality or mechanism... It's just how lexical scoping works.Accountable
"It's just how lexical scoping works." Interesting thought, but I don't think that's correct. Bergi mentions mutual recursion so it might as well be that most languages use lexical scope and most languages want to support mutual recursion and that's why they behave like this. Consider C, which has lexical scope: repl.it/repls/SmartHumongousConnection . This doesn't work, you'd need a forward declaration. But it works in other languages without forward declaration. Hence such an effect is likely not part of lexical scope itself.These
Shoot... You're right... I'm wrong. Good call. Do you have any resource you'd recommend? I am reading through something right now, but it might be useful for others that read through this.Accountable
Not really. I'm just piecing together how various languages work and things I read on Wikipedia ;)These
FWIW, I tried Haskell first which seems to work like JavaScript: repl.it/repls/RemarkableSlimFeedsThese
@Accountable thank you for the reference to YDKJS, im actually watching his videos on front-end masters and reading through his books. Really great stuff and that's essentially why it got me thinking about the concept of hoisting, at least in the videos I've watched he really only goes into saying hosting variables is messy and 'meh' and I wanted to delve a bit deeper into it conceptually. Thank you though! :)Kimikokimitri
also thanks @FelixKling, definitely going to revisit this post in the future and dive more into it.Kimikokimitri
B
0

Not really. The only thing I can think of where it would be helpful is if you are writing code in a hurry and you happen to declare it later on. So it doesn't really matter, it's a weird addition to JS that a lot of people don't really even know about because utilizing it feels backwards and inefficient.

Borrero answered 18/10, 2018 at 18:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.