吊装和可变范围

时间:2016-05-17 23:06:05

标签: javascript variables scope hoisting

有人会帮忙解释为什么下面的两段代码会打印出不同的结果吗?

区别在于条件语句。首先,有一个本地变量分配' Jack'要命名,条件是真实的(意思是!名称评估为真)。在第二个,同名的杰克'被分配但是全局变量名称,但条件是假的(!name是false)。我的问题是,如果所有其他条件相同,为什么第一个条件为真,第二个条件为假,如果改变的是 in 条件体?

我唯一的解释是条件的主体由JS解释器首先读取,这就是它如何确定name是全局/本地的,变量声明是否需要是否悬挂,最后记录不同的名称值。

在开始解释它的正文之前,是否应首先评估条件布尔值?然后在这两种情况下,变量名称'将被评估为' undefined' ...任何帮助将不胜感激!

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

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"

3 个答案:

答案 0 :(得分:5)

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

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"

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:-1)

这是订单!

优先顺序。在解析时的文档代码中,在代码开始执行之前对函数进行求值。但是声明的变量在任何给定的上下文中都有更高的优先级。并且只有在实例化所有变量之后才允许函数运行。

这就是具有声明变量“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"是否应该定位到全局'name'变量值?

为什么这个功能在返回“Jack”时不会像以前那样持续存在?或者 - 为什么输出突然不是“杰克”,当然不是“保罗”了,而是奇怪的,完全出乎意料的“迪特里希”?!

- 正是出于同样的原因,它坚持重新调整“杰克”,而不是遵循函数语义及其条件暗示预期,并且那就是“保罗”。

这是出于上述原因。在第一眼和功能宣言中我们name = "Dietrich"明确针对全球“保罗” 比,我们还有一个额外的预防措施,一个条件,它应该阻止执行“杰克”,因为外部范围已经有一个变量“名称”。但无济于事......

然而让事情变得更加混乱 - 全球“保罗”仍然完好无损!

Wee仅从我们的阅读点将“Dietrich”指定为来自外部范围的“name”变量。因为var(s)在函数之前和函数体声明开始执行之前很久就会得到评估。

if(condition){ doesn't create a scope of its own }开始,我们声明'name'变量并不重要。现在每个方程都已解决。

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

因此,它之前用于返回“意外”“杰克”的原因相同。就这样。