JavaScript/循环混淆中的动态作用域与词法作用域



我遇到了一个微妙的错误,它被提炼如下:

function huh() {
    for (var i = 0; i < 10; i++) {
        if ( i % 2 == 1 ) {
            var x = i;
            console.log(i + ' ' + x);
        }
        else {
            console.log(i + ' ' + x);
        }
    }
}
huh();

首先,我甚至会挑战经验丰富的JavaScript程序员,让他们只在纸上正确预测这个程序的确切输出。但主要的是,JavaScript似乎混合了动态范围和词汇范围。似乎没有块作用域,只有函数作用域,这基本上打破了我在JavaScript中的整个作用域概念。有人能参照标准解释一下吗,也许还有一些理由?这似乎与直觉相悖。

使用let关键字的ecmascript版本6中的JavaScript中存在块作用域。

除此之外,它处理var语句的方式是,在执行函数之前,该语句浮动到函数的顶部。变量声明发生在其他任何事情之前,而不管它们在哪个块中,包括循环语句内部。变量的实际赋值确实发生在适当的位置,因此它们是未定义的,但在被赋值的代码行之前不会未声明。

对于您的特定示例,请记住,像数字这样的基元值不是通过引用而是通过值来分配的,并且x的更新只会发生在不均匀的数字上,并且第一次将是未定义的,但如果是对象,则一旦您将一个对象分配给另一个对象,它们就会引用同一个对象(无论if/else条件如何)。

为了提供进一步的混乱,请注意浏览器中的控制台是异步的,因此您可以将对象打印到控制台(但不是基元),并在console.log语句时无法获得对象状态的地方进行检查,而是在以后的时间。这对于调试来说非常令人困惑。

是的,JavaScript在实践中确实有一些学习曲线。

这种有点令人困惑的效果是功能范围可变吊装的组合。

您应该考虑上述代码等效于:

function huh() {
  var i, x;
  for (i = 0; i < 10; i++) {
    if ( i % 2 == 1 ) {
      x = i;
      console.log(i + ' ' + x);
    }
    else {
      console.log(i + ' ' + x); }
  }
} 

最新更新