在AS3中,我认为你应该初始化循环外的所有变量以提高性能。 这也是JavaScript的情况吗? 哪个更好/更快/最佳实践?
1 2 3 4 5 6
| var value = 0;
for (var i = 0; i < 100; i++)
{
value = somearray[i];
} |
要么
1 2 3 4
| for (var i = 0 ; i < 100; i++)
{
var value = somearray[i];
} |
-
外!永远在外面。
-
嗯,在Javascript和AS3中,不要将变量声明推到功能范围吗?如果我是对的,那真的没关系。
-
@Andy - 你在声明函数体之前尝试分配了吗?也许你的先入之见让你误入歧途。 WRT性能,使用上推范围,如果JS被解释,那么它将在循环块内咀嚼额外的循环。如果编译(现在大多数引擎都会这样做),那就不重要了。
-
@spender:你是对的:-)
-
好问题!谢谢。在阅读完所有答案之后,我相信如果只是一个小循环或只是一个临时变量,我会将它们保留在需要它们的位置并且它不会影响性能。如果在函数内多次使用var,为什么不在函数内部引用它,最后全局变量可以坐在fn()之外
-
我很惊讶没有人试图衡量表现。我创建了一个jsperf。在Safari和Firefox的循环中声明时似乎要快一点,与Chrome相反...
-
什么花钱的人称之为吊装bonsaiden.github.io/JavaScript-Garden
-
为了记录,waterfox =几乎相同,firefox quantum =几乎相同。
在JavaScript或ActionScript中,意义或性能完全没有区别。
var是解析器的指令,而不是在运行时执行的命令。如果特定标识符已在函数体(*)中的任何位置声明var一次或多次,则块中该标识符的所有使用都将引用局部变量。是否将value声明为循环内部,循环外部或两者都是var没有区别。
因此,你应该写出你认为最具可读性的。我不同意Crockford的说法,将所有变量放在函数顶部总是最好的。对于在一段代码中临时使用变量的情况,最好在该部分中声明var,因此该部分是独立的并且可以进行复制粘贴。否则,在重构期间将几行代码复制粘贴到一个新函数,而无需单独挑选和移动关联的var,并且您自己也是偶然的全局。
特别是:
1 2 3 4 5
| for (var i; i<100; i++)
do something;
for (var i; i<100; i++)
do something else; |
Crockford建议您删除第二个var(或同时删除var s并执行上面的var i;),jslint将为此向您发出警告。但IMO更容易保持两个var,将所有相关代码保持在一起,而不是在函数顶部有一个额外的,容易被遗忘的代码。
就个人而言,我倾向于将var声明为独立代码段中变量的第一个赋值,无论是否在同一函数的某个其他部分中使用相同的变量名称。对我来说,必须声明var是一个不受欢迎的JS wart(将变量默认为local更好);我不认为我有责任在JavaScript中复制[旧版本] ANSI C的限制。
(*:除嵌套函数体外)
-
我仍然无法决定我是否与克罗克福德在一起。在块内声明变量感觉有点像使用条件函数语句(这是顽皮的)......但是,我同意你的观点:) #confused
-
+1我不同意Crockford的推理(在Daniel的回答中引用),因为JavaScript开发人员不应该编写代码以便其他"C系列"程序员能够理解它。我常常在块和循环中使用var,因为它对我来说更有意义。
-
-1 OP在循环开始之前询问是否应该声明循环体中的变量。循环的索引值显然是一个特殊情况(并且被提升)并且根本不帮助OP。
-
循环索引不是特殊情况,它们的处理和提升方式与正常分配完全相同。
-
+1 Crockford对于这个(和其他人,但我离题)是错误的。要求var仅用于函数的顶部只是要求意外的全局变量创建。并且在一个地方声明了大量不相关的变量在语义上毫无意义,特别是当这些变量中的一些最终可能永远不被使用时。
-
@MooGoo:他会告诉你使用jsLint来避免你提到的问题:)
-
糟糕,错误的帖子。向前走。
-
我刚刚运行了一个测试,如果var在循环之外,IE8的性能会更好。 Chrome就是精确的对面。 jsperf.com/var-inside-for-loop
-
埃里克:我认为差异不大。在Chrome中的两次链接测试中,我有两种可能的结果。
-
此外,在某些情况下,如果数组为空,则表示循环外的声明变量是多余的,那么内部强化不是这种情况?
-
re:"......在一个地方声明大量不相关的变量在语义上毫无意义":我同意,但是我发现这通常是一个强烈的迹象,表明你的功能过于复杂,你应该做出额外的抽象,而不是为了发展起见,但是为了表现。
-
将var放在for循环之外不断开始意味着for循环正在创建一个范围,因此外部提供了性能优势。如果你不是100%警觉,就像在下午12点你会做奇怪的事情。
-
@bobince我修改了jsperf以便更多地嵌套变量声明。几个firefox运行表明,在范围的开头声明变量的变量始终比替代方案更好。铬没有区别。见jsperf.com/javascript-variables-declare-outside-vs-inside-loop/… 。重大?可能不是。
-
在一个地方和地方宣布所有变量之间的争论让我想起了pascal和borland delphi,一种非常漂亮的语言
-
这也适用于在for循环语句中声明i变量("signature","heading",无论正确的术语是什么),如:for (var i = 0; i < len; i++) {...}?
-
@jbyrd:是的,var在任何地方都有功能范围。在ES6中,let不同,并且将是这个问题中提出的许多问题的答案。
-
我认为这更像是一种风格问题而且没有"正确"或"错误"的方式。有些代码组合了var语句,有些是单独的,有些是在循环内等等。就像有些人使用制表符而其他人使用空格。 JS在其工作方式上足够宽容,无论哪种方式都很好,无视边缘情况。
-
如果这是真的,那么在循环之外声明你的变量在技术上会略微降低性能,因为你现在有更多的代码行?
理论上它不应该在JavaScript中有任何区别,因为语言没有块范围,只有函数范围。
我不确定性能参数,但Douglas Crockford仍然建议var语句应该是函数体中的第一个语句。引用JavaScript编程语言的代码约定:
JavaScript does not have block scope, so defining variables in blocks can confuse programmers who are experienced with other C family languages. Define all variables at the top of the function.
我认为他有一个观点,你可以在下面的例子中看到。声明函数顶部的变量不应该让读者误以为变量i保存在for循环块的范围内:
1 2 3 4 5 6 7
| function myFunction() {
var i; // the scope of the variables is very clear
for (i = 0; i < 10; i++) {
// ...
}
} |
-
+1告诉OP有关JS范围的真相。我想知道是否要回答说不然的答案!
-
@Kieranmaine:我没有说它不会影响性能。我只是提出了一个论点,将它们放在循环之外,与性能无关......我没有任何对性能参数的引用,但是你没有在你的答案中引用任何一个:)
-
@Kieranmaine:你有这方面的消息来源吗?
-
@Kieranmaine:AFAIK即使你在循环中声明变量,ecma- / javascript也会在运行时将这些变量搞砸。这被称为"吊装"。所以应该没有任何区别。
-
@spender:谢谢......我当然不打算引起争议:)
-
@Andy E - 现在试着找到它。
-
+1并撤回我的评论。忘了提升,但我建议你将var awnser提升到顶部,将'额外'范围信息提升到底部; o)
-
循环中的var声明不会降低性能,但静态值赋值(即len = list.length)
-
+1也退缩了。我的例子与BGerrissen指出的相同。
-
-1错过了这一点。这不是关于for循环的索引,而是关于循环内部的内容。
-
@mkoistinen:我认为我没有错过这一点。我只引用了一个权威的消息来源,它仍然建议将变量声明放在块之外,天气是否有性能提升。我的目的并不是一个详尽的答案(它没有回答性能问题,我强调了这一点),但我仍然认为它增加了问题主题的价值......我不认为这个答案是值得的它获得了6个下注(其中2个后退)。
-
@Daniel,你说的没错。我在这里口头回溯了我的投票,但在你因某些原因再次编辑你的帖子之前,我似乎无法真正做到这一点。
-
@mkoistinen:是的,有一些机制可以阻止对SO的战术downvoting ...做了一个小修改:)
-
for(var i; ...)只会让初学者和最迷失的JS程序员混淆一个循环有自己的范围......
-
ES6的let如何影响这个答案?
ECMA-/Javascript语言hoists任何在函数顶部声明的变量。那是因为这种语言确实有function scope,并且没有像许多其他类C语言那样具有block scope。
这也称为lexical scope。
如果你宣布类似的东西
1 2 3 4
| var foo = function(){
for(var i = 0; i < 10; i++){
}
}; |
这得到hoisted:
1 2 3 4 5
| var foo = function(){
var i;
for(i = 0; i < 10; i++){
}
} |
所以它在性能上没有任何差别(但如果我在这里完全错了,请纠正我)。
不在函数顶部而不是在函数顶部声明变量的更好的论据是可读性。在for-loop中声明变量可能会导致错误的假设,即此变量只能在循环体中访问,这是完全错误的。事实上,您可以在当前范围内的任何位置访问该变量。
-
与接受的答案基本相同,但IMO更具可读性,而且信息量更大。不错的工作。
-
ES6的let如何影响这个答案?
明年,所有浏览器都会有预编译代码的JS引擎,因此性能差异(通过一次又一次地解析相同的代码块以及执行赋值)应该可以忽略不计。
此外,除非必须,否则永远不要优化性能。第一次将变量保持在您需要的位置附近可以保持代码清洁。从消极方面来说,习惯于使用块范围的语言的人可能会感到困惑。
我只是在Chrome中做了一个简单的测试。在浏览器中尝试小提琴并查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var count = 100000000;
var a = 0;
console.log(new Date());
for (var i=0; i<count; i++) {
a = a + 1
}
console.log(new Date());
var j;
for (j=0; j<count; j++) {
a = a + 1;
}
console.log(new Date());
var j;
for (j=0; j<count; j++) {
var x;
x = x + 1;
}
console.log(new Date()); |
结果是最后一次测试需要大约8秒,前两次测试只需要~2秒。非常重复,无论顺序如何。
所以,这证明了我应该总是在循环之外声明变量。好奇的情况是我在for()语句中声明i的第一个。这个看起来和我预先声明索引的第二个测试一样快。
-
我尝试了1000万次迭代而不是1亿次测试用例。 Chrome 6.0.472 for Mac(2.93 Ghz Intel Core 2 Duo - 6 GB RAM):img691.imageshack.us/img691/3581/chrometest.jpg ...看不出任何差异。
-
@KP:只有在您自己测试或大量人员验证结果时才会证明结果。 @mkoistinen:我构建了一个更公平的测试,jsfiddle.net / GM8nk。在Chrome 5中多次运行脚本后,我发现没有一致的赢家。经过几次刷新后,所有三种变化都比其他变体表现得更好。从我这里来的,我很害怕。请注意,您可能希望在其他浏览器中运行此程序。 IE和Fx不喜欢1亿次迭代。
-
我再重复几次。结果与我在Mac OS 10.6.4上的Chrome 7.0.503.0 dev上的帖子非常一致。
-
所以看起来chrome 7 dev版本不能优化循环中的局部var声明,而如果var声明取出循环呢?任何人都知道为什么,因为所有其他答案都表明它应该是一样的?
-
@AndyE,你的小提琴确实看起来'更公平'。并且,我的机器上的每个结果大致相同。我纠正了。
-
@NimChimpsky,我不知道你是否可以得出那个结论。我能够在上面的jsFiddle中执行Andy E所写的测试,每个测试的性能大致相同。我仍然无法解释为什么我本地运行的测试''仍然'显示测试中的性能损失与循环中声明的var。
-
@mkoistinen:在Chrome 7 Dev中尝试过你的测试(在Dev Tools中使用JS Console),我得到了相同的结果。每个测试用例12-13秒:img812.imageshack.us/img812/931/chrome7test.jpg
-
@mkoistinen:平心而论,我只是在Opera上运行我的第一个小提琴,结果是第三个测试稍慢。但它只是边缘,超过1亿次迭代差异<20ms所以我不确定是否值得改变你编写代码的方式来解释它将在真实世界中保存的微秒。不过,值得我撤回我的投票。 FWIW,Firefox和IE在这么多次迭代中窒息,将其降至100万,显示出与Chrome相似的结果。
-
@mkoistinen,我不确定是否要投票,因为进行测试是个好主意;或者是因为你得到了狡猾/无法解释的结果......
-
@NimChimpsky ......我认为陪审团还在外面! =)
-
@AndyE。哇,基于这个简单的测试,IE吸收了100多倍? =)
-
@mkoistinen:IE和Firefox ;-)我对Opera的脚本引擎速度印象深刻,tbh。
-
对我来说,结果已经到处都是,尽管有时存在明显的速度差异,但没有明确的跨浏览器赢家。奇怪的。我认为Andy的小提琴是一个更好的测试,但是每个候选人都有自己的功能...当然,如果原始脚本在一个函数之外运行,那么它应该不会测试任何东西,因为var声明为全局无论如何都是全局的变量。
-
一年后事实,但SHAPOW
-
这不是我的,但我想你们中的一些人会感兴趣:jsperf.com/var-in-for-loop
-
-1:代码中存在错误:"var a"未定义,因此a = a + 1是"undefined + 1"是NaN。使用"var a = 0"并且最后一次测试是最快的。 jsfiddle.net/zzg75j5o/2
-
@Thomas究竟是什么"未定义"?见"var a = 0;"在第二行代码上。此外,自8年前以来,时代已发生变化。大声笑。如果有的话,应该测试自己的环境,
-
在这里确认。
现在我们在ES2015中有let和const的另一个考虑因素是,您现在可以将变量专门用于循环块。因此,除非你需要在循环外部使用相同的变量(或者如果每次迭代都依赖于在前一次迭代中对该变量所做的操作),那么最好这样做:
1 2 3 4
| for (let i = 0; i < 100; i++) {
let value = somearray[i];
//do something with `value`
} |
JavaScript是一种用C或C ++底部编写的语言,我不太确定它是哪一种。其目的之一是节省处理内存的重要性。
即使在C或C ++中,您也不必担心在循环内声明变量时是否会消耗大量资源。你为什么要在JavaScript中担心呢?
-
C或C ++可能位于JavaScript的底部。但我们不应忘记,浏览器将JavaScript转换为底层语言(C,C ++)。因此性能取决于浏览器
-
@Kira它实际上并没有转换为C / C ++。 Javascript被编译成一组指令,这些指令由JS运行时(即在浏览器中运行的虚拟机)执行。同样的原则适用于其他动态语言,如Python和Ruby。
-
@AnthonyE,感谢您的信息。我不确定JS转换为C或C ++。所以我用过的可能就在我的评论中
如果在for循环内部或外部声明变量,则没有区别。
下面是要测试的示例代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function a() {
console.log('Function a() starts');
console.log(new Date());
var j;
for (j=0; j<100000000; j++) {
var x;
x = x + 1;
}
console.log(new Date());
console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
console.log(new Date());
var a;
var j;
for (j=0; j<100000000; j++) {
a = a + 1;
}
console.log(new Date());
console.log('Function B() Ends');
}
b() |
结果显示在我的案例中
1 2 3 4 5 6 7 8
| Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends |
谢谢 -
MyFavs.in
那么,这取决于你想要实现的目标......如果value假设只是循环块内的临时变量,那么使用第二种形式就更清楚了。它也更合乎逻辑,更冗长。
-
我发现将所有变量声明推到顶端 - 包括临时变量 - 实际上可能会导致混乱,因为它只是"吵闹"。
这里的问题基本上是在循环内声明一个var。试想如果你这样做会发生什么:
1 2 3
| var a = 30;
var a = 50;
var a = 60; |
你认为这是对的吗?不...因为您不想多次声明变量。当你在循环中声明一个变量时,它不会声明循环运行多少次?当你处于'严格'模式时,它显然会打你。人们在不考虑原始问题的情况下不同意克罗克福德。
因此,最好将变量声明在上面 - 1.为了便于阅读,2。养成良好的习惯。
-
"当你在循环中声明一个变量时,它不会声明循环运行的次数吗?" < - 不,那不对。变量声明被提升,所以你剩下的就是赋值。
关于在Linux操作系统上运行Chrome,Firefox和jsperf测试后的性能,在循环中和循环中声明变量之间似乎存在性能差异。这是一个很小的差异,但迭代的数量和变量声明的数量也会加剧这一点。
因此,为了获得最佳性能,我必须建议在循环外声明变量。或者更好地声明你的变量。见例子。
1 2 3 4 5 6 7 8 9 10 11
| // inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
av = av + 1;
}
// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
bv = bv + 1;
} |
注意变量'al'和'av'是如何在for循环声明行中的。这个内联声明为我提供了始终如一的更好的表现。甚至在循环之外的变量声明。性能差异再次非常小。
https://jsperf.com/outside-inline-for-loop-ase/1
-
对我来说,你的测试在循环中给出了。然而,事实并非如此,差异太小而无法得出结论,并且接受的答案清楚地解释了没有区别
-
随着变量声明被提升,确实没有区别。