用for...of循环,我应该使用const还是let?

72
使用 for of 循环时,这两种写法都是允许的并起作用:
const numbers = [1,2,3];
// works
for(let number of numbers) {
    console.log(number);
}
// also works
for(const number of numbers) {
    console.log(number);
}

我总是使用const,因为我无法想象在任何情况下更改number变量,但是当我看到其他人的代码中出现for...of循环时,它经常使用let。也许const存在我没有发现的缺点?浏览器错误?

for...of循环中为什么要使用const,何时使用let


2
唯一的区别在于 constlet 的常规规则,它们在 for..of 循环声明中声明并不会改变任何东西。 - CertainPerformance
1
@CertainPerformance 这个问题特别涉及到 for...of,我知道 const 的一般含义。如果你所说的是真的,为什么开发者还会使用 let?我看到他们在其他地方都使用 const,但在循环中仍然使用 let。 - Tomáš Zato
4
这是一个重复的内容。变量在“for”循环声明中的声明事实并不影响任何东西。 - CertainPerformance
1
@TomášZato-ReinstateMonica,我很高兴你这样做了,那真是尴尬。我们只需要重新打开它。然而,我建议标题编辑很好,因为它指出了核心问题,不是“使用哪个”,而是为什么它如此普遍。 - AwokeKnowing
1
@TomášZato-ReinstateMonica 好的,问题已经重新开放了,所以我把我的想法移到了自己的答案中。我感觉澄清一下会更好!在循环中使用它的问题确实不同于它们之间的一般区别 :) - AwokeKnowing
显示剩余7条评论
4个回答

60
为什么要在for...of循环中使用constlet,以及何时使用它们?如果循环体内没有对标识符的赋值操作,那么使用letconst都可以,这只是个人风格问题。如果想保证在循环体内标识符为只读状态,可以使用const,这样即使有人修改代码添加了一个赋值操作,也会在编译时提示错误。如果想在循环体内赋值操作,则使用let,因为你需要进行赋值操作或者希望在不更改声明的情况下让其他人能够添加赋值操作。无论是使用for-of还是for-in循环,都可以实现这一点。通常情况下,for循环的控制变量不应该是常量(因为通常情况下你会在for的“更新”子句中更新它;如果你不这样做,就可能使用了错误的循环类型),所以在for循环中通常使用let。以下是一个在循环体内进行赋值操作的示例,以增加清晰度:

for (let str of ["a", " b", " c "]) {
    str = str.trim();
//  ^^^^^----- assignment to the identifier
    console.log(`[${str}]`);
}

如果您在上述代码中对 str 使用 const,将会出现错误:

for (const str of ["a", " b", " c "]) {
    str = str.trim();
//  ^^^^^----- Error
    console.log(`[${str}]`);
}


15
我认为“完全是样式问题”这部分有争议。const 提供额外的保护。我看过很多次循环变量被修改,比如为了显示目的,但下面的代码仍然依赖于原始值。 - Yury Tarabanko
@YuryTarabanko - 对我来说,无论你是否想将其作为早期错误,如果有人以那种方式修改代码,这仍然属于“风格”问题。(我想要这样做,所以我习惯性地使用const,但是...)但我也能理解你的观点。 - T.J. Crowder
@T.J.Crowder,您之前回答了一个关于const、let和var性能的问题:https://dev59.com/blkS5IYBdhLWcg3wSk_b#40070682。我想知道在for循环中使用const而不是let是否会对引擎优化产生影响?我知道实际上这是微不足道的...只是好奇。 - Craig
3
在大多数情况下(虽然有些情况下可以),你不能在for循环中使用const,因为这个循环的“增量”部分。例如,在 for (let i = 0; i < 10; ++i) 中,经过一次迭代和测试后,在执行 ++i 部分时,引擎会创建新的 i 来进行新的迭代,并给它赋以旧 i 的值,然后执行增量部分:++i,从而修改新的 i。因此,只有在控制变量不改变的情况下才可以在 for 循环中使用 const,比如 for (const x = start(); !x.done(); x.next()) { console.log(x.current()); }。*(续)* - T.J. Crowder
2
这很不寻常,也很微小,我怀疑它不是引擎实现者的优化目标。我习惯在for-infor-of循环中使用const,因为我几乎从不想在块内修改每次迭代的变量,而且我怀疑(但不确定)如果你在循环体内创建函数,可能会有一些好处。不过,我认为如果有任何好处,那么它们并不显著,就像你说的那样。 :-) - T.J. Crowder

