Let is a new command in ES6. ES6 recommends using let instead of var, and it is mandatory in Typescript. The author will introduce the usage of let, the difference between let and VAR, and the reason why let can replace VAR.

Basic usage

ES6 added the let command to declare variables. Its use is similar to var, but the declared variable is only valid within the code block in which the let command resides.

{
  let a = 10;
  var b = 1;
}
a // ReferenceError: a is not defined.
b / / 1
Copy the code

The above code declares two variables, let and var, respectively, in the code block. These two variables are then called outside of the code block, with the let variable reporting an error and the var variable returning the correct value. This indicates that the variable declared by the LET is valid only in the code block in which it is located.

A counter for a loop is suitable for the let command.

for (let i = 0; i < 10; i++) {
  // ...
}
console.log(i);
// ReferenceError: i is not defined
Copy the code

In the code above, the counter I is only valid inside the for loop, and references outside the loop report an error.

The following code, if var is used, will output 10.

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6] ();/ / 10
Copy the code

In the above code, the variable I is declared by the var command and is valid globally, so there is only one variable I globally. The value of variable I changes each time through the loop, and the console.log(I) inside the function assigned to array A refers to the global I. That is, all the I’s in array A refer to the same I, causing the runtime to print the value of the last round of I, which is 10.

If let is used, the declared variable is only valid in the block-level scope, and the final output is 6.

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6] ();/ / 6
Copy the code

In the above code, the variable I is declared by let, and the current I is only valid for this loop, so each loop I is actually a new variable, so the final output is 6. You might ask, if the variable I for each cycle is redeclared, how does it know the value of the previous cycle and thus calculate the value of the current cycle? This is because the JavaScript engine internally remembers the value of the previous cycle, and when it initializes the variable I of this cycle, it evaluates on the basis of the previous cycle.

Another special feature of the for loop is that the part that sets the variables of the loop is a parent scope, while the inside of the loop body is a separate child scope.

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc
Copy the code

The above code works correctly, printing three ABCs. This indicates that the variable I inside the function and the loop variable I are not in the same scope and have separate scopes.

There is no variable promotion

The “variable promotion” phenomenon occurs when the var command is used, that is, the variable can be used before the declaration and its value is undefined. This is somewhat strange, as normal logic dictates that variables should be used only after the statement has been declared.

To correct this, the let command changes the syntactic behavior so that the variables it declares must be used after the declaration or an error will be reported.

// var case
console.log(foo); / / output is undefined
var foo = 2;
// let
console.log(bar); / / error ReferenceError
let bar = 2;
Copy the code

In the above code, the variable foo is declared with the var command, and the variable promotion occurs. That is, when the script starts running, the variable foo already exists, but has no value, so undefined is printed. The variable bar is declared with the let command, and no variable promotion occurs. This means that the variable bar does not exist until it is declared, and an error will be thrown if it is used.

Temporary dead zone

As long as the let command exists in the block-level scope, the variables it declares are “binding” to the region, no longer subject to external influence.

var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}
Copy the code

In the above code, there is a global variable TMP, but in the block-level scope, let also declared a local variable TMP, causing the latter to bind the block-level scope. Therefore, before let declared the variable, TMP assignment will report an error.

ES6 explicitly states that if there are let and const commands in a block, the variables declared by the block to those commands form a closed scope from the start. Any time these variables are used before declaration, an error is reported.

In short, the variable is not available within the code block until it is declared using the let command. This is grammatically known as a “temporal dead zone” (TDZ).

if (true) {
  / / start TDZ
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError
  let tmp; / / end of TDZ
  console.log(tmp); // undefined
  tmp = 123;
  console.log(tmp); / / 123
}
Copy the code

In the above code, the TMP variable is in the “dead zone” of TMP until the let command declares it.

A “temporary dead zone” also means that Typeof is no longer a 100 percent secure operation.

typeof x; // ReferenceError
let x;
Copy the code

In the above code, variable X is declared using let command, so before declaration, it belongs to the “dead zone” of X, and an error will be reported whenever this variable is used. Therefore, the Typeof runtime throws a ReferenceError.

By comparison, typeof does not generate an error if a variable is not declared at all.

typeof undeclared_variable // "undefined"
Copy the code

In the above code, undeclared_variable is a nonexistent variable name and returns “undefined”. So, before let, typeof operators are 100 percent safe and never report errors. That’s no longer true. This is designed to encourage good programming practice. Variables must be used after declaration, otherwise an error will be reported.

Some “dead zones” are hidden and hard to spot.

function bar(x = y, y = 2) {
  return [x, y];
}
bar(); / / an error
Copy the code

In the above code, calling bar fails (and some implementations may not) because the default value of parameter x is equal to another parameter y, which has not yet been declared and is a “dead zone.” If y defaults to x, no error is reported because x is already declared.

function bar(x = 2, y = x) {
  return [x, y];
}
bar(); / / (2, 2)
Copy the code

In addition, the following code also reports an error, which behaves differently from var.

/ / is not an error
var x = x;
/ / an error
let x = x;
// ReferenceError: x is not defined
Copy the code

The above code error is also due to temporary dead zone. When declaring a variable with let, an error is reported whenever the variable is used before the declaration is complete. The above line is an example of this situation, where the value of x is fetched before the declaration of variable x is completed, causing an error “x undefined”.

ES6 makes it clear that temporary dead zones and let and const statements do not promote variables. The main purpose of ES6 is to reduce runtime errors and prevent the variable from being used before it is declared, resulting in unexpected behavior. Such errors are common in ES5, and with this provision, it’s easy to avoid them.

In short, the essence of a temporary dead zone is that the variable you want to use already exists as soon as you enter the current scope, but you can’t get it until the line of code that declared the variable appears.

Duplicate declarations are not allowed

Let does not allow the same variable to be declared twice in the same scope.

/ / an error
function func() {
  let a = 10;
  var a = 1;
}
/ / an error
function func() {
  let a = 10;
  let a = 1;
}
Copy the code

Therefore, arguments cannot be redeclared inside functions.

function func(arg) {
  let arg;
}
func() / / an error
function func(arg) {{let arg;
  }
}
func() / / is not an error
Copy the code