JavaScript变量是在循环内部还是循环内部声明?


212

我相信在AS3中,您应该在循环外初始化所有变量,以提高性能。JavaScript也是如此吗?哪个更好/更快/最佳实践?

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

要么

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}

7
外!总是在外面。
BGerrissen

37
嗯,无论是Javascript还是AS3,变量声明都不会被推到函数作用域中吗?如果我是正确的,那真的没关系。
支出者2010年

3
@Andy-在函数体中声明之前尝试过分配吗?也许您的先入之见使您误入歧途。具有上推作用域的WRT性能,如果解释了JS,则它将咀嚼循环块内的额外循环。如果已编译(当今大多数引擎都可以编译),则无所谓。
支出者2010年

2
好问题!谢谢。阅读完所有答案后,我相信如果只是一个小循环或一个临时变量,我会将它们保留在需要的地方,并且不会影响性能。如果在函数中多次使用var,为什么不在函数内部引用它,最后在全局变量中引用它,则可以坐在fn()之外
Dean Meehan 2014年

3
令我惊讶的是,没有人试图衡量性能。我创建了一个jsperf。在Safari和Firefox的循环中声明时似乎更快一些,在Chrome中则相反……
Buzut 2014年

Answers:


281

绝对没有区别意义还是性能,在JavaScript或ActionScript。

var是解析器的指令,而不是运行时执行的命令。如果某个特定标识符var在函数体(*)中的任何位置一次或多次声明,则该标识符在块中的所有使用将引用局部变量。value声明是var在循环内,在循环外还是在两者上都没有区别。

因此,您应该写出最容易阅读的内容。我不同意Crockford的观点,将所有var放在函数顶部始终是最好的选择。对于在代码部分中临时使用变量的情况,最好var在该部分中进行声明,因此该部分可以独立存在并可以复制粘贴。否则,在重构过程中将几行代码复制粘贴到新函数中,而无需分别挑选并移动关联的var,这会使您自己成为意外的全局对象。

特别是:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Crockford将建议您删除第二个var(或同时删除vars和do var i;),而jslint会为此向您请教。但是IMO将两个都var保留在一起,将所有相关代码保持在一起,而不是在函数顶部保留一些容易忘记的额外代码,是更可维护的。

我个人倾向于将var变量声明为代码的独立部分中的第一个赋值,无论是否在同一函数的其他部分中单独使用了同一变量名。对我来说,var根本不需要声明是一个不受欢迎的JS wart(最好将变量默认设置为local)。我不认为在JavaScript中复制ANSI C的旧版本的限制不是我的责任。

(*:嵌套函数体中除外)


4
我仍然不能决定是否要和克罗克福德在一起。在块内声明变量有点像使用条件函数语句(这很顽皮)...但是,我也同意您的观点:) #confused
Daniel Vassallo 2010年

20
+1我不同意Crockford的推理(在Daniel的回答中引用),作为JavaScript开发人员,我们不应该编写代码,以便其他“ C系列”程序员可以理解它。我经常在var内部使用if块和循环,因为它对我来说更有意义。
安迪E 2010年

4
-1 OP正在询问是否应在循环开始之前声明循环主体中的var。循环的索引值显然是一种特殊情况(已被提升),完全对OP没有帮助。
mkoistinen 2010年

21
循环索引不是一种特殊情况,它们的处理和提升方式与普通分配完全相同。
bobince 2010年

31
+1克罗克福德(Crockford)在这件事上是错的(还有其他事情,但我离题了)。要求var仅在函数顶部使用仅要求意外创建全局变量。而且,在一个地方声明所有不相关变量的数量在语义上是没有意义的,尤其是当其中一些变量最终可能永远都不会被使用时。
MooGoo 2010年

64

从理论上讲,它在JavaScript中不应有任何区别,因为该语言没有块范围,而只有函数范围。

我不确定性能参数,但是Douglas Crockford仍然建议该var语句应该是函数体中的第一条语句。引用JavaScript编程语言的代码约定

JavaScript没有块范围,因此在块中定义变量会使使用其他C系列语言的程序员感到困惑。在函数顶部定义所有变量。

正如您在以下示例中看到的,我认为他有一点。在函数顶部声明变量不应使读者误以为该变量i 位于for循环块的范围内:

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
+1告诉OP有关JS范围的真相。我想知道是否要否决其他答案!
支出者2010年

1
@Kieranmaine:我并不是说这不会影响性能。我只是为了将它们放在循环之外而不考虑性能而提出了一个论点...。我没有任何有关性能论据的引用,但您也未在回答中引用任何论点:)
Daniel Vassallo

1
@Kieranmaine:你有资料吗?
安迪E 2010年

5
@Kieranmaine:AFAIK,即使您在循环内声明变量,ecma- / javascript也会在运行时将其增大。那就是所谓的“吊装”。因此,应该没有任何区别。
jAndy 2010年

1
ES6如何let影响此答案?
jbyrd

58

ECMA-/Javascript语言hoists是在函数顶部任何位置声明的任何变量的语言。这是因为这种语言function scope和没有具有block scope像许多其他类似C语言。
也称为lexical scope

如果您声明类似

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

这将hoisted达到:

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

因此,它对性能没有任何影响(但是,如果我在这里完全错了,请纠正我)。
关于声明变量而不是在函数顶部声明变量的更好论据是可读性。在a中声明变量for-loop可能导致错误的假设,即只能在循环体内访问此变量,这是完全错误的。实际上,您可以在当前范围内的任何位置访问该变量。


