吊装和可变范围

Hoisting and variable scope

谁能帮忙解释一下为什么下面两段代码打印出不同的结果?

区别在于条件语句。首先,有一个 'Jack' 的局部变量赋值给 name,条件为真(意味着 !name 的计算结果为真)。在第二个中,相同的名称 'Jack' 被分配给全局变量名称,但条件是假的(!名称是假的)。我的问题是,如果其他一切都相同,如果更改的是条件主体 inside,为什么第一个条件为真而第二个为假?

我唯一的解释是JS解释器首先读取条件语句的主体,这就是它如何确定name是否为global/local ,是否需要提升变量声明,最后,记录不同的名称值。

难道不应该在开始解释其主体之前先评估条件布尔值吗?然后在这两种情况下,变量 'name' 将被评估为 'undefined' ...任何帮助将不胜感激!

有一些关于 hoisting/scope 上下文的非常好的资源,但我没有找到专门回答这个问题的资源。

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"

对比

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

当您使用 var 时,您是在 current 范围内实例化一个变量。 - 在第一种情况下,是 user 函数的作用域。

当您不使用 var 时,您只是在该范围内没有该变量 (function)。由于您已经在当前范围 (globally) 之外实例化了变量 name,因此您将其作为变量 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"

其他情况:

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"

变量声明提升到执行上下文的顶部,在本例中是函数用户。重写这些以显示从提升的角度来看它的外观通常会消除任何混淆

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

对比

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

执行上下文包含几个关键组件,这里最相关的是词法环境和变量环境。如果您在这里感兴趣,我将更深入地介绍两者之间的差异(以及一些简短的历史):

是顺序!

优先顺序。在解析期间的文档代码中,函数在代码开始执行之前得到评估。但是声明的变量在任何给定的上下文中都有更高的优先级。只有在所有变量都被实例化之后,函数才被允许运行。

这就是为什么带有已声明变量 "name" 的函数 return 使用其本地名称的值。因为它已经存在并且函数不必 'look up' 为其在外部范围内的值。


编辑

为了更深入的理解,这里有一个更有趣的相同案例的例子:

var name = "Paul";

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

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

那么刚刚发生了什么?

声明 name = "Dietrich" 不是应该针对全局 'name' 变量值吗?

为什么这个功能不像以前那样在 returning "Jack" 中持续存在?或者 - 为什么输出突然不再是 "Jack",当然也不再是 "Paul",而是奇怪且完全出乎意料的 "Dietrich"?!

-出于同样的原因,它坚持重新调整 "Jack" 而不是遵循函数语义及其条件建议的预期内容,即 "Paul"。

这是出于上述原因。乍一看,函数声明行 name = "Dietrich" 明确针对全局 "Paul" 比起,我们有额外的预防措施,一个条件应该阻止 "Jack" 的执行,因为已经有一个变量 "name" 可以从外部范围获得。但无济于事...

而更令人困惑的是 - 全局 "Paul" 仍然完好无损!

我们仅从我们的阅读点将外部范围的 "Dietrich" 分配给 'name' 变量。因为 var(s) 在函数之前并且在函数体声明开始执行之前很久就被求值了。

并且由于 if(condition){ doesn't create a scope of its own } 我们在函数体中声明 'name' 变量有多深并不重要。现在每个方程都已经解决了。

name = "Dietrich" 将不再修改外部作用域的全局 'name',因为 'name' 变量已经存在于这个(局部)函数作用域中,所以通常 "Dietrich" 正在覆盖本地 "Jack" 而不是宇宙的搭便车 "Paul"。那是因为 var name 已经在当前范围的某处定义。

这与之前 return 'unexpected' "Jack" 的原因相同。就这样。