There’s no end to learning. Why is there always so much?

Today in a technical group, a friend lost a question, asking what is this output?

If (true) = 21; if(true) = 21

Then, there is no then …………

“Glorious” was wrong!

Correct answer: inside 21, outside 1;

The mystery really lies in the block-level scope if.

Let’s say we get rid of the if.

var a = 0;
// if(true){
 a = 1;
function a(){}
a = 21;
console.log("Inside",a);
// }
console.log("External",a);
Copy the code

No one is going to ask this question, but of course, the output of a is 21.

JS has no block-level scope, so why does the if block affect the final result?

The following is an implementation in Chrome

Main points of this paper:

  • The magic of the let
  • Function block-level scope

What is Hosting?

It is mainly divided into variable promotion and function promotion.

Variable ascension

The promotion of variables is determined by the scope of variables, that is, variables declared in the global scope will be promoted to the top level of the global scope, while variables declared in the function will only be promoted to the top level of the function scope.

Entry level

Look at the topic:

console.log(a);
var a = 0;
Copy the code

This is not reported: Uncaught ReferenceError: A is not defined.

It’s going to print undefined.

Because the result of variable promotion is:

var a;
console.log(a);
a = 0;
Copy the code

In the class

Example 1:

var x = 0;
function a(){
    console.log(x);
    let x = 1;
}
a();
Copy the code

If let x is not variable promoted, then x should print 0, which is actually:

VM296:3 Uncaught ReferenceError: Cannot access ‘x’ before initialization at a (:3:14) at :1:1

And instead of saying error x not defined, it says Cannot access.

What is the reason for this error?

Example 2:

let a = a;
let a = a;
Copy the code

What errors do you think will be reported?

“A not defined” or “Cannot access ‘a’ before initialization”?

A has already been declared.

Let is also “variable promoted”. If it is not promoted, x in example 1 should print 0, and example 2 should report an error a not defined.

Example 1 above should print undefined.

So we call this a “temporary dead zone.”

In fact, this is neither a variable lift as we understand it nor a variable lift without it. So what is a temporary dead zone?

Let variable definition has a “special declaration” process. When JS is pre-parsing, the let,const “special declaration” is first defined, similar to “raise hands”. The JS engine specifies the same scope, the same variable can only be “raised” once.

This differs from the definition and assignment of var, which ignores the declaration if it is already declared.

Let’s go back to the problem.

let a = a; // define variable A, which I'll temporarily label a1
let a = a; // define variable A, which I'll temporarily call a2
Copy the code

A1 is declared, and then it is ready to declare a2. At this point, the JS engine finds that a1 has already been declared when a2 is declared.

The same variable can only be declared once in the same scope. In fact, the assigned a variable in the code has not been read (undefined errors can only be thrown when a variable is read).

Error: A2 has been declared (a1 declared a).

Therefore, back to example 1 above, when the code reads x, it finds that x has been declared by let but has not been initialized, so it directly reports an error that X cannot be accessed.

So what is the magic of the let variable “special declaration”?

DeclareData is used by the JS engine to solve the problem of let variable promotion. It stores all the let and const declaration data in the scope during pre-parsing.

In fact, all functions and variables created in a scope need to be checked for conflicts with the value of declareData.

Example 3:

var a = 1;// define variable A, which I'll temporarily label a1
let a = 2;// define variable A, which I'll temporarily call a2
Copy the code

If a1 is declared by declareData, then a1 is declared by declareData. If a1 is declared by declareData, a1 is declared by A2.

Function increase

Function promotion, similar to variable promotion, but slightly different.

Functional expression

console.log(a);// undfined
var a = function (){}

console.log(a); // function a
function a(){}
Copy the code

Function expressions do not declare promotion. The first example prints undefined instead of not defined because the variable var a is promoted.

Block-level scope

console.log(a);// undefined
if(true) {console.log(a); // function a
    function a(){}}Copy the code

For variable promotion, there is no block-level scope, but there is a function promotion.

var a; // The declaration of function a
console.log(a);// undefined
if(true) {function a(){} // The definition of function a
    console.log(a); // function a
}
Copy the code

Function a(){} moves the function declaration to the front of the function scope after pre-parsing, and then moves the function definition to the front of the block scope.

Note: The function definition here is promoted to the front of the block-level scope.

Let’s do another one:

try{
    console.log(a);// undefined
    aa.c;
}catch(e){
    var a = 1;
}
console.log(a);/ / 1
console.log(e);// Uncaught ReferenceError: e is not defined
Copy the code

A, defined in catch, is declared ahead of time. But e in the catch is not externally accessible. If you think of catch as a “function scope,” then the inside a should not be promoted to the outermost layer. The catch actually follows block scope.

In the JS world, there is block-level scope (outside of let const).

Look at the original problem

For the convenience of reading, I will post the title again:

var a = 0;
if(true){
    a = 1;
    function a(){}
    a = 21;
    console.log("Inside",a);
}
console.log("External",a);
Copy the code

So with what we’ve learned above. Function a(){} function a(){} function a(){} function a(){}

var a;// the declaration of function a is advanced
var a = 0;  // A is already declared, so the declaration is ignored and the value is set to 0
if(true) {function a(){} // The function definition a declaration is promoted to the top of the block level
    a = 1; // This resets the first function of the block-level scope to 1.
    // function a(){}; How to do?
    a = 21;
    console.log("Inside",a);
}
console.log("External",a);
Copy the code

The function itself is [define function name variable pointer to function memory block].

Function promotion is at the block level scope, but function name variables are at the function level scope. So the function name variable is synchronized to the function scope at the block level function definition (where the original code function is declared), and only then can the function definition be accessed outside the block level scope.

The pre-resolution is as follows:

var a = 0;
console.log(a,window.a); // Outputs 0 and 0
if(true) {console.log(a,window.a);// function promotion, which is block-level scope, outputs function a and 0
    a = 1;  // Take the nearest block-level scope function a, reset to 1, essentially a variable assignment.
    console.log(a,window.a);// a refers to a of the block-level scope, and outputs 1 and 0
    function a(){} // The function declaration synchronizes the definition of the variable executing the function to the function-level scope.
    console.log(a,window.a);// Outputs 1 and 1
    a = 21; // still a of function definition block-level scope, reset to 21
    console.log(a,window.a); // the output is a of the block-level scope promoted by the function, with output 21,1
    console.log("Inside",a);
}
console.log("External",a);
Copy the code

So far, should all understand, will not answer wrong again! If you don’t understand, watch it a few more times and try.