-
Notifications
You must be signed in to change notification settings - Fork 33.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
let
hoisting?
#767
Comments
Hello, Hoisting is the topic of the next chapter in that book: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch4.md the scope application of hoisting starts in this part specifically: EDIT: phrasing |
Thanks for the comment. If you take a look at the chapter you link to, as well as my example, you'll notice that the chapter on hoisting actually doesn't address the specifics of hoisting with the Hoisting is shown in the chapter as a step of the compiler that lifts
into something that is "invisibly" interpreted during compilation as something more similar to
According to this interpretation, it'd be easy for someone to make the assumption regarding the clause I quoted above:
For someone unfamiliar with the nuances around
is misleading. Let me know my explanation makes sense. You can also take a look at the article linked above - he explains in further detail about how the Temporal Dead Zone works. EDIT: phrasing on last paragraph to more clearly outline what happens in the example. |
@dxu Maybe the error message is clear enough. |
Hoisting is not a real thing. It's a made up concept. It's a metaphor to describe what actually happens when the compiler finds declarations and associates them with the scope they belong to so they can be used in that scope. When we say that this code: x = 2;
var x; is interpreted as this code via hoisting: var x;
x = 2; ...what we're actually saying is not just that the With
So to recap: the metaphor of hoisting is about both making a declaration attach to a scope at compile time before execution, and about initializing that variable to be available throughout the scope. Both are true of To the confusion that you're pointing out around not being able to access the outer Whenever I teach, I only mention such a thing in passing as an informal explanation, which is kinda fudging with the truth a bit. Then again, the entire usage of "hoisting" to describe JS's compiler scoping behavior is fudging with the truth, because hoisting implies a reordering of code, which is not at all what happens. The accurate explanation of the confusion you point out is that A variable cannot be used while it's in its TDZ, which is why you're able to observe the inaccessibility of an very-much-already-existent-but-not-yet-initialized inner I cover TDZ in more detail in subsequent books in the series ("Types & Grammar" and "ES6 & Beyond"). At the time of the writing of this book, I didn't have as full an understanding of TDZ and its implications on how scopes work as I do now, which is why it's not mentioned here. It's a planned issue to address TDZ in the second edition of the book, whenever I get around to that effort. |
@getify function foo(value) {
var x = 1
if (true) {
/*
* TDZ for x
*/
console.log(x) // ReferenceError: x is not defined
let x = 2 // here TDZ ends and x get its value
console.log(x) // => 2
}
}
foo() This demostrates that We can't use it and get I'm missing something? |
@felixsanz Hoisting is used to describe the creation of the binding and the initialization. I think even members of the committee do not consider I also only considered the mere fact that the bindings are created as hoisting, but that's not technically true. Otherwise, why wouldn't |
Nope.
Nope. Please re-read my message above where I correct this misunderstanding. Quoting myself:
What @fkling said is correct. |
@getify Why you say that hoisting means adding the variable to the scope and ALSO initializing it so it can be used in the entire scope? I mean, where do you get that definition from? Hoisting is the act of hoist, which means in the dictionary "raising" or "lifting". And this is true since Some source for what hoisting really means in programming would be nice, because if it's a concept/idea, then everyone can have its own definition (and that doesn't mean you are wrong, but means that we both could be right). |
Also: http://www.ecma-international.org/ecma-262/7.0/#sec-evaldeclarationinstantiation 5 -> d -> ii -> 1
"let hoisting". |
I think we're arguing about semantics and wording here. I think everyone's agreed on how this actually works - it's just the interpretation of what "hoisting" is strictly referring to. There's definitely a difference between the Thanks for the great work @getify! I look forward to finishing up the rest of the book. Thanks for all your contributions to the community, I really enjoyed your talk at Forward 3 last year. |
https://twitter.com/awbjs/status/434044880180871168 (@allenwb is the editor of the ES6 spec, so I trust his authority on the terms/definitions.) |
Thanks for @getify 's nice explanation. I get the key points:
The fourth point is important that
But I'm still confused about the meaning of I thought the declared variable (has value |
Yeah, the problem is nuances and conflations of words. From the code author's perspective, "declaring" is the Declaring always happens at time of compilation, and its effect can be seen whenever a scope is first entered. Initializing for |
Here's the real question. What is the benefit of changing the behavior of let and const from the way var works? |
Mostly to prevent code that does silly things like: x = 2;
var x; By far, most developers would agree this is poor coding style. So, the language applies social pressure to devs to convince them to stop doing that so theu can use the shiny new toys. |
The spec. uses the phrase "create a binding" to mean "registering a variable in a scope". A binding is an association between a name and a value. Bindings are created for all the names declarated within the scope when the scope is first entered. When a binding is created it does not yet have a value associated with it. It is "uninitialized". Determination of when a binding actually gets initialized depends upon how its name was declared and the kind of scope that contains itl. |
Except, the above is legal. The real motivation for temporal deed zones is: console.log(k);
x=42;
const k=x;;
console.log(k); Should the first For consistency, all new declarations introduced by ES6 follow the same initialization rule as |
Of course the above is legal. I intentionally used a |
@getify |
:+1 |
Interesting discussion here. I agree that at this point people are basically arguing about the semantics of what the term hoisting means. MDN states here that let is hoisted which will only add to the confusion. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let |
@getify I can't agree more. console.log(a) // undefined
var a = 1
console.log(a) // 1 equals to var a
console.log(a)
a = 1 but the compiler really just replace the code? i'm not sure. |
Is 'hoisting' a valid term/concept in specification, now? Is this tweet https://twitter.com/awbjs/status/434044880180871168 written at 2014 would be old answer? Update: The links below are very helpful discussion for me. Refer to them. |
Just for the record... I was wrong in this thread years ago. "Hoisting" is attaching the binding, only, not whether it's initialized. Let and const "hoist" but they aren't initialized. My apologies for the confusion caused. I know better now. |
@getify So, If the hoisting means what you said, should explanation below be updated?
Update: See #1132 together.
|
For the clarity, sorry, i don't fully understand your meaning. What's your meaning of "scope is entered"? At compile time? By whom? |
"scope is entered" is referring not to compile time but to runtime execution... each time a scope is executed. parsing/compilation sets up the plan for a scope, but this is just a plan. the scope itself isn't created (memory reserved, etc) until it's executed. |
Thanks!! To sum up, the phases to execute declarations( |
Yes, and additionally, there is no "gap" for |
You are right! So temporal dead zone doesn't exist in case of |
I don't believe I have fully understood the "binding" in "create a binding" part, since I had seen it popping up in places like const declaration, in which someone pointed out that it creates an "immutable binding". |
"Immutable binding" is, IMO, a bit misleading. I would say: |
As commonly used in programming language literature, a "binding" is an association, within an environment, between a name and some entity. In JS, most bindings are to computational "values", statement labels are also a kind of binding. An immutable binding is one whose entity association never changes. A mutable binding us one whose entity association can change. Mutability of bindings is an orthogonal concept to mutability of entities (eg values). A JS let or var declaration creates a mutable binding. The associated entity (at some point in time) might be either an immutable value (a number, string, etc) or a mutable value (most objects). A const declaration creates an immutable binding, but just like a let/var the associated entity might be either mutable or immutable. |
BTW, extra credit question. In JS, a binding can usually be thought of as associating a name with a value. Some language have features that require bindings that associate a name with a storage cell. For example, call-by-reference parameters. This enables multiple names to refer to the same "variable". What features in modern JS create bindings to storage cells rather than values. |
Shrugs. I suppose I don't really see it useful to use the word "binding" to talk about the "assignment of a value", why not just talk about that as "assignment". To me, "binding" is more about the attachment between a variable name and the scope it's in... its entry in the lexical environment. But whatever. It's not really worth nitpicking over these words.
What comes to mind: |
In general, I think it's fine to talk about "assignment of a value to a variable". Talking about changing a binding is a way to define what is "assignment" means. Right on import, but also applies to export. Export also binds to storage cells. |
For the sake of completeness, here's a Lua sample. Lua The scope starts at the declaration and last until either the end of the block or until it is shadowed by an identically named variable. local a = 5
local function factory()
local function outer()
return a
end
local a = 6
local function firstInner()
return a
end
local a = 7
local function secondInner()
return a
end
return {outer = outer, firstInner = firstInner, secondInner = secondInner}
end
o = factory()
print(o.outer()) --> 5
print(o.firstInner()) --> 6
print(o.secondInner()) --> 7 IMO |
Firefox had a let (x = 2) {
// ..
} I think this was a million times better than the |
Hi, thanks for taking the time to write such a great book! I'm working my way through it - in Chapter 3, you talk about hoisting
let
variables. Specifically:Even though the variable
bar
isn't initialized as undefined, it seems that they are still created.Using the example from this article as an example,
Do you think it would be helpful to add a clarification regarding the meaning of "hoisting" (since
x
here is technically still being created at the top of the block - it's just not initialized and can't be accessed), or perhaps just include a similar example?EDIT: removed extra console.log in the example that may have caused confusion
The text was updated successfully, but these errors were encountered: