content
                                
                                 content copied to clipboard
                                
                                    content copied to clipboard
                            
                            
                            
                        Hoisting of let and const
MDN URL
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
What specific section or headline is this issue about?
No response
What information was incorrect, unhelpful, or incomplete?

What did you expect to see?
Although the accessing of let and const variable before initializing throws the error, still they are initialized with the default placeholder value undefined, the exact issue is they are created in a different memory than global memory (window)
 .
.
Do you have any supporting links, references, or citations?
No response
Do you have anything more you want to share?
No response
MDN metadata
Page report details
- Folder: en-us/glossary/hoisting
- MDN URL: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
- GitHub URL: https://github.com/mdn/content/blob/main/files/en-us/glossary/hoisting/index.md
- Last commit: https://github.com/mdn/content/commit/b06d0695cf809707b421f5202495209928e3519d
- Document last modified: 2022-04-27T05:45:53.000Z
No, the information is correct. When you run the code you get the reference error. If the value was undefined this would be logged.

I don't know why the script variable x is shown as undefined. Perhaps @Josh-Cena might know.
@hamishwillee  I am not saying that it won't throw an error, but they are also initialized with the default undefined value just like the var variable it's just let and const are created in a different scope.
@vmeet24 If the value was initialized to undefined you would not get any error, as undefined is a valid value to log or access.
The fact you get a ReferenceError tells you that the value is uninitialized or does not exist. That's what ReferenceError means.
Are you basing your thinking on anything else other than the value shown in your screenshot above? I ask because the definitions used here come from the relevant specifications. So it is far more likely that the browser simply chooses to use the term "undefined" for an uninitialized value.
Let's see what @Josh-Cena says.
Hi @vmeet24
It's often unclear to me what "hoisting" means. It's only meant to be a mental model, not rigorous terms used in the spec. I've expressed the same concern here: https://github.com/mdn/content/pull/18571
Basically, you are right: lexical declarations are hoisted. If they aren't hoisted, there's no way to implement temporal dead zone at all! Consider this code:
function foo(x) {
  {
    console.log(x);
    let x = 1;
  }
}
foo(2);
If let x is not hoisted, then x should only be 1 after that line, and the first console.log should log 2. However, because let x is actually hoisted to the top of the scope, the engine knows that the console.log is logging the x value to be defined later instead of the x being shadowed in the upper scope, and will throw an error here. TDZ is a separate thing to be enforced by the engine, orthogonal to its lexical scoping rules. (And it actually causes let to be slightly slower than var due to the extra runtime check.)
However, my question is, is that information really helpful? Quoting https://github.com/mdn/content/pull/18571#pullrequestreview-1045925734:
When we say "hoisting", we really mean that "a variable can be safely used anywhere in this scope because the interpreter has already declared it as undefined". Otherwise, if accessing a variable always throws a reference error, I personally wouldn't care if it's because I'm in the TDZ, or because it's undeclared at all.
From a pragmatic point of view, I'll argue it's much simpler for us to say "it's not hoisted". The fact that the debugger says it's undefined before its actual declaration is largely an implementation detail—they can well say its value is <In TDZ> and prevent that confusion. I didn't read the V8 source and I don't know if it's literally undefined or just conveniently represented that way in the debugger.
As for your second question about "in a different memory than global memory", it's mentioned in our docs. let and const are "lexically scoped". var can only be in two scopes: "Global" or "Function". But for let, "Block" and "Script" can be scopes as well. This means if you have a var-declared variable in a script's top scope, you can reference that variable in a module imported later; the same isn't true for let because the module doesn't have access to the script's scope.
Does that make sense? I plan to rewrite that glossary entry anyway, and I'll likely remove the note that "let and const are hoisted" at all, since that's not quite useful.
As for your second question about "in a different memory than global memory", it's mentioned in our docs. let and const are "lexically scoped". var can only be in two scopes: "Global" or "Function". But for let, "Block" and "Script" can be scopes as well. This means if you have a var-declared variable in a script's top scope, you can reference that variable in a module imported later; the same isn't true for let because the module doesn't have access to the script's scope.
ahh, maybe I missed this part from the docs.
However, my question is, is that information really helpful? Quoting https://github.com/mdn/content/pull/18571#pullrequestreview-1045925734: When we say "hoisting", we really mean that "a variable can be safely used anywhere in this scope because the interpreter has already declared it as undefined". Otherwise, if accessing a variable always throws a reference error, I personally wouldn't care if it's because I'm in the TDZ, or because it's undeclared at all.
From the pragmatic point of view, this information looks correct, but however still JavaScript, as we saw that it does hoist the let and const variable, so a more proper way would be to stick to the part let and cost are hoisted too because somewhere down the road again someone will come shouting that the doc is incorrect and let and const are hoisted too 🤣
As I said, it largely depends on how you interpret "hoist". If accessing it always throws a ReferenceError I personally don't feel like it's hoisted.
MDN has wasted endless time on this point. I am pretty sure that whatever we do someone will complain.
I think the right way to address this might be to remove almost all mentions of hoisting in the docs and replace them with explicit explanation of the declaration being moved to global scope vs where initialization happens.
Then the hoisting article could be updated to explain that it was historically used to explain why you could call functions before they were declared etc. Cut it way back.
Upshot make the argument about whether something is hoisted or not irrelevant.
Perhaps easier said than done.
Adding my two cents here, I'm working on #18571
I think the FF debugger does a better job with showing how the declarations are registered:
 
It shows class, const and let as uninitialized and not undefined. I think FF communicates better what actually happens, since undefined is a real value, it confuses you if you see that let and const are set to undefined.
And I think this is where the confusion starts, since it seems like the environment is hoisting/lifting them (but is only registering them or allocating memory, not sure what the term is) they're included in the Hoisting concept, but I think we should just refer to Hoisting as the original and historical mechanism, pre ES6 which  allows function execution before declarations, and var declarations were accidentally/unintentionally tagged along, Brendan Eich said that himself in a tweet.
I'm going to quote what several texts on JavaScript, and what they say about Hoisting:
In JavaScript: The Definite Guide (covers ES6+), here's what you can find about hoisting:
Functions
Function declarations are 'hoisted' to the top of the enclosing script, function or block so that that functions defined this way may be invoked from code that appears before the definition"
Classes
"Unlike function declarations, class declarations are not 'hoisted'...Although a class declarations are like functions declarations in some ways, they do not share the 'hoisting' behavior: you cannot instantiate a class (or even reference the name) before declaration"
This can be actually seen in the spec, as a ClassDeclaration is not part of a HoistableDeclaration:
Variables
"One of the most unusual features of
vardeclarations is known as hoisting. When a variable is declared withvar, the declaration is lifted up (or "hoisted") to the top of the enclosing function." It then mentions something aboutlet"... (This can be source of bugs and is one of the important misfeatures thatletcorrects: if you declare a variable withletbut attempt to use it before theletstatement runs, you will get an actual error instead of just seeing anundefinedvalue)"
In Speaking JavaScript (this book ONLY covers ES5, classes, let and const did not exist)
Functions
"Hoisting means 'moving to the beginning of a scope'. Function declarations are hoisted completely, variable declarations only partially. Function declarations are completely hoisted. That allows you to call a function before it has been declared."
Variables
JavaScript
hoistsall variable declarations, it moves them to the beginning of their direct scopes.
In JavaScript for Impatient Programmers (ES2022)
Functions
"A function declaration is always executed when entering its scope, regardless of where it's located within that scope. That enables you to call a function
foo()before it is declared" In here, the term hoisting is not used, only early activation.
Classes
"Even though they are similar to function declarations in some ways, class declarations are not activated early"
I think this glossary entry should be clear about these two usages, because you often see these different meanings when you search online. MDN being what it is, it should aim to be the single point of truth.
Variables
"var: hoisting (partial early activation)... The scope of a
vardeclared variable is the innermost surrounding function and not the innermost surrounding block, as for most other declarations. Such a variable is already active at the beginning of its scope and initialised with `undefined"
const and let TDZ
"For JavaScript, TC39 needed to decide what happens if you access a constant in its direct scope, before its declaration... " "The time between entering the scope of a variable and executing its declaration is called the temporal dead zone (TDZ) of that variable:
- During this time, the variable is considered to be uninitialized (as if it were a special value it has)
- If you access an uninitialised variable, you get a
ReferenceError.- Once you reach a variable declaration, the variable is set to either the value of the initializer (speci via the assignment symbol) or
undefined--if the declaration usesletand there is no initializer."
in You Don't Know JS Yet: Scopes & Closures
This book does say that all declarations are hoisted, but makes distinctions about how var and function are initialized to and how let, const and class are "hoisted (lifted) but cannot be referenced. And the book proposes a definition for hoisting, which I think is fine, so long as you have invested a lot of time in learning what hoisting is, for a begineer, I think this can cause a lot of confusion.
Hoisting is generally cited as an explicit mechanism of the JS engine, but it's really more a metaphor to describe the various ways JS handles variable declarations during "compilation". But even as a metaphor, hoisting offers useful structure for thinking about the life-cycle of a variable--when it's created, when it's initialized, when it's available, when it goes away.
So, we can think of hoisting as two things:
- 
the original Hoisting concept of pre ES6, where it was the term that referred to the mechanism that allowed to call functions before their declarations, with vardeclarations unintentionally being brought along. Or
- 
We can think of Hoisting as the lifting of all the declarations before execution, where we start talking about complete initialization for functiondeclarations (the name of the variable becomes a variable who's value is the function itself), partial initialization forvar(undefined), and TDZ forlet,constandclasswith a record in the environment but with no value.
Which one should MDN expose?
Option one would be desirable to me if I thought it would work. Essentially it relegates hoisting to be "a useful way of describing a particular set of behaviours for functions". But the fact is that this won't work. If we define it this way we will be back where we started (i.e. people seem to want to believe in hoisting, even though as a term it has outlived its usefulness IMO).
I lean towards the second, in particular if the "all" is true. Separating the declaration from the initialization part of the story is a good thing. It would be even better to describe all of these things without using the word hoisting - just the declaration and initialization.
Just my two bits.