在多个 for 循环中使用相同的变量名是不好的做法吗?
Posted
技术标签:
【中文标题】在多个 for 循环中使用相同的变量名是不好的做法吗?【英文标题】:Is it bad practice to use the same variable name in multiple for-loops? 【发布时间】:2013-03-24 20:54:02 【问题描述】:我只是在使用 JSHint 对一些 javascript 代码进行 linting。在代码中,我有两个 for 循环都像这样使用:
for (var i = 0; i < somevalue; i++) ...
所以两个 for 循环都使用 var i 进行迭代。
现在 JSHint 向我显示第二个 for 循环的错误:“'i' is already defined”。我不能说这不是真的(因为它显然是)但我一直认为这无关紧要,因为 var i 只在那个特定的地方使用。
以这种方式使用 for 循环是不好的做法吗?我是否应该为代码中的每个 for 循环使用不同的变量,例如
//for-loop 1
for (var i = 0; ...; i++) ...
//for-loop 2
for (var j = 0; ...; j++) ...
或者这是我可以忽略的错误之一(因为它不会破坏我的代码,它仍然会做它应该做的事情)?
JSLint 顺便说一句。在第一个 for 循环处停止验证,因为我没有在函数顶部定义 var i (这就是我首先切换到 JSHint 的原因)。所以根据这个问题中的例子:Should I use JSLint or JSHint JavaScript validation?——无论如何我都应该使用这样的for循环来确认JSLint:
...
var i;
...
//for-loop 1
for (i = 0; ...; i++) ...
...
//for-loop 2
for (i = 0; ...; i++) ...
这对我来说也很好,因为这样我应该避免 JSLint 和 JSHint 中的错误。但我不确定的是是否应该为每个 for 循环使用不同的变量,如下所示:
...
var i, j;
...
//for-loop 1
for (i = 0; ...; i++) ...
//for-loop 2
for (j = 0; ...; j++) ...
那么有没有一个最佳实践,或者我可以使用上面的任何代码,这意味着我选择“我的”最佳实践?
【问题讨论】:
只有在嵌套循环时才需要不同的计数器变量(除非嵌套循环在其自己的函数范围内并且不使用前一个范围中的计数器)。 【参考方案1】:由于变量声明被提升到它们出现的范围的顶部,解释器将以相同的方式有效地解释两个版本。因此,JSHint 和 JSLint 建议将声明移出循环初始化程序。
以下代码...
for (var i = 0; i < 10; i++)
for (var i = 5; i < 15; i++)
... 被有效地解释为:
var i;
for (i = 0; i < 10; i++)
for (i = 5; i < 15; i++)
请注意,i
实际上只有一个声明,并且对其进行了多次赋值 - 您不能真正“重新声明”同一范围内的变量。
要真正回答你的问题...
是否有最佳实践,或者我可以使用上述任何代码吗?
对于如何最好地处理这个问题有不同的意见。就个人而言,我同意 JSLint 并认为当您在每个范围的顶部一起声明所有变量时,代码会更清晰。既然这就是代码的解释方式,为什么不编写看起来像它的行为的代码呢?
但是,正如您所观察到的,无论采用何种方法,代码都可以正常工作,因此这是一种风格/约定的选择,您可以使用您觉得最舒服的任何形式。
【讨论】:
我认为我最喜欢这种方法,因为当您说“为什么不编写看起来符合其行为的代码?”时,我同意您的看法。它非常清晰易读。我想我会擅长这种风格。 我宁愿在循环头中声明我的循环迭代器,因为它使变量的用途非常明显,因为循环迭代器在历史上是简单的非描述性名称,例如i
和 @ 987654325@。您应该如何避免在多个循环中使用与迭代器相同的变量的问题。? 不要将同一个变量用作多个循环的迭代器。 要么声明多个迭代器变量,要么不要在同一个块中执行多个循环。我个人觉得如果你把每个循环分解成它自己的辅助函数,它最易读,那么你可以在两者中使用i
。【参考方案2】:
只在the comment by @TSCrowder中提到过:如果你的环境支持(Firefox,Node.js),在ES6中你可以使用let
declaration
//for-loop 1
for (let i = 0; ...; i++) ...
//for-loop 2
for (let i = 0; ...; i++) ...
将范围限制在 for 循环内。奖励:JSHint 不再抱怨了。
【讨论】:
【参考方案3】:javascript 中的变量是函数范围的(不是块范围的)。
当您在循环中定义var i
时,它会保留在循环中以及具有该循环的函数中。
见下文,
function myfun()
//for-loop 1
for (var i = 0; ...; i++) ...
// i is already defined, its scope is visible outside of the loop1.
// so you should do something like this in second loop.
for (i = 0; ...; j++) ...
// But doing such will be inappropriate, as you will need to remember
// if `i` has been defined already or not. If not, the `i` would be global variable.
【讨论】:
是的,这太容易混淆了(比如为什么它在第一个循环中声明而不是在第二个循环中声明)。 但是在这种情况下,如果第一个循环被删除,第二个循环最终会将i
泄漏到全局范围内。但是,如果您使用的是 linter,则不应提交此错误。
谢谢你,这很好地说明了问题。【参考方案4】:
JSHint 显示错误的原因是因为在 JS 中变量范围是函数,变量声明被提升到函数的顶部。
在 Firefox 中,您可以使用 let
关键字来定义块范围,但目前其他浏览器不支持。
let
关键字包含在 ECMAScript 6 规范中。
【讨论】:
【参考方案5】:我知道这个问题已经回答了,但是如果你想要超级 for 循环,可以这样写:
var names = ['alex','john','paul','nemo'],
name = '',
idx = 0,
len = names.length;
for(;idx<len;++idx)
name = names[idx];
// do processing...
这里发生了一些事情......
数组长度存储在len
中。这会阻止 JS 在每次迭代时评估 names.length
idx
增量是一个预增量(例如 ++idx NOT idx++)。预增量本身比后增量快。
存储对name
的引用。这是可选的,但如果您将大量使用 name
变量,建议您这样做。每次调用names[idx]
都需要在数组中找到索引。无论此搜索是线性搜索、树搜索还是哈希表,查找仍在进行。因此,将引用存储在另一个变量中以减少查找。
最后,这只是我个人的偏好,我没有证据或任何性能优势。但是我总是喜欢将变量初始化为它们将要成为的类型,例如name = '',
.
【讨论】:
通过从循环本身中删除初始化程序,如果您想使用相同的标识符(这就是问题所在),您现在必须在每个循环之后重置idx = 0
。【参考方案6】:
最好的做法是减少变量的范围,因此为循环声明迭代变量的最佳方法是
//for-loop 1
for (var i = 0; ...; i++) ...
//for-loop 2
for (var j = 0; ...; j++) ...
我知道用var
声明的变量的范围,但我在这里考虑代码的可读性。
【讨论】:
除非在 JavaScript 中不是这样。var
always 具有函数范围(或全局范围,如果在所有函数之外)。它没有块范围。在 ES6 中,我们将拥有 let
(一旦它被广泛实现),此时这是一个可行的答案。但在当今世界,var
毫无意义,而且具有误导性。
@ Nikolay:是的,如果那是声明实际发生的地方,无论您将它放在哪里。做任何不同的事情都是积极的误导。主动误导阅读您的代码的人不能称为“最佳实践”。最佳做法是让您的函数足够短,以便在顶部声明 var 将它们定义为接近使用它们的位置。
@ Nikolay:“谢谢你的缺点,但我正在考虑代码可读性” 我也是。所以,我怀疑有几个人这么说没用。重新“在使用之前”,您是否阅读了我上面的评论?保持函数简短,以便顶部的变量 靠近它们的使用位置。
Best practice is to keep your functions short enough that declaring vars at the top is defining them close to where they're used
。这应该是用任何语言编写代码的方式。
我不同意反对者。 “心理”范围比特定语言中的具体范围规则重要得多。通过在for(var i...
中添加“var”,我告诉读者i
将在此块中使用,而不会在其他任何地方使用。是的,从技术上讲,它并没有限制i
的范围,而是明确了意图。以上是关于在多个 for 循环中使用相同的变量名是不好的做法吗?的主要内容,如果未能解决你的问题,请参考以下文章