我遇到了一个微妙的错误,它被提炼如下:
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); }
}
}