44

即使是经验丰富的开发人员或教程制作者,即使不会在循环体中修改值且了解const/let,也普遍使用for(let i而不是for(const i,其原因很简单。

许多人将const视为“常量”,认为它永远不应该改变(当然它永远不会改变)。但是他们进一步感到尴尬的是“重新定义具有相同名称的多个常量”。例如,在一个地方有const server = 'fun.example.com',在另一个地方有const server = 'boring.example.com',这将是“令人反感的”。

循环中的变量(至少在大多数基于类C语法的语言中,JavaScript就是其中之一)是最常更改值的变量。没有什么比所有for循环中的'i'更容易更改值了。通常情况下,'i'最终会成为CPU本身的“寄存器变量”(不在RAM中),以便它可以更快地“更改”。这在JavaScript的诞生时就是这样,使用'var'时仍然如此,而且当您执行一个简单的for(let i=0;i<50;i++)时也是如此。也就是说,for(const i=0;i<50;i++)会抛出错误。

因此,当您遍历列表并使用for(const i时,每秒可能会更改(或重新定义)数千次的 i 之间存在不和谐。因此,for(const i...看起来只像i将始终具有其分配的第一个值。在代码的下一行中,i每次“通过循环”都可能是完全不同的值,似乎说const i..是“错误的”。即使您“知道”您“为每次运行循环重新定义它”,对于某些开发人员来说,这本身就是可疑的。

出于这个原因,许多开发人员更喜欢将const保留在JavaScript中,用于“仅定义一次并在整个程序执行或至少类中代表单个值”的“值”。

因此,对于您标题中的问题(是否应使用),答案可能是“保持使用const,因为您有理由,并且对您有意义”。您对“为什么for(let i如此常见” 的疑问的回答也是我在这里回答的。对于一些人来说,他们只是习惯于使用for(let,因为它适用于“for循环”和“遍历列表”。对于其他人来说,这是一种有意识的选择,因为他们不喜欢一遍又一遍地“重新定义一个const”。

我谦虚地建议,如果您使用 const ,请不要使用 i 来表示“项(item)”,因为 i 通常与递增的整数变量相关联。至少,如果您使用一个好的名称,比如 item,那么想到它作为一个只存在一次的单个事物会更加自然舒适。我也谦虚地建议,如果您使用 'let',也不要使用 let i of,原因相同。只将 i 用于实际递增的项目。例如使用 for(let item of items) 或 for(let i=0;i<y;i++)。


9
这应该是被接受的答案。它直接回答了问题,而不是解释“const”和“let”的区别,而提问者显然已经理解了这个区别。 - snarf
4
这确实可能是原因,但推理毫无意义。在调用函数或创建类时,经常会出现使用相同名称创建多个变量的情况。人们是否避免在函数作用域中使用const声明?不是的。 - Bergi
1
@Bergi 是的,在大多数语言中都是这样。在JavaScript中到处使用const是相当反常的。老实说,我认为人们只是在炫耀。就像那些想要在任何给定的语法中尽可能地限制类型的人一样。你会期望let无处不在,除了对固定的“硬编码”值的引用。我猜那些在let同样适用的情况下使用'const'的人是那些将投票支持TypeScript取代JavaScript的人。 - AwokeKnowing
6
@AwokeKnowing 这与我的经验不符,无论是从JS/TS还是其他语言。你可能需要做更多的函数式编程来理解这种思维方式,但这绝对不是为了“炫耀”,而是为了传达意图。人们使用const是因为let并不是“同样好的选择”,它会更糟糕。 - Bergi
@Bergi const 在诸如映射函数、数组或行内迭代等方面的使用只是在炫耀方面更好,在“实际”意义上并没有什么不同。没有人会对你的“意图”有任何问题,也没有人会为了其他目的而重复使用你的变量。而且即使他们想要,const 也无法阻止他们,因为他们可以将其更改为 let。const 绝对有它的用处,但它也可能是空洞的美德信号。 - AwokeKnowing

0

0

我认为for循环内的const并不是必需的。当变量在for循环内不发生变化时,才会使用const:

let tmp = 0;
for(const num of numbers){
 tmp += num;
}

如果你必须修改变量,可以使用let:

for(let num of numbers){
 num += 5;
}

但我更喜欢使用这种语法来迭代数组:

numbers.forEach(num => {
  console.log(num)
});

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