Hoisting and variable scope

一世执手 提交于 2019-12-02 01:28:29

问题


Would someone help explain why the two snippets of code below print out different results?

The difference is inside the conditional statement. In the first, there is a local variable assignment of 'Jack' to name, and the conditional is truthy (meaning !name evaluates to true). In the second, the same name 'Jack' is assigned but to the global variable name, but the conditional is falsy (!name is false). My question is, if all else is the same, why would the first conditional be true and the second is false if what is changed is inside the conditional body?

My only explanation is that the body of the conditional is read by the JS interpreter first, which is how it determines whether name is global/local, whether the variable declaration needs to be hoisted or not, and finally, the different name values logged.

Shouldn't the conditional boolean be evaluated first before even starting to interpret its body? Then in both cases, variable 'name' would be evaluated as 'undefined'... Any help would be greatly appreciated!

There are a few really good resources about hoisting/scope contexts but I'm not finding one that specifically answers this question.

http://javascriptissexy.com/javascript-variable-scope-and-hoisting-explained/

var name = "Paul"; 
function users () {
    if (!name) {
        var name = "Jack";  //<== difference is here, *inside* conditional body
    }
    console.log(name);
}
users(); //outputs "Jack"

vs.

var name = "Paul"; 
function users () {
    if (!name) {
       name = "Jack";  //<== difference is here, *inside* conditional body
    }
    console.log(name);
}
users();  //outputs "Paul"

回答1:


Variable declarations hoist to the top of the execution context, in this case the function users. Rewriting these to show how it looks from the hoisted perspective often clears up any confusion

var name = "Paul"; 
function users () {
    var name;//<- hoisted variable declaration
    if (!name) {
        name = "Jack";
    }
    console.log(name);
}
users(); //outputs "Jack"

vs.

var name = "Paul"; 
function users () {
    if (!name) {//no hoisted variable declaration, uses global
       name = "Jack";
    }
    console.log(name);
}
users();  //outputs "Paul"

Execution contexts contain several key components, the most relevant here are the lexical environment and variable environment. I cover the differences between the two (and some brief history) in more depth if you are interested here: https://stackoverflow.com/a/32573386/1026459




回答2:


When you use the var , you are instantiating a variable in the current scope. - which in the first case, is the user function's scope.

When you don't use var, you simply don't have that variable in that scope (function). And since you already instantiated the variable name outside of the current scope (globally), you get that as the variable name

var name = "Paul"; 
function users () {
// var name;  is essentially hoisted to here - the top of the current scope
    if (!name) {
        (var) name = "Jack"; // that hoisted var is set here
    }
    console.log(name);
}
users(); //outputs "Jack"

other case:

var name = "Paul"; 
function users () {
    if (!name) {
       name = "Jack"; // name is hoisted outside of the scope, 
                      // but it already is declared as "Paul"
    }
    console.log(name);
}
users();  //outputs "Paul"



回答3:


It's the order!

The order of precedence. In document code during the parse time Functions get evaluated before the code begins executing. But declared variables have a greater precedence in any given context. And Functions are allowed to run only after all variables has been instantiated.

This is why the function with a declared variable "name" is returning the value of its local name. Because it's there already and the function doesn't have to 'look up' for its value in the outer scope.


EDIT

For deeper understanding, a more interesting example of the same case is here:

var name = "Paul";

   function users () {
          name = "Dietrich";
      if (!name) {
          var name = "Jack";
      }
      console.log(name);
   }

users(); // outputs "Dietrich"
console.log(name);  // again outputs "Paul"

So what just happened?

Isn't the declaration name = "Dietrich" supposed to be targeting the global 'name' variable value?

Why doesn't this same function persist in returning "Jack" as it did before? Or -Why is the output suddenly not "Jack" and of course not "Paul" anymore but strange and completely unexpected "Dietrich"?!

-It's for the very same reason it persisted retuning "Jack" instead of what following the function semantics and its conditional suggests to be expected, and that is "Paul".

It's for the reasons explained above. At the first sight and line of function declaration we have name = "Dietrich" clearly targeting the global "Paul" Than, we have the additional precaution, a conditional which should prevent the execution of "Jack" since there is already a variable "name" available from the outer scope. But to no avail...

Whereas to make matters even more confusing - the global "Paul" is still intact!

Wee are assigning "Dietrich" to 'name' variable from the outer scope only from our reading point. Because var(s) get evaluated before functions and long before the function body declarations start to execute.

And since if(condition){ doesn't create a scope of its own } it doesn't matter how deep in that function body do we declare the 'name' variable. Every equation has been resolved by now.

name = "Dietrich" will NOT modify the global 'name' of the outer scope anymore, because 'name' variable is already present in this (local) function scope, so normally "Dietrich" is overwriting the local "Jack" not the hitchhiker "Paul" of the Universe. And that's because var name has been already defined somewhere along the current scope.

So it's the same reason it used to return the 'unexpected' "Jack" before. And that's all.



来源:https://stackoverflow.com/questions/37287609/hoisting-and-variable-scope

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!