1.var

Before ES6 we used to define JavaScript variables with the var keyword. The let and const keywords were added in ES6

var num = 1

Declare a variable in the global scope using var. By default, it is mounted in the window object (Node is global).

var num = 1
console.log(window.num) / / 1
Copy the code

The scope of a variable declared with var is its current execution context, which can be a function or global

var x = 1 // Declare in global scope

function foo() {
    var x = 2 // Declare in the scope of foo
    console.log(x) / / 2
}
foo()
console.log(x) / / 1
Copy the code

If x is not declared in foo, but is assigned, then x is assigned in the outer scope of foo

var x = 1 // Declare in global scope

function foo() {

    x = 2 / / assignment

    console.log(x) / / 2

}

foo()

console.log(x) / / 2
Copy the code

If assigned to an undeclared variable, the variable is implicitly created as a global variable (it becomes a property of the top-level object).

a = 2
console.log(window.a) / / 2
function foo(){
    b = 3
}
foo()
console.log(window.b) / / 3
Copy the code

Var defect 1: All undeclared and directly assigned variables will automatically hang under the top-level object, resulting in uncontrollable and chaotic global environment variables

Variable promotion (hoisted)

Variables declared with var are promoted

console.log(b) // undefined
var b = 3Note that the promotion is only a variable declaration and does not affect the initialization of its value, which can be implicitly understood as:var b
console.log(b) // undefined
b = 3
Copy the code

Scope rule

The var declaration can be accessed anywhere within the enclosing function, module, namespace, or global scope. The enclosing code block has no effect on this declaration, so declaring the same variable multiple times does not cause an error:

var x = 1
var x = 2
Copy the code

Such scoping rules can cause errors

