这些块的 JS 范围如何工作?

How does the JS scope of these blocks work?

谁能解释为什么下面的产生 1,2 而另一个产生 5?他们不应该都生产5吗?

//produces 1,2
(function () {

    var a = [5];

    function bar() {
        if (!a) {
          var a = [1, 2];
        }
        console.log(a.join());
    }

    bar();

})();

根据阅读一些关于 JS 闭包的文章,我希望它们都产生 5。似乎找不到任何文章可以提供一些关于为什么第一个块产生其他结果的见解。

//produces 5
(function () {

    var a = [5];

    function bar() {
        if (a) {
          console.log(a.join());
        }
        else {
          console.log([1, 2].join())
        }
    }

    bar();

})();

谢谢!

由于 javascripts var hoisting,此代码:

(function () {
    var a = [5];
    function bar() {
        if (!a) {
          var a = [1, 2];
        }
        console.log(a.join());
    }
    bar();
})();

相当于此代码:

(function () {
    var a = [5];
    function bar() {
        var a; // a === undefined at this point
        if (!a) {
          a = [1, 2];
        }
        console.log(a.join());
    }
    bar();
})();

所以你可以看到,当测试if条件

时,a确实是falsey(即!a === true)

避免重新声明 var a = [1, 2]; 并在您的顶级函数中初始化为 a = [1, 2];。就像@Jaromanda 解释的那样,变量在 JavaScript.

中被提升

我们知道每个函数都会创建自己的上下文,并且可以有自己的局部变量和东西。

因此,我们假设在最顶层函数上下文的上下文中编写 var a = [5] 将使它在我们可能编写的任何新嵌套函数的上下文中可用和访问。

这就是我们遇到的行为很可能是 'undocumented' 以及 'unexpected'。

op 的 1at 示例

function bar(){
    if(!a) { 
           var a = [1,2];
          }
    console.log(a);
    }
>> 1,2

return 本地值为 1,2。 我们可以看到 if 条件 bar 函数 context正在处理宿主函数变量a ,因为 undefined 因此它否定它 !a

这是为什么? 一位评论员坚持吊装。但是什么吊装? 在同一函数的两个版本的最终结果或行为中绝对没有提升原则的干扰,唯一的区别在于条件参数:一个检查 'a' 是否为 false 和另一个,如果它是 true

吊装!什么吊装? 如果是 "hoisting" - !a 操作应该明确无误 return false 因为 a 已经在更高的上下文中定义了一个 truthy 值,主机的任何和每个较低上下文函数都可以访问它。但恰恰相反

作为 if(!a [false] ) 因此 true 来自条件 return 的结果,它允许执行 [=63] 的新声明=]a variable 使用 var key 具有相同的名称,就好像它遥不可及或根本不存在于更高的范围内。

不应该。但它应该。

现在尝试同样的事情而不否定条件中的 if 参数,我们将得到一个错误和赋值失败,如下所示:

 function bar(){
    if(a) { 
           var a = [1,2];
          }
    console.log(a);
    }
>> undefined

[!与使用 if(!!a) 时相同] 除了现在我们询问 a 是否为真以便我们可以创建一个值为 [1,2] 的新本地 'a' 变量外,我们没有做任何更改;

这是怎么回事?! if 条件 明确需要 a 参数true 以便继续 查找 和 "climbing it down" 到 函数的本地上下文bar.

出于某种原因,它被处理为尝试重新声明宿主函数的变量,并拒绝在本地上下文中初始化和赋值给具有相同名称的新变量。这是一个混乱,因为条件现在引用它拒绝声明的主机变量并将值分配给本地 a。

但是,在控制台日志中,一个局部变量 a 正在被 returned,它是 undefined

这是出乎意料的;自相矛盾;挑衅但看似正确! 这是正确的,因为局部 a 变量已初始化但没有正确地开始。我们试图从更高的上下文中重新声明一个,但这是不允许的。

唯一的好处是 - 浏览器之间的行为似乎是统一的。

Jeromanda X 给出了正确的答案,这是由于var hoisting,我想补充一下以更好地说明。

您的 2 个代码块的不同之处在于您在一个代码块中创建了一个新变量,而在另一个代码块中没有。公平地说,您应该将代码块 #2 更改为以下

//produces [1,2] now
(function () {

  var a = [5];

  function bar() {
      if (a) {
        console.log(a.join());
      }
      else {
        var a = [1,2];
        console.log(a.join())
      }
  }

  bar();

})();

如果您在两个代码块中都省略了 var,那么您就不会 运行 进入这个范围问题,两个代码块都会产生 5