与已接受的基本答案相同,但是,IMO更具可读性,而且内容丰富。不错的工作。
安妮·冈恩

5
ES6如何let影响此答案?
jbyrd

13

明年,所有浏览器都将具有可预编译代码的JS引擎,因此性能差异(来自一次又一次地解析同一代码块再加上执行分配)的性能差异应该可以忽略不计。

另外,除非必须这样做,否则请不要针对性能进行优化。将变量保持在第一次需要它们的地方,以保持代码干净。不利的一面是,习惯于使用块作用域语言的人可能会感到困惑。


6

另一个要考虑的,现在我们已经letconst在ES2015是,你现在可以作用域变量专门环形块。因此,除非您需要在循环外使用相同的变量(或者如果每次迭代都取决于上一次迭代中对该变量执行的操作),那么这样做可能更可取:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}

4

我只是在Chrome中做了一个简单的测试。在浏览器中尝试小提琴并查看结果

  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个测试仅需要2秒钟。非常可重复,无论顺序如何。

因此,这向我证明了,应该始终在循环外部声明var。对我来说,奇怪的案例是我i在for()语句中声明的第一个案例。这个速度似乎和我预先声明索引的第二次测试一样快。


14
@KP:仅当您自己测试或大量人验证了结果时,才会证明结果。@mkoistinen:我构建了一个更公平的测试,jsfiddle.net/GM8nk。在Chrome 5中多次运行该脚本后,我看到没有一个一致的赢家。经过几次刷新后,所有三个变体的性能都比其他变体好。我怕-1。请注意,您可能要在其他浏览器中运行此程序。IE和Fx不喜欢1亿次迭代。
安迪E 2010年

1
@AndyE。哇,那么根据这个简单的测试,IE会多吸100倍吗?=)
mkoistinen 2010年

2
结果对我来说无处不在,虽然有时会有明显的速度差异,但没有明显的跨浏览器冠军。奇怪的。我认为,安迪(Andy)的提琴琴是一个更好的测试,将每个候选项置于其自己的函数中……肯定的是,如果原始脚本在函数外部运行,那么它实际上并不需要进行任何测试,因为它将var声明为全局变量,无论如何都要全球化。
bobince 2010年

4
事实发生后的一年多,但SHAPOW
sdleihssirhc 2011年

2
这不是我的,但我想你们中的一些人会感兴趣: jsperf.com/var-in-for-loop
m1。

1

JavaScript是由C或C ++底层编写的一种语言,我不太确定它是哪一种。其目的之一是节省处理内部存储器的需求。即使在C或C ++中,也不必担心在循环内声明变量时它是否会消耗大量资源。为什么要在JavaScript中担心它?


1
C或C ++可能位于JavaScript的底部。但我们不要忘记,浏览器将JavaScript转换为基础语言(C,C ++)。因此,性能取决于浏览器
Kira

3
@Kira实际上并没有转换为C / C ++。Javascript被编译成一组指令,这些指令由JS运行时(即在浏览器中运行的虚拟机)执行。相同的原则适用于其他动态语言,例如Python和Ruby。
Anthony E

@AnthonyE,感谢您的信息。我不确定将JS转换为C或C ++。因此,我可能在评论中使用了
Kira

0

好吧,这取决于您要实现的目标...如果value假设只是循环块中的一个临时变量,那么使用第二种形式会更加清晰。这也更加合逻辑和冗长。


我发现将所有变量声明(包括临时变量)推到顶部实际上会导致混乱,因为它只会变得“嘈杂”。
Daniel Sokolowski14年

0

如果在for循环内部或外部声明变量,则没有区别。下面是要测试的示例代码。

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()

结果显示在我的情况下

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


在这两种情况下,您都在循环外声明j!X_X
约翰ktejik

我试图在铬81 let,而不是vara()往往是有点慢(如120 VS 115毫秒=〜6%= IMO微不足道)
mikiqex

-1

这里的问题基本上是在循环内声明var。试想一下,如果执行此操作会发生什么:

var a = 30;
var a = 50;
var a = 60;

你认为这是对的吗?不...因为您不想多次声明变量。当您在循环内声明变量时,是不是声明了循环运行了多少次?显然,当您处于“使用严格”模式时,它会打您一巴掌。人们不同意克罗克福德而未考虑原始问题。

因此,始终最好在顶部声明变量-1.为了便于阅读,2.养成良好的习惯。


1
“当您在循环内声明变量时,是不是声明了循环运行了多少次?” <-不,那是不对的。变量声明被吊起,所以剩下的就是赋值。
Matthias

-2

关于在Linux OS上的Chrome,Firefox和jsperf上运行测试后的性能,在循环中和循环外的变量声明之间确实存在性能差异。这是一个很小的差异,但是迭代次数和变量声明的数量也使情况更加复杂。

因此,为了获得最佳性能,我将不得不建议在循环外声明变量。或者更好地声明您的变量。参见示例。

// 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


对我来说,您的测试在循环内进行。然而,事实并非如此,两者之间的差异很小,无法得出结论,公认的答案清楚地说明了两者之间没有差异
Ulysse BN

当变量声明被悬挂时,实际上没有任何区别。
特里科特'17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.