1. Disadvantages of VAR declaration variables in ES5

ES5 provides two methods for declaring variables: the var command and the function command. Using the function command to declare functions will not be discussed here; we will focus on the var command.

The var command has three features:

  • Variables can be declared repeatedly;
  • Variable declarations will be promoted;
  • No block-level scope;

These three characteristics make the compilation of JS code appears to be a little “arbitrary”, not standard, and such syntax logic is also counterintuitive, to give two examples:

1.1 Inner variables overwrite outer variables of the same name

We used var to declare a global variable num with a value of 1, and then redeclared num in the show scoped if block with a value of 2

var num = 1;

function show() {
  console.log(num);
  if (false) {
    var num = 2;
  }
}

show(); // undefined
Copy the code

The final output of the show function is undefined. This is because the variable declared by var has no block-level scope. The second redeclaration of num will be promoted to the beginning of the function, overwriting the outer global variable of the same name, and the output must be undefined.

1.2 Loop variables used to count in various loop structures are exposed as global variables

Loop structures declare a loop’s control variables (such as I, j, k, and so on), but when the loop ends, it does not disappear and leaks into global variables.

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

Better yet, we want it to work only within the loop control and expire automatically when the loop ends without affecting the rest of the code execution. At this time, we need to use our new LET in ES6.

2. Let, const basis

2.1 Basic usage of let

The let command is an addition to the ES6 standard for declaring variables. Its mission is to replace the var command, its usage is similar to var, but it makes up for the design defects of VAR.

Difference: The let command can declare the current code block as block-level scope. Variables declared with the let command are only valid within the current code block. (Code block in a pair of curly braces {})

{
  let a = 1;
  var b = 2;
}

console.log(a) // ReferenceError: a is not defined.
console.log(b) / / 2
Copy the code

Accessing variable A outside the code block raises an undefined error, while variable B defined using var is still accessible.

Now that you have the let command, you have a good solution to the second problem at the beginning of this article: the leak of loop variables. We declare the counter I for the for loop as let:

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

The variable I is inaccessible.

Lovetof1: Lovetof1

The let command is used in the loop, and one other thing to note is that the variable I is redeclared each time.

Use the loop variable I declared by var, and add a timer function to each loop to print the loop variable I:

for(var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); / / 3 3 3
  }, 1000);
}
Copy the code

Three 3’s are printed because the loop variable I is always the same throughout the for loop, and at the end of the loop variable I is assigned 3. Then change the let command to print again:

for(let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); / / 0 1 2
  }, 1000);
}
Copy the code

You can see that the output is the same as if the timer function was not added, because the variable I is redeclared in each loop. The reason it still outputs as normal logic is that 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.

Lovetoday extension 2: Lovetoday

The for loop itself has another special feature: 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 = 'zevin';
  console.log(i); // zevin zevin zevin
}
Copy the code

Zevin output 3 times. This indicates that the variable I inside the loop body and the loop variable I are not in the same scope and have separate scopes.

2.2 Basic usage of const

The const command is used to declare a read-only constant. The value must be assigned at the beginning of the declaration and cannot be changed once declared.

const PI;
// SyntaxError: Missing initializer in const declaration

const PI = 3.1415926;
PI = 3.14;
// TypeError: Assignment to constant variable.
Copy the code

The const command actually guarantees that the value of the variable is not changed, but that the data stored at the memory address to which the variable points is stored is not changed.

For simple data of basic types (values, strings, booleans), the memory address that the variable points to holds the value itself, so it is equivalent to the value of the variable.

const obj = {};

// Attributes can be added
obj.name = 'zevin';
console.log(obj.name);
// zevin

// Making obj point to another object will cause an error
foo = {}; 
// TypeError: "obj" is read-only
Copy the code

For reference data types (mainly arrays, objects), the memory address that a variable points to holds the reference address, but the reference address cannot be changed. For arrays, objects themselves, we can still add or remove elements.

3. ES6 variable declaration new specification

In order to improve the declaration status of var command in ES5 and improve the standardization of JS language, ES6 proposes the following four new variable declaration specifications, which are applicable to both let and const commands.

3.1 Block-level scope

ES5 has only global and functional scopes, but block-level scopes have finally been added to ES6. Variables declared using let or const are valid only in the block-level scope in which they are declared.

{
  let a = 1;
  if(true) {const a = 2;
  };
  console.log(a); / / 1
}
Copy the code

Code blocks in a pair of curly braces {} unit, each other can be nested arbitrarily, and do not affect each other.

{{{{{const age = 12 }
  console.log(age); // age is not defined
}}}};
Copy the code

The code above uses a five level block-level scope, with each level being a separate scope. The fourth scope cannot read the internal variables of the fifth scope.

3.2 There is no variable promotion

In ES6, we fixed this syntax. Variables declared by let and const must be used after the declaration. Otherwise, an error will be reported.

console.log(a); // undefined 
console.log(b); // ReferenceError: Cannot access 'b' before initialization
console.log(c); // ReferenceError: Cannot access 'c' before initialization

var a = 1;
let b = 2;
const c = 3;
Copy the code

3.3 Temporary dead zones

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. 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.

❀ expand ❀

The advent of “temporary dead zones” also means that Typeof is no longer an error-free operation.

console.log(typeof a); // undefined
console.log(typeof b); // ReferenceError
const b = 1;
Copy the code

The variable b is “dead zone” until it is declared const, and an error is reported whenever it is used. Therefore, the Typeof operator throws a ReferenceError. However, if a variable is not declared at all (variable A), typeof does not return an error, but undefined.

3.4 Do not allow repeated declarations

ES6 does not allow you to declare the same variable twice in the same block scope.

if(true) {var a = 1;
  let a = 2;
  const a = 3;
}
// SyntaxError: Identifier 'a' has already been declared
Copy the code

Similarly, you can’t use let or const to redefine a variable with the same name as a parameter inside a function, but you can use var.

function func(num) {
  let num = 1;
  console.log(num);
}
func() // SyntaxError: Identifier 'num' has already been declared
Copy the code
function func(num) {
  var num = 1;
  console.log(num);
}
func() / / 1
Copy the code

Since different block-level scopes do not affect each other, we can define variables with the same name in different block-level scopes.

So the following code does not report an error:

function func(num) {
  var num = 1;
  if(false) {let num = 2;
    console.log(num); / / 2
  }else{
    const num = 3;
    console.log(num); / / 3
  }
  console.log(num); / / 1
}
func()
Copy the code