在for循环中将变量声明为const用于循环控制

3

我理解在typescript/javascriptfor循环中使用varlet的行为,但有人可以解释一下将一个常量变量作为循环变量时的行为是如何以及为什么吗?

for (const i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 100 * i);
}

据我理解,当您将变量声明为const并初始化其值时,该值无法更改。
然而,在console.log()中您可以看到值被更改。编译时应该会抛出错误,是我漏掉了什么吗?
我创建了两个示例来演示这种行为。 Loop variable as a const Const variable re assignment 能否有人帮助我理解这个问题?

1
constlet都具有块级作用域,并在其定义的块的顶部提升,constlet之间唯一的区别是声明为const的变量不能被重新初始化。 - Kunal Mukherjee
这是一个很好的例子,说明JS实际上并没有像“常量”这样的东西。你是正确的,在IDE和构建过程中会显示,但在运行时JS将只是重新分配那个“const”。 - messerbill
1
请在问题本身中包含所有代码,而不仅仅是在外部网站上。 - Heretic Monkey
3
一个真正的 ES2015 环境确实会有常量。 - Pointy
1
@HereticMonkey 我本来想把代码也包含进去,但如果不是一个 StackBlitz 的例子,我就无法展示其行为。希望你能理解我的意思。 - CruelEngine
1
如果是这样的话,并且您知道该行为只能在Stackblitz中复制,那么您应该在问题中提到。 - Heretic Monkey
2个回答

7

它在 Stackblitz 中可以工作是因为它正在运行转换后的代码:

AppComponent.prototype.test = function () {
    var _loop_1 = function (i) {
        setTimeout(function () {
            console.log(i);
        }, 100 * i);
    };
    for (var i = 0; i < 5; i++) {
        _loop_1(i);
    }
};

如果您在此处添加代码片段,它将无法工作,因为它未被转译。

for (const i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 100 * i);
}

enter image description here


看起来我不应该太信任 StackBlitz,或者是我不理解它的工作原理。非常感谢您澄清了我的疑惑。所以,我的假设是循环会中断的是正确的。 - CruelEngine
@CruelEngine 说实话,它确实在 i++ 下方显示了红色波浪线,并显示错误信息:"无法分配给 'i',因为它是一个常量或只读属性。" - adiga
但它已经编译通过了,这让我有点困惑。 - CruelEngine

0
回答你的问题,
  test(){
    for(const i =0 ; i< 5; i++){
      setTimeout(function(){
        console.log(i)
      },100*i);
    }
  }

这段代码本质上变成了:

  test(){

    // can be only initialized once
    const i;
    for(i = 0 ; i< 5; i++){
      setTimeout(function(){
        console.log(i)
      },100*i);
    }
  }

由于每个 JavaScript 变量都会在其作用域的顶部 提升, 在这种情况下,test() 作为其 const 变量被提升到该块中,因此在该块之外无法访问。
要更正代码片段:
  test(){

    // can be only multiple times in that block
    for(let i = 0 ; i< 5; i++){
      setTimeout(function(){
        console.log(i)
      },100*i);
    }
  }

这将变成:

  test(){

    let i;
    // can be only multiple times in that block
    for(i = 0 ; i< 5; i++){
      setTimeout(function(){
        console.log(i)
      },100*i);
    }
  }

由于constlet都具有块级作用域,并且在其定义的块的顶部提升,因此constlet之间唯一的区别是声明为const的变量不能重新初始化。


提升与这种行为没有关系,对吧?如果我将循环变量声明为const,循环就不应该运行。 - CruelEngine
@CruelEngine,一旦初始化了 const 变量,就不能进行增量操作。 - Kunal Mukherjee
是的,完全正确,但是在我在问题中附上的示例中代码运行了。 - CruelEngine
1
@CruelEngine 我认为webpack将代码转换为ES3以保持兼容性,而ES3没有constlet,因此const被转换为var。无论如何,您可以检查dist文件夹中的转换后的文件。 - Kunal Mukherjee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接