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?
var is declared and initialized during hoisting. let and const are only declared during hoisting, not initialized. Accessing uninitialized variables result in ReferenceError . Prefer let over var , wherever possible to avoid the confusion arising due to hoisting.
Yes, variables declared with let and const are hoisted. Where they differ from other declarations in the hoisting process is in their initialization. During the compilation phase, JavaScript variables declared with var and function are hoisted and automatically initialized to undefined .
In JavaScript, Hoisting is the default behavior of moving all the declarations at the top of the scope before code execution. Basically, it gives us an advantage that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.
var variables can be updated and re-declared within its scope; let variables can be updated but not re-declared; const variables can neither be updated nor re-declared. They are all hoisted to the top of their scope.
@thefourtheye is correct in saying that these variables cannot be accessed before they are declared. However, it's a bit more complicated than that.
Are variables declared with
let
orconst
not hoisted? What is really going on here?
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:
x = "global"; // function scope: (function() { x; // not "global" var/let/… x; }()); // block scope (not for `var`s): { x; // not "global" let/const/… x; }
This is true both for function and block scopes1.
The difference between var
/function
/function*
declarations and let
/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 a ReferenceError
exception is thrown when you try to access it. It will only get initialised when the let
/const
/class
statement is evaluated, everything before (above) that is called the temporal dead zone.
x = y = "global"; (function() { x; // undefined y; // Reference error: y is not defined var x = "local"; let y = "local"; }());
Notice that a let y;
statement initialises the variable with undefined
like let 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).
Is there any difference between
let
andconst
in this matter?
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;
, both const one;
and later reassignments like one = 2
are invalid).
1: var
declarations are still working only on the function level, of course
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With