I have been playing with ES6 for a while and I noticed that while variables declared with var
are hoisted as expected...
console.log(typeof name); // undefined
var name = "John";
...variables declared with let
or const
seem to have some problems with hoisting:
console.log(typeof name); // ReferenceError
let name = "John";
and
console.log(typeof name); // ReferenceError
const name = "John";
Does this mean that variables declared with let
or const
are not hoisted? What is really going on here? Is there any difference between let
and const
in this matter?
From MDN web docs:
In ECMAScript 2015,
let
andconst
are hoisted but not initialized. Referencing the variable in the block before the variable declaration results in aReferenceError
because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.@thefourtheye is correct in saying that these variables cannot be accessed before they are declared. However, it's a bit more complicated than that.
All declarations (
var
,let
,const
,function
,function*
,class
) are "hoisted" in JavaScript. This means that if a name is declared in a scope, in that scope the identifier will always reference that particular variable:This is true both for function and block scopes1.
The difference between
var
/function
/function*
declarations andlet
/const
/class
declarations is the initialisation.The former are initialised with
undefined
or the (generator) function right when the binding is created at the top of the scope. The lexically declared variables however stay uninitialised. This means that aReferenceError
exception is thrown when you try to access it. It will only get initialised when thelet
/const
/class
statement is evaluated, everything before (above) that is called the temporal dead zone.Notice that a
let y;
statement initialises the variable withundefined
likelet y = undefined;
would have.The temporal dead zone is not a syntactic location, but rather the time between the variable (scope) creation and the initialisation. It's not an error to reference the variable in code above the declaration as long as that code is not executed (e.g. a function body or simply dead code), and it will throw an exception if you access the variable before the initialisation even if the accessing code is below the declaration (e.g. in a hoisted function declaration that is called too early).
No, they work the same as far as hoisting is regarded. The only difference between them is that a
const
ant must be and can only be assigned in the initialiser part of the declaration (const one = 1;
, bothconst one;
and later reassignments likeone = 2
are invalid).1:
var
declarations are still working only on the function level, of courseES6
introducesLet
variables which comes up withblock level scoping
. UntilES5
we did not haveblock level scoping
, so the variables which are declared inside a block are alwayshoisted
to function level scoping.Basically
Scope
refers to where in your program your variables are visible, which determines where you are allowed to use variables you have declared. InES5
we haveglobal scope,function scope and try/catch scope
, withES6
we also get the block level scoping by using Let.var
keyword, it's known the entire function from the moment it's defined.When you define a variable with
let
statement it's only known in the block it's defined.If you run the code, you could see the variable
j
is only known in theloop
and not before and after. Yet, our variablei
is known in theentire function
from the moment it is defined onward.There is another great advantage using let as it creates a new lexical environment and also binds fresh value rather than keeping an old reference.
The first
for
loop always print the last value, withlet
it creates a new scope and bind fresh values printing us1, 2, 3, 4, 5
.Coming to
constants
, it work basically likelet
, the only difference is their value can't be changed. In constants mutation is allowed but reassignment is not allowed.If a constant refers to an
object
, it will always refer to theobject
but theobject
itself can be changed (if it is mutable). If you like to have an immutableobject
, you could useObject.freeze([])
Quoting ECMAScript 6 (ECMAScript 2015) specification's,
let
andconst
declarations section,So, to answer your question, yes,
let
andconst
hoist but you cannot access them before the actual declaration is evaluated at runtime.