function sumArr(arrList) {
    var sum = 0;
    for (var i = 0; i < arrList.length; i++) {
        var arr = arrList[i];
        for (var i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
    }
    return sum;
}
Copy the code

It’s easy to see some problems here. The inner for loop overrides the variable I because all I refer to variables in the same function scope. Experienced developers are well aware that these issues can slip through the cracks during code reviews and cause endless headaches.

Var bug 2: Allow the same variable to be declared multiple times without error, making code not easy to maintain

Catch variable quirks

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

At the end of the for loop, I =10, so a [6] () is also 10, and all elements of a have I of 10

2.let

Let is written in the same way as var, except that it uses block scope

let a = 1

Block-scoped variables are not accessible outside the block or for loop that contains them

{
    let x = 1
}
console.log(x) // Uncaught ReferenceError: x is not defined
Copy the code

So:

var a = []; for (let i = 0; i < 10; A [I] = function () {console.log(I); }; } // The JavaScript engine internally memorizes the value of the last round of the loop, initializes the variable I, and computates a[6]() on the basis of the last round; / / 6Copy the code

At the same time, let addresses two defects of var:

Variables declared in global scope using let are also not properties of top-level objects*

let b = 2
window.b // undefined
Copy the code

Repeated declarations in the same block are not allowed

let x = 1
let x = 2
// Uncaught SyntaxError: Identifier 'x' has already been declared
Copy the code

It can be declared if it’s in a different block

{
    let x = 1
    {
        let x = 2}}Copy the code

The act of declaring the same variable name in a nested scope is called masking, and it perfectly solves the sumArr problem above:

function sumArr(arrList) {
    let sum = 0;
    for (let i = 0; i < arrList.length; i++) {
        var arr = arrList[i];
        for (let i = 0; i < arr.length; i++) { sum += arr[i]; }}return sum;
}
Copy the code

You will get the correct result because the I of the inner loop can mask the I of the outer loop

Masking should be avoided in general because we need to write clear code. There are also scenarios where it can be used, and you need to plan for them

3. The difference between var and let

(1) Scope

The scope of a variable declared with var is its current execution context, i.e. the global execution context if outside any function, or the current function execution context if inside the function. In other words, the scope of variables declared by var can only be global or entire function blocks.

The scope of a variable declared by a let is the code block in which it is currently located, that is, its scope can be either global or the entire function block, or the code block defined by {} for if, while, switch, etc.

In addition, the scope rules for var and let are the same; variables declared are only available in the block or subblock they declare.

Example code:

function varTest() { var a = 1; { var a = 2; // In the function block, the same variable console.log(a); // 2 } console.log(a); // 2 } function letTest() { let a = 1; { let a = 2; // In the code block, the new variable console.log(a); // 2 } console.log(a); // 1 } varTest(); letTest();Copy the code

As you can see from the above examples, the scope of a variable declared by a let can be more flexible than that of a variable declared by var.

(2) Repeat the statement

Var allows repeated declarations in the same scope. Let does not allow repeated declarations in the same scope, otherwise an exception will be thrown.

Var example code:

var a = 1;
var a = 2;

console.log(a) // 2

function test() {
  var a = 3;
  var a = 4;
  console.log(a) // 4
}

test()
Copy the code

Let example code:

if(false) {
  let a = 1;
  let a = 2; // SyntaxError: Identifier 'a' has already been declared
}
switch(index) {
  case 0:
    let a = 1;
  break;

  default:
    let a = 2; // SyntaxError: Identifier 'a' has already been declared
    break;
}
Copy the code

As you can see from the above example, the repetition checking of let declarations occurs during the lexing phase, that is, before the code officially starts executing.

(4) Variable promotion and temporary dead zone

Var declares that a variable has a variable boost. How to understand a variable boost?

To explain this, it involves execution context and variable objects.

When JavaScript code is running, interpreting and executing global code, calling a function, or executing a string expression using the eval function creates and enters a new execution environment called the execution context. Therefore, there are three types of execution contexts: global execution context, function execution context, and eval function execution context.

The execution context can be understood as an abstract object.

A Variable object that stores variables and function declarations that are defined in the execution context.

Scope chain: a list of objects used to retrieve identifiers that appear in context code

ThisValue: this pointer is a special object related to the execution context, also known as the context object. The life cycle of an execution context can be divided into three phases: creation, execution, and release.

All variables declared with var will be created and initialized as attributes of variable objects during the creation phase of the execution context, so as to ensure that the corresponding variable can be found in the variable object through the identifier for the assignment operation during the execution phase.

The following is what happens when you build a variable object from a variable declared by var:

  • Properties of a variable object are created (created and initialized) consisting of a name and a corresponding value (undefined)
  • If the variable name is the same as the declared formal parameter or function, the variable declaration does not interfere with the existing properties. This process is what we call “variables”, which could explain why variables can be used before the statement, because is used in the implementation stage, before the creation stage has been declared variables added to the object, so the execution phase through identifier can be found on variables in the object, there would be no error.

Example code:

console.log(a) // undefined

var a = 1;

console.log(a) // 1
Copy the code

Let declares that a variable has a temporary dead zone.

In fact, let also has a “variable promotion” process similar to var, but unlike var, it only creates variables and is not initialized (undefined) during the creation phase of the execution context. Also, ES6 specifies that its initialization is performed during the execution phase of the execution context (that is, not until their definitions are executed), and that using uninitialized variables will result in an error.

Accessing a variable before it is initialized results in a ReferenceError, so the period of time (process) from the time it is created into the scope to the time it becomes accessible is called Temporal Dead Zone.

Example code 1:

console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined

var bar = 1;
let foo = 2;
Copy the code

Example code 2:

var foo = 33;
{
  let foo = (foo + 55); // ReferenceError: foo is not defined
}
Copy the code

Note: First of all, it is necessary to distinguish the creation, initialization, and assignment of variables as three different processes. In addition, from ES5, the Variable object in ES3 is replaced by Lexical Environment to manage static scope, but the function is the same. For the sake of convenience, the above explanation retains the use of variable objects for description.

summary

  • Variables declared by var are “created” and “initialized” during the execution context creation phase, so for execution, they can be used before the declaration.
  • Variables declared by a let are only “created” and not “initialized” during the execution context creation phase, so for the execution phase, if used before its definition is executed, it is equivalent to using an uninitialized variable and an error will be reported.

4. Let is const

Const is similar to let in that it has the same properties as let, except that it declares a read-only variable and cannot be changed after the declaration. Therefore, if const is declared, it must be initialized, otherwise an error will be reported.

Example code:

let a;
const b = "constant"

a = "variable"
b = 'change' // TypeError: Assignment to constant variable
Copy the code

How do you understand that you are not allowed to change its value after a declaration?

In fact, const does not guarantee that the value of the variable will not change, but that the data stored in the memory address to which the variable points will not be changed (i.e. the value and address in the stack).

There are two types of data types in JavaScript: primitive value types and Object types.

For primitive value types (undefined, null, true/false, number, string), the value is stored at the same memory address (on the stack) that the variable points to, so a const primitive value type variable is equivalent to a constant.

For object types (object, array, function, etc.), the memory address that the variable points to is actually a pointer to the actual data, so const only guarantees that the pointer is unmodifiable, and there is no guarantee that the data structure that the pointer points to cannot be modified (in the heap).

Example code:

const obj = {
  value: 1
}

obj.value = 2

console.log(obj) // { value: 2 }

obj = {} // TypeError: Assignment to constant variable
Copy the code
const user = {
    name: "sisterAn".age: num,
}
user = {
    name: "pingzi".age: num
} // Uncaught TypeError: Assignment to constant variable.

// All of the following are successful
user.name = "Hello"
user.name = "Kitty"
user.name = "Cat"
user.age--
Copy the code

Other const values are the same as let, for example:

· Same scope, valid only in the block-level scope where the declaration is made

· Constant is not promoted, and there is also a temporary dead zone