The ES2015 (ES6)
introduced two important JavaScript
keywords: let
and const
.
If there is let
or const
inside a code block, the code block will form a closed scope for the variables declared by these commands from the beginning of the block.
[[Scopes]]
is an object that saves the function's scope chain and is an internal property of the function that cannot be directly accessed. In [[Scopes]]
, we can see the appearance of a Block
block-level scope. This makes let
especially suitable for use inside for
loops. Before the introduction of the let
keyword in ECMAScript 2015
, there were only function scope and global scope. Functions could nest within function scopes, but there was no true block scope within for
loops, leading to a common closure creation problem.
The output is 3 3 3
instead of the expected 0 1 2
. The reason is that when these three closures are created during the loop, they share the same lexical scope. This scope, due to the existence of an i
declared with var
, has a function scope. By the time the closure function is executed, the loop has already finished, and i
has been assigned the value of 3
, resulting in the printout 3 3 3
. This problem can be solved by using the let
keyword to declare i
and create block-level scope.
Alternatively, the issue can also be resolved by using an anonymous function to create a function scope.
Within the same scope, let
and const
can only be declared once, while var
can be declared multiple times.
When using let
and const
to create block-level scope, the code block will form a closed scope for the variables declared by these commands from the beginning of the block. Within the code block, using a variable before its declaration will result in an error, known as the temporal dead zone.
let
and const
have variable hoisting as well. In the ES6
documentation, there is the term var/let hoisting
, indicating that the official documentation explains that let
and var
are the same, both have variable hoisting, but the variable hoisting of let
is different from that of var
.
A convincing example found on stackoverflow
is as follows:
In js
, regardless of the declaration form (var
, let
, const
, function
, function*
, class
), hoisting occurs. However, the difference is that the declarations of var
, function
, and function*
are initialized to undefined
during hoisting, so when accessing these variables, no ReferenceError
exception will be thrown. On the other hand, the variables declared with let
, const
, and class
are hoisted without being initialized, and these variables are said to be in the "temporal dead zone", hence accessing them will result in a ReferenceError
exception, making it appear as if they were not hoisted at all.
When using var
to directly declare variables or methods in the global scope, they will be attached to the window
object, while the variables or methods declared using let
and const
will be saved in the Script
scope.
var
and let
can be declared without initializing, whereas const
must be initialized.
const
is used to declare a read-only constant, and once initialized, its value cannot be changed.
In fact, const
ensures not the immutability of the variable's value, but that the data stored at the memory address pointed to by the variable cannot be changed. For simple types like number
, string
, boolean
, and Symbol
, the value is stored at the memory address pointed to by the variable, making const
of these simple type variables equivalent to constants. For complex types like object
, array
, and function
, the variable points to the memory address where a pointer to the actual data is stored, so const
can only guarantee that the pointer is fixed, and it cannot control whether the data structure pointed to by the pointer changes.