ES6 block-level binding, let, const declaration
Var declarations and variable promotion
A variable declared with the var keyword is considered declared at the top of the function (or at the top of the global scope if the declaration is not in any function), regardless of where it is actually declared. This is called variable lifting.
function getValue(condition) {
if(condition) {
var value = "blue"
return value
}else {
console.log(value) // It can be accessed here and the value is undefined
return null
}
console.log(value) // It can be accessed here and the value is undefined
}
Copy the code
For those new to JS, you might think that the variable value is created only if condition has a value of true. But the reality is that the value is always created. The JS engine adjusts the getValue function in the background, as follows:
function getValue(condition) {
var value // Uninitialized variable, default value is undefined
if(condition) {
value = "blue"
return value
}else {
console.log(value) // It can be accessed here and the value is undefined
return null
}
console.log(value) // It can be accessed here and the value is undefined
}
Copy the code
The declaration of the value variable is promoted to the top, and the initialization is left in place. And that also means that we can access the value variable in the else branch, where it’s going to be undefined, because it’s not initialized.
? Block-level declarations: block-level declarations make declared variables unreachable outside the scope of the specified block. Block-level scopes (also known as lexical scopes) are created when:
// 1. Inside a function
function fun() {
// no thing
}
2. Inside a code block (enclosed by a pair of curly braces)
{
// no thing
}
Copy the code
Let the statement
The syntax of the let declaration is the same as that of var. We could have used let instead of VAR to declare variables, but we would have limited the scope of the variables to the current block-level scope. Since there is no variable promotion in the LET declaration, we need to manually place the let declaration at the top so that the variable can be accessed throughout the code block.
function getValue(condition) {
if(condition) {
let value = "blue"
return value
}else {
// value is not accessible here
return null
}
// value is not accessible here
}
Copy the code
? The getValue function behaves more like other C-like languages. Because the variable value declaration uses let instead of var, the declaration is not elevated to the top of the function definition, so the variable value is not accessible outside the if block; And if condition is false, this variable is never declared and initialized.
No duplicate declaration
If an identifier is already defined within a code block, let declarations using the same identifier within that code block will result in an error being thrown. As follows:
var count = 30
// Syntax error Identifier 'count' has already been declared
let count = 40
Copy the code
In this case, the count variable is declared twice: once using var and once using let. Because let cannot repeatedly declare an existing identifier in the same scope, the let declaration here throws an error. On the other hand, declaring a new variable with the same name using let in a nested scope does not throw an error.
var count = 30
// No errors are thrown
if(condition) {
let count = 40
}
Copy the code
The let declaration does not throw an error here, because instead of creating the variable again in the same block-level scope, it is created inside the if code block, and the new variable will mask the global count variable, thereby locally blocking access to the latter.
Constant statement
In ES6 it is also possible to declare using const syntax. Variables declared with const are considered constant, meaning their value cannot be changed after being set. Because of this, all const variables need to be initialized at declaration time. As follows:
// Valid constants
const maxItems = 30
// Syntax error: not initialized
const name
Copy the code
The maxItems variable is initialized, so its const declaration works fine. The name variable is not initialized, causing an error to be thrown when you try to run the code.
Compare const declarations with let declarations
Constant declarations, like let declarations, are block-level declarations. This means that constants are not accessible outside the block in which they are declared, and the declaration is not promoted either. As follows:
if (condition) {
const maxItems = 5;
}
// maxItems cannot be accessed here
Copy the code
Another similarity with let is that const declarations also throw errors when defining a declared variable in the same scope. As follows:
var message = "Hello!"
let age = 25
// Both throw errors
const message = "Good!"
const age = 18
Copy the code
Both const declarations can be used separately, but either throws an error after the previous var and let declarations.
! There is one important difference to keep in mind between let and const: Attempting to assign a constant previously declared as const throws an error, in strict or non-strict mode:
const maxItems = 5
// Throw an error
maxItems = 6
Copy the code
? Note here that the maxItems variable cannot be reassigned. However, unlike other languages, JS constants can contain values that can be modified if they are an object. This is because a const declaration creates a read-only reference to a value. This does not mean that the values it holds are immutable, just that variable identifiers cannot be reassigned. For example, in the case where the reference content is an object, this means that you can change the object’s content (for example, its parameters).
const person = {
name: 'White Rabbit Creamy Candy'
}
/ / normal
person.name = 'roll'
// Throw an error
person = {
name: 'jelly'
}
Copy the code
This is because person is bound to an object with an attribute when initialized. Changing Person.name is ok because it only modifies the member of the Person object, not the binding value of Person. But when we try to reassign the Person object (changing the variable binding), an error will be thrown.
! > const blocks changes to the variable binding, not to the member value.
Temporary dead zone
Variables declared using let or const are not accessible until they reach the declaration, and attempting to access them results in a reference error, even when we are doing something safe (such as using the Typeof operator). As follows:
if(condition) {
// Cannot access 'value' before initialization
console.log(typeof value)
// The same thing happens to const, even though let is declared here
let value = "blue"
}
Copy the code
When we use a variable declared by let or const, we cannot in any way avoid temporary dead zones if we try to use it before we define the location.
Block-level binding in loops
The scenario where we most need to use block-level scope for variables is probably within the for loop, where we want one-time loop counters to be used only within the loop. As follows:
for(var i = 0; i < 10; i++) {
process(arr[i])
}
// I is also accessible here
console.log(i) / / 10
Copy the code
If we replaced the above example with a let, it would look like this:
for (let i = 0; i < 10; i++) {
process(arr[i])
}
// I is not accessible here, an error is thrown
console.log(i)
Copy the code
The function inside the loop
For a long time, the nature of VAR made loop variables accessible outside the scope of the loop, making it problematic to create functions inside the loop. As follows:
var funcs = []
for(var i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs.forEach(function (fun) {
fun() // Output the value 10 times
})
Copy the code
? > < p style = “max-width: 100%; clear: both; min-height: 1em; This is because variable I is shared in each iteration of the loop, meaning that the functions created within the loop all have references to the same variable. At the end of the loop, the variable I will have a value of 10, so when console.log(I) is called, 10 is printed each time.
To solve the above problem, we can use immediate call function expressions (IIFE) within the loop to force the creation of a new copy of the variable in each iteration. As follows:
var funcs = []
for(var i = 0; i < 10; i++) {
funcs.push((function (value) {
return function() {
console.log(value)
}
}(i)))
}
funcs.forEach(function(fun) {
fun() // Output from 0 to 9
})
Copy the code
While this approach did what we wanted it to do, it increased our code load. Fortunately, block-level binding with lets and const simplifies this loop in ES6.
The let declaration within the loop
The LET declaration simplifies the loop by effectively mimicking the role of IIFE in the example above. In each iteration, a new variable with the same name is created and initialized. This means that we can completely omit IIFE and get the desired results. As follows:
var funcs = []
for(let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs.forEach(function(fun) {
fun() // Output from 0 to 9
})
Copy the code
? > This achieves the same effect as using var declarations and IIFE, but is much cleaner. The let declaration creates a new I variable each time in the loop, so functions created inside the loop get their own copy of I, and the value of each copy of I is determined each time the variable is declared in the loop iteration. The same applies to for-in and for-of loops. As follows:
var funcs = []
var object = {
a: true.b: true.c: true
}
for(let key in object) {
funcs.push(function() {
console.log(key)
})
}
funcs.forEach(function(func) {
func() // Output "a", "b", "c"
})
Copy the code
A constant declaration within a loop
The ES6 specification does not explicitly prohibit the use of const declarations in loops; however, it behaves differently depending on how the loop is used. In a regular for loop, we can use const during initialization, but the loop will throw an error when you try to change the value of that variable. As follows:
var funcs = [];
// Throw an error after an iteration
for (const i = 0; i < 10; i++) {
console.log(i) // An error is thrown after 0 is printed.
}
Copy the code
? > because the value of I is 0 after the first iteration is successfully executed. An error is thrown when i++ is executed because the statement attempts to modify the value of a constant.
But when a const variable is used in a for-in or for-of loop, it has the same effect as a let variable. As follows:
var funcs = []
var object = {
a: true.b: true.c: true
}
for(const key in object) {
funcs.push(function() {
console.log(key)
})
}
funcs.forEach(function(func) {
func() // Output "a", "b", "c"
})
Copy the code
? > const works in for-in or for-of because the loop creates a new variable binding for each iteration, rather than changing the value of the bound variable.
Global block-level binding
Another way that lets differ from const var is in their behavior at global scope. When var is used in the global scope, it creates a new global variable and becomes an attribute of the global object (window on browsers). This means that using VAR could inadvertently overwrite an existing global property. But when we use let or const in the global scope, no properties are added to the window, although new bindings are created in the global scope as well.
var RegExp = "hello!"
console.log(window.RegExp) // "hello!"
var good = "Good!"
console.log(window.good) // "Good!"
Copy the code
let RegExp = "hello!"
console.log(RegExp) // "hello!"
console.log(window.RegExp === RegExp) // false
const good = "Good!"
console.log(good) // "Good!"
console.log(good in window) // false
Copy the code
conclusion
The block-level binding of lets to const introduces block-level scope into JS. Neither method promotes variables, exists only within the block-level scope in which they are declared, and causes a temporary dead zone error when accessed at the location before variables are declared.
Let and const behave similarly to var in many cases, but there are significant differences in the loop. In for-in and for-of loops, both let and const can create a new binding at iteration time, which means that the function created in the body of the loop can use the value of the loop variable bound to the current iteration (rather than the value of the variable at the end of the loop, as with var). This is also true when using let declarations in a for loop, but using const declarations in a for loop results in an error.
Const should be used by default when using block-level binding, and let should only be used when it is clear that the variable value needs to be changed. This helps prevent certain types of errors.
6 Reading notes.
- ES6 block-level binding, let, const declaration
- Strings and Regular expressions (RegExp)
- Functions
- Extended object functionality
- Deconstruction assignment
- Symbol and Symbol attribute
Refer to the article
- MDN
- Understanding ECMAScript 6
- Understanding ECMAScript 6