JavaScript Scoping & Hoisting

var a = 1;

function foo() {
    if (!a) {
        var a = 2;
    }
    alert(a);
};

foo();

What is the result of the above code when it is run?

Although this is a piece of cake for experienced programmers, I will describe it along the lines of common beginners:

  1. Created a global variable  a, defined its value as 1

  2. created function foo

  3. In  foo the body of the function, the if statement will not be executed because  the !a variable will be  a converted to a boolean false value, i.e. false

  4. skip conditional branches, alert variables  a, the final result should be the output 1

Well, seemingly impeccable reasoning, but surprisingly: the answer turned out to be  2! Why?

Don't worry, I'll explain it to you. First let me tell you that this is not a bug, but an (unofficial) feature of the JavaScript language interpreter that someone ( Ben Cherry ) called: Hoisting (there is no standard translation yet, the more common one is hoisting ).

Declaration and Definition

To understand Hoisting, let's first look at a simple case:

var a = 1;

Have you ever wondered what exactly happens when the above code is running?
Do you know which of the two statements "declare a variable  a" or "define  aa variable" is true for this code?

  • The following example is called "declaring a variable":

var a ;
  • The following example is called "defining a variable":

var a = 1;
  • Declaration: means that you claim the existence of something, such as a variable or a function; but you do not say what such a thing is, just tell the interpreter that such a thing exists;

  • Definition: It means that you specify the specific implementation of something, such as what is the value of a variable, what is the function body of a function, and exactly expresses the meaning of such a thing.

in conclusion:

var a;            // 这是声明
a = 1;            // 这是定义(赋值)
var a = 1;        // 合二为一:声明变量的存在并赋值给它

 Here comes the point: when you think you've only done one thing (var a = 1), the interpreter actually breaks it down into two steps, a declaration (var a) and a definition (a = 1).

How does this relate to Hoisting?

Going back to that confusing example at the beginning, I tell you how the interpreter analyzes your code:

var a;
a = 1;

function foo() {
    var a;        // 关键在这里
    if (!a) {
        a = 2;
    }
    alert(a);     // 此时的 a 并非函数体外的那个全局变量
}

As shown in the code, the interpreter declares a new variable after entering the function body,  aand its value is at that time  undefined, so  the result if of the statement conditional judgment is true, and then the new variable is  a assigned a value of  2. If you don't believe it can be outside the function body  alert(a), and then execute  it and foo() compare the results.

Scoping (scope)

Someone might ask: "Why not  if declare the variable inside the statement  a?"

Because JavaScript does not have block scoping, only function scoping, so seeing a pair of curly braces  {} means that a new scope has been created, which is different from C!

When the parser reads  if the statement, it sees that there is a variable declaration and assignment, so the parser hoists its declaration to the top of the current scope (this is the default behavior and cannot be changed), this behavior is called  Hoisting .

OK, everyone understands, do you understand...

If you understand it, it doesn't mean you will use it. Take the first example, what if I just want to  come up alert(a) with that  1 ?

Create new scope

alert(a) When executing, it will look for  a the location of the variable, it starts from the current scope upwards (or outwards) until it reaches the top-level scope, and reports if it is not found  undefined.

Because in  alert(a) the same-level scope, we declare the local variable again  a, so it reports  2; so we can  a move the declaration of the local variable down (or inward), so that  alert(a) it cannot be found.

Remember: JavaScript only has function scope!

var a = 1;

function foo() {
    if (!a) {
        (function() {        // 这是上一篇说到过的 IIFE,它会创建一个新的函数作用域
            var a = 2;       // 并且该作用域在 foo() 的内部,所以 alert 访问不到
        }());                // 不过这个作用域可以访问上层作用域哦,这就叫:“闭包”
    };
    alert(a);
};

foo();

You've probably read in countless JavaScript books and articles: "Always keep the declarations of all variables in scope at the top of the scope", now you know why? Because this can avoid the troubles caused by the Hoisting feature (I'm not very willing to say this, because there is nothing wrong with Hoisting itself), and it can also clearly tell everyone who reads the code (including yourself) that there are in the current scope. which variables can be accessed. However, the hoisting of variable declarations is not the whole story of Hoisting. In JavaScript, there are four ways to bring names into scope (by precedence):

  1. Language-defined naming: like  this or  arguments, they're valid in all scopes and have the highest precedence, so anywhere you can't name this a variable something like that, it doesn't make sense

  2. Formal parameters: Formal parameters declared when a function is defined will be hoisted into the scope of the function as variables. So the formal parameters are local, not external or global. Of course, you can pass in external variables when executing the function, but after passing in, it is local

  3. Function declaration: Functions can also be declared inside the function body, but they are also local

  4. Variable declaration: This priority is actually the lowest, but they are also the most commonly used

Also, remember when we discussed  the difference between declarations  and  definitions earlier  ? I didn't say at the time why you should understand the distinction, but now is the time to remember: Hosting only lifts the naming, not the definition

Hosting only promotes the naming, not the definition

This is closely related to what we are going to talk about next, please see:

Let's look at two examples:

function test() {
    foo();

    function foo() {
        alert("我是会出现的啦……");
    }
}

test();

//////////////////////////////
function test() {
    foo();

    var foo = function() {
        alert("我不会出现的哦……");
    }
}

test();

Classmates, after learning about Scoping & Hoisting, do you know how to explain all this?

In the first example, the function  foo is a declaration, and since it is a declaration, it will be promoted (I deliberately wrapped an outer scope, because the global scope requires your imagination, not so intuitive, but the reason is the same), So the  foo() scope knows about  foo the existence of the function before it is executed. This is called a function declaration , and the function declaration is hoisted to the top of the scope along with the name and the function body.

In the second example, however, only the variable name is hoisted  foo, and its definition remains in place. Therefore, before execution  foo() , the scope only knows  foo the name, not what it is, so the execution will report an error (usually: undefined is not a function). This is called a function expression , and only the name of the function expression will be hoisted, the body of the defined function will not.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324455223&siteId=291194637