【问题标题】:How does the JS scope of these blocks work?这些块的 JS 范围是如何工作的?
【发布时间】:2017-04-05 16:37:46
【问题描述】:

谁能解释为什么下面产生 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();

})();

谢谢!

【问题讨论】:

  • 您可以避免此类问题,并且不必担心提升,只需按照最佳实践要求在函数顶部声明变量即可。
  • @torazaburo 我听说了,但你能提供一个来源吗?
  • @torazaburo Crockford,很好!谢谢!

标签: javascript scope closures lexical-closures


【解决方案1】:

由于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)

【讨论】:

  • 感谢您指出正确的方向。我阅读了其他一些文档,现在有了这个想法。只需要理解下面的陈述,混乱就消失了。 “提升是将变量和函数声明移动到函数范围顶部的机制。” rainsoft.io/javascript-hoisting-in-details
  • 吊装绝对与问题无关。
  • @BekimBacaj 你的伤害是什么?
【解决方案2】:

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

【讨论】:

  • 谢谢。是的,我知道应该避免这种情况。我只是不知道这就是原因及其背后的术语。 :)
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

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

    这就是我们遇到的最有可能“未记录”和“意外”的行为。

    op 的 1at 示例

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

    是否返回本地值 1,2。 我们可以看到 bar 函数 context的 if 条件> 将宿主函数 变量 a 视为 undefined,因此它否定它 !a 到 true。

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

    吊装!什么吊装? 如果是为了“提升” - !a 操作应该明确返回 false,因为 a 已经用 truthy 值对主机的任何和每个较低上下文功能都可访问的较高上下文。但恰恰相反

    由于 if(!a [false] ) 因此 true 来自条件返回,它允许执行 a 使用 var 键具有相同名称的变量,就好像它遥不可及或根本不存在于更高的范围内。

    它不应该。但它应该。

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

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

    [! 与使用 if(!!a) 时相同] 除了现在我们询问 a 是否为真之外,我们什么都没有改变,这样我们就可以创建一个新的局部 'a' 变量,其值为 [1,2];

    发生了什么事?! if 条件,明确需要 a 参数为 true为了继续查找并“向下爬”到函数bar的本地上下文。

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

    但是,在控制台日志中返回了一个局部变量 a,它是 undefined。

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

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

    【讨论】:

    • 允许执行以下声明 不,声明不按顺序执行;正如其他答案所表明的那样,它们被吊起。
    • 是的,if 条件测试是否定义了变量“a”,例如true 并且因为 var a 已经为 true,所以条件允许执行本地 var a = [1, 2];。
    • 我认为这里讨论的不是同一个问题
    • 我想我必须重写我的答案,因为操作员正在询问其他内容,但编写了两个逻辑上不对应的代码块。
    • @JaromandaX - 我刚刚在我的 FF 控制台中测试了第一个示例,我遇到了一个错误 - 这是完全出乎意料的 - 测试一下:(function () { var a = [5]; function bar() { if (a) { var a = [1, 2]; } console.log(a.join()); } bar(); })(); 你对此有何看法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多