From ES6 & Beyond - Chapter 2: Syntax I couldn't understand whether const
declaration is subject to the variable hoisting?
example:
;(function () {
"use strict";
const x = 3;
function foo() {
console.log("const x: ", x);
console.log("const y: ", y);
};
const y = 5;
foo();
})();
In the example above function foo
is declared before const y
declaration so according to Scope & Closures - Chapter 2: Lexical Scope which states that: _"No matter where a function is invoked from, or even how it is invoked, its lexical scope is only defined by where the function was declared."_ foo
should throw an error when it's called, but in this example when foo
is called it does log x
and y
values. So how does it possible if const
does not hoisted.
Of course it is hoisted; the lexical scope of 'foo' includes the definition of 'y' (even though it follows it textually).
HTH,
--ag
This example isn't actually about hoisting at all. You don't call foo()
until after you've declared and initialized y
, so obviously y
is in the scope when foo()
runs.
If you reverse those two lines, you'll see you get a ReferenceError
error, commonly referred to as a TDZ (temporal dead zone) error.
It is commonly cited that let
and const
don't "hoist", but this is an inaccurate statement. Hoisting (a made up metaphor, not a real thing) describes the notion that a variable's binding is added to a lexical scope at the time the lexical scope is entered. In all of var
, let
, and const
, this is true.
The difference between var
and the other two (let
, const
) is not that they don't "hoist". Rather, var
initializes (to undefined
) its variable at the time the lexical scope is entered, whereas let
and const
defer the initialization until the actual declaration statements are encountered.
A variable that has not been initialized (to undefined
) in a lexical scope is said to be in its TDZ, and thus cannot be accessed yet.
IOW, let
and const
declarations do in fact "hoist" their variable declarations to the entire enclosing scope (just like var
), but they remain uninitialized (unlike var
) and thus cannot be accessed earlier (from an execution perspective, not a code location perspective) in the scope than their declaration point.
@artiegold Thanks for your answer but if what you are saying is true, then const
should not be defined when it hoisted. In Scope & Closures - Chapter 2 Lexical Scope there is a statement that: _"....functions are hoisted first, and then variables."_ so when the function is declared it can't see the const
variable because it's declared afterwards.
Moreover, if you take a look a const
/let
explanation in MDN you will find that: _"In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context."_ (const / let)
like so:
;(function () {
"use strict";
function foo() { // hoisted
console.log("const x: ", x);
console.log("const y: ", y);
};
const x = 3; // hoisted (unlikely)
const y = 5; // hoisted (unlikely)
.
.
.
})();
MDN is wrong.
when the function is declared it can't see the const variable because it's declared afterwards.
That's just not how lexical scoping works. The order of declaration of a function versus a var
, let
, or const
is not relevant, as the observation of a binding doesn't happen until run time. The order of execution is the important part.
@getify but what about Scope & Closures - Chapter 2: Lexical Scope which states that: _"No matter where a function is invoked from, or even how it is invoked, its lexical scope is only defined by where the function was declared."_ ? Doesn't it mean that foo
is declared before so it can't see const y
?
That statement is referring to the scope as a whole, meaning if one function is inside another, then that inner function's scope is entirely determined by that location. It doesn't mean where inside that outer function it's declared, only that it's inside.
In "... the notion that a variable's binding is added to a lexical scope at the time the lexical scope is entered." what do you mean by variable's binding ?
The attachment of a variable to a scope.
So if a let
falls in the forest, it only makes a noise if its been set?
(Thanks @getify, the mdn description didn't seem correct.)
I asked on twitter, but let me follow up here.
Do let and const "hoist" to the top of block scope (i.e. If statement) or will they hoist to the top of the inner most function?
@akras14 yes, let
and const
attach to the nearest enclosing block (aka lexical scope environment) whereas var
attaches to the nearest enclosing function (also a lexical scope environment).
@getify The following statement from Scope & Closures Chapter 3:
However, declarations made with
let
will not hoist to the entire scope of the block they appear in.
contradicts a previous comment of yours:
IOW,
let
andconst
declarations do in fact "hoist" their variable declarations to the entire enclosing scope (just like var)...
the "scope & closures" book was written almost 2 years earlier than the ES6 book. my understanding evolved and improved over that period. and also ES6 itself settled and finalized.
Were you planning on fixing those mistakes? It almost sounds like you were already aware of this issue.
I am aware of the issue. Yes it's planned for being addressed in the second edition (whenever that happens!). :)
Most helpful comment
This example isn't actually about hoisting at all. You don't call
foo()
until after you've declared and initializedy
, so obviouslyy
is in the scope whenfoo()
runs.If you reverse those two lines, you'll see you get a
ReferenceError
error, commonly referred to as a TDZ (temporal dead zone) error.It is commonly cited that
let
andconst
don't "hoist", but this is an inaccurate statement. Hoisting (a made up metaphor, not a real thing) describes the notion that a variable's binding is added to a lexical scope at the time the lexical scope is entered. In all ofvar
,let
, andconst
, this is true.The difference between
var
and the other two (let
,const
) is not that they don't "hoist". Rather,var
initializes (toundefined
) its variable at the time the lexical scope is entered, whereaslet
andconst
defer the initialization until the actual declaration statements are encountered.A variable that has not been initialized (to
undefined
) in a lexical scope is said to be in its TDZ, and thus cannot be accessed yet.IOW,
let
andconst
declarations do in fact "hoist" their variable declarations to the entire enclosing scope (just likevar
), but they remain uninitialized (unlikevar
) and thus cannot be accessed earlier (from an execution perspective, not a code location perspective) in the scope than their declaration point.