【问题标题】:Hoisting and variable scope起重和可变范围
【发布时间】:2016-09-14 05:33:34
【问题描述】:

有人能帮忙解释一下为什么下面的两个 sn-ps 代码会打印出不同的结果吗?

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

我唯一的解释是,条件的主体是由JS解释器首先读取的,它是如何判断name是全局/局部的,变量声明是否需要吊装与否,最后,记录了不同的名称值。

不应该在开始解释它的主体之前先评估条件布尔值吗?然后在这两种情况下,变量“名称”将被评估为“未定义”......任何帮助将不胜感激!

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

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"

【问题讨论】:

  • 在一种情况下有一个局部变量,在另一种情况下没有。为什么结果会一样?
  • @Bergi 如果提升的变量声明没有改变条件的逻辑,则返回值将是相同的。
  • “不应该在这两种情况下都将变量 'name' 评估为 'undefined'” 哦不?在第二个示例中,name 不是局部变量。它指的是具有值"Paul" 的全局变量。提升仅适用于变量声明。第二个例子的函数内部没有变量声明,
  • @Felix King:是的,在这种情况下,我知道全局变量和局部变量之间的区别。我知道将var 放在变量前面会创建一个局部变量。我知道声明name = 'Jeff' 会改变全局变量。我实际上感到困惑的部分是,为什么在条件 if (!name) 甚至被评估之前,这些内部语句甚至首先被评估。如果我问一个更有针对性的问题,也许会有所帮助。在条件语句(无论是真还是假)内声明的变量是否被提升到顶部?
  • 这个问题似乎与您帖子中的问题不同(您想知道为什么 both 不是undefined)。无论如何,是的,var 声明的作用域是函数。

标签: javascript variables scope hoisting


【解决方案1】:

当您使用 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 = "Jack" 不会被提升,只是变量声明会。与var name;var name = undefined 相同。
【解决方案2】:

变量声明提升到执行上下文的顶部,在这种情况下是函数用户。重写这些以从提升的角度显示它的外观通常可以消除任何混乱

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"

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

【讨论】:

  • 那么var name = "Paul"; function users () { name = "Dietrich"; if (!name) { var name = "Jack"; } console.log(name); } users(); //outputs "Dietrich" 刚刚发生了什么? :)
  • @BekimBacaj - 名称被“Dietrich”覆盖,对 !name 的检查为假且未输入,然后记录了“Dietrich”。此外,现在全局变量名称已更改为“Dietrich”。
  • 当然可以,但这并不能解释为什么? name="Dietrich" 仍然是全球性的!
  • @BekimBacaj - 在你的切线示例中它从来不是本地的。
  • 感谢您详细说明步骤,Travis。我理解提升和变量声明提升到执行上下文的顶部。我最初的问题是为什么var name 在第一个示例中而不是第二个示例中被提升到顶部。我能想到的唯一解释是 JS 解释器分别在每个场景 var name = "Jack"name = "Jack" 中读取和评估条件的主体,之前条件被评估为真/假,这就是它首先确定需要吊装的方式。这仍然让我感到困惑。
【解决方案3】:

这是命令!

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

这就是为什么带有声明变量“name”的函数返回其本地名称的值。因为它已经存在并且函数不必在外部范围内“查找”它的值。


编辑

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

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" 不应该针对全局“名称”变量值吗?

为什么这个函数不像以前那样持续返回“Jack”?或者 - 为什么输出突然不再是“Jack”,当然也不再是“Paul”,而是奇怪且完全出乎意料的“Dietrich”?!

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

出于上述原因。乍一看和函数声明,我们有name = "Dietrich" 明确针对全球“保罗” 比,我们有额外的预防措施,一个条件应该阻止“Jack”的执行,因为外部范围已经有一个变量“name”可用。但无济于事...

而更令人困惑的是 - 全局“保罗”仍然完好无损!

Wee 仅从我们的阅读点将“Dietrich”分配给外部范围的“name”变量。因为 var(s) 在函数之前和函数体声明开始执行之前很久就被评估了。

由于if(condition){ doesn't create a scope of its own },我们在函数体中声明“name”变量的深度并不重要。到此为止,所有方程都已解决。

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

所以这与之前返回“意外”“Jack”的原因相同。就这样。

【讨论】:

  • 谁是文盲且极其愚蠢的智障,否决了线程上唯一正确的答案 - 只是想知道:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
  • 2017-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多