Touching the blind spot, this question is crazy in front of the group

This article comes from the reader's contribution and introduces a title of the creation function that appears in the if statement, but it has different effects in different browsers, and the differences are introduced at the end of the article. It's fine to use function expressions instead when writing code. Take Chrome as an example to explain this question in detail.

Today, in the front-end communication group, a friend lost a question, ask what is this output?

At first glance, the output is 21, do you think if you add an if (true), I do n’t know?

Then, no more ...

"Glory" is wrong!

Correct answer: internal is 21, external is 1;

In fact, this mystery is indeed inside the block-level scope if.

For example, we remove the if topic.

    var a = 0;
    // if(true){
     a = 1;
    function a(){}
    a = 21;
    console.log("里面",a);
    // }
    console.log("外部",a);

It is estimated that no one has asked this question in a good way. There is no doubt that the output a is 21.

However, it is said in JS that there is no block-level scope, so why does this block affect the final result?

The main points of this article:

  • Magical let
  • Function block scope

What is Hosting?

Mainly divided into variable promotion and function promotion.

Variable promotion

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

beginner level

Topic:

console.log(a);
var a = 0;

This does not report: Uncaught ReferenceError: a is not defined.

Instead, it will output undefined.

Because the result after variable promotion is:

var a;
console.log(a);
a = 0;

Into class

Example 1:

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

If let x does not promote variables, then x should output 0, which is actually:

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

It is not an error x not defined, but Cannot access.

What is the reason for this error?

Example 2:

let a = a;
let a = a;

What error do you think will be reported?

“ a not defined ” 或者 “Cannot access 'a' before initialization” ?

Not actually, but an error: a has already been declared.

Here it looks like: let will also "variable promotion", if it will not be promoted, x in example 1 should output 0, and example 2 should report an a not defined error.

But if the variables will be promoted, it will not be justified, then the above example 1 should output undefined.

So we call this "temporary dead zone".

In fact, this is neither a variable promotion nor a variable promotion that we understand. What is a temporary dead zone?

Let defines a variable with a "special declaration" process. When JS pre-parses, let the defined let and const "special declaration" be advanced in advance, similar to "raise hand". The JS engine specifies the same scope and the same variable Can only be "raised" once.

This is different from the definition and assignment of var. The declaration of var is that if it has already been declared, the latter directly ignores the declaration.

We continue to return to this topic.

let a = a; // 定义变量 a,我暂标识为 a1
let a = a; // 定义变量 a,我暂标识为 a2

Pre-parse, declare a1, and then prepare to declare a2. At this time, the JS engine found that when a2 was declared, a1 was already declared.

Therefore, it violates the rule of "the same scope, the same variable can only be declared once", and an error is reported directly. In fact, the a variable assigned in the code has not been read yet (the variable undefined error may be thrown when the variable is read).

Therefore, an error is reported, the error content: a2 has been declared (a is declared by a1).

So back to Example 1 above, when the code reads x, it finds that there is x declared by let, but it is not initialized, and then directly reports the error x cannot be accessed.

So what is the magic of let variable "special declaration"?

In fact, it is the JS engine that introduces the declareData that was introduced when the let variable was promoted. During pre-resolution, it stores all the let and const declaration data in the scope.

In fact, the creation of all functions and variables in the scope needs to check whether they conflict with the value of declareData.

Example 3:

var a = 1;//定义变量 a,我暂标识为 a1
let a = 2;//定义变量 a,我暂标识为 a2

declareData declares the variable a2, then prepares to define the variable a1, and finds that declareData has already declared a2, and directly reports an error: a1 has already been declared because the variable a has already been declared by a2.

Function promotion

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

Function expression

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

console.log(a); // function a
function a(){}

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

Block scope

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

In the case of variable promotion, there is no block-level scope, but function promotion does exist. This pre-analysis is as follows:

var a; //  函数 a 的声明
console.log(a);// undefined
if(true){
    function a(){} // 函数 a 的定义
    console.log(a); // function a
}

In fact, after the function a () {} is preparsed, the function declaration is brought to the front of the function-level scope, and then the function definition is promoted to the front of the block-level scope.

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

Take another question:

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

The a defined in the catch is declared earlier. But the e in the catch is not accessible from the outside. If you think that catch is a "function scope", then the a inside should not be promoted to the outermost level. In fact, the block scope is followed in the catch.

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

Look at the original question again

In order to facilitate reading, I will post the topic again:

var a = 0;
if(true){
    a = 1;
    function a(){}
    a = 21;
    console.log("里面",a);
}
console.log("外部",a);

Combined with the knowledge we learned above. First, the function a () {} in if will declare the promotion, move the declaration "var a" to the front of the function-level scope, and move the function definition to the front of the block-level scope. The pre-resolution is as follows:

var a;// 函数 a 的声明提前
var a = 0;  // 已经声明了 a,这里会忽略声明 ,直接赋值为 0
if(true){
    function a(){} // 函数定义 a 声明提升到块级最前面
    a = 1; // 这里将 块级作用域最前面的函数 a 重置为 1了。
    // function a(){};  how do ?
    a = 21;
    console.log("里面",a);
}
console.log("外部",a);

There is also a problem here, the function itself is [define function name variable pointer to function memory block].

Function promotion is at block level scope, but function name variables are function level scope. Therefore, when the block-level function definition (the original code function declaration position), the function name variable is synchronized to the function-level scope, in fact, only this time, the function definition can be accessed outside the block-level scope.

The pre-parse is as follows:

var a = 0;
if(true){
    console.log(a,window.a);// 函数提升,是块级作用域,输出 function a 和 0
    a = 1;  // 取作用域最近的块级作用域的 function a ,且被重置为 1了,本质又是一个 变量的赋值。
    console.log(a,window.a);// a 是指向块级作用域的 a, 输出 1 和 0 
    function a(){} // 函数的声明,将执行函数的变量的定义同步到函数级的作用域。
    console.log(a,window.a);// 输出 1 和 1
    a = 21; // 仍然是函数定义块级作用域的 a ,重置为 21
    console.log(a,window.a); // 输出为函数提升的块级作用域的 a, 输出 21,1
    console.log("里面",a);
}
console.log("外部",a);

So far, you should understand it, and you will never get it wrong again! If you don't understand it, try it again.

Differences between different browsers

Differences are referenced from MDN

var hoisted = "foo" in window;
console.log(`'foo' name ${hoisted ? "is" : "is not"} hoisted. 
  typeof foo is ${typeof foo}`);
if (true) {
  function foo(){ return 1; }
}
  • In Chrome: foo variable name is promoted, but typeof foo is undefined

  • In Firefox: foo variable name is promoted. But typeof foo is undefined

  • In Edge: foo variable name is not promoted. And typeof foo is undefined

  • In Safari: foo variable name is promoted. And typeof foo is function

Change if (true) in the above code to if (false), the result is the same.

This article is reproduced from: https://mp.weixin.qq.com/s/YQEBZo1pdy-5B1Jz8cvWmw

Guess you like

Origin www.cnblogs.com/AAABingBing/p/12711193.html