Les variables JavaScript déclarent une boucle extérieure ou intérieure?

213

Dans AS3, je pense que vous devriez initialiser toutes les variables en dehors des boucles pour des performances accrues. Est-ce également le cas avec JavaScript? Quelle est la meilleure / la plus rapide / la meilleure pratique?

var value = 0;

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

ou

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}
davivid
la source
7
À l'extérieur! toujours dehors.
BGerrissen
37
Hmm, les déclarations de variables ne sont-elles pas poussées de toute façon dans la portée de la fonction à la fois en Javascript et en AS3? Si j'ai raison, alors ça n'a vraiment pas d'importance.
passer le
3
@Andy - avez-vous essayé d'assigner avant de déclarer dans un corps de fonction? Peut-être que vos idées préconçues vous égarent. Performances WRT, avec portée push-up, si le JS est interprété, il mâchera des cycles supplémentaires dans un bloc de boucle. S'il est compilé (ce que la plupart des moteurs font aujourd'hui), cela n'aura pas d'importance.
dépenser le
2
Grande question! Merci. Après avoir lu toutes les réponses, je crois que si c'est juste une petite boucle ou seulement une variable temporaire, je les garderai là où elles sont nécessaires et cela n'affecte pas les performances. Si un var est utilisé plus d'une fois dans une fonction, pourquoi ne pas y faire référence à l'intérieur de la fonction et enfin les globaux peuvent alors être assis à l'extérieur de la fn ()
Dean Meehan
3
Je suis surpris que personne n'ait essayé de mesurer les performances. J'ai créé un jsperf . Semble être un peu plus rapide lorsqu'il est déclaré à l'intérieur de la boucle pour Safari et Firefox, l'inverse pour Chrome…
Buzut

Réponses:

281

Il n'y a absolument aucune différence de sens ou de performance, en JavaScript ou en ActionScript.

varest une directive pour l'analyseur, et non une commande exécutée au moment de l'exécution. Si un identifiant particulier a été déclaré varune ou plusieurs fois n'importe où dans un corps de fonction (*), alors toute utilisation de cet identifiant dans le bloc fera référence à la variable locale. Cela ne fait aucune différence si valueest déclaré à l' varintérieur de la boucle, à l'extérieur de la boucle, ou les deux.

Par conséquent, vous devez écrire ce que vous trouvez le plus lisible. Je ne suis pas d'accord avec Crockford selon lequel mettre tous les vars au sommet d'une fonction est toujours la meilleure chose. Dans le cas où une variable est utilisée temporairement dans une section de code, il est préférable de déclarer vardans cette section, afin que la section soit autonome et puisse être copiée-collée. Sinon, copiez-collez quelques lignes de code dans une nouvelle fonction pendant la refactorisation, sans sélectionner et déplacer séparément l'associé var, et vous obtenez un global accidentel.

En particulier:

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

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

Crockford vous recommandera de supprimer le second var(ou supprimez les deux vars et faites var i;ci - dessus), et jslint se plaindra de vous pour cela. Mais OMI, il est plus varfacile de conserver les deux s, en gardant tout le code associé ensemble, au lieu d'avoir un bit de code supplémentaire facilement oublié en haut de la fonction.

Personnellement, j'ai tendance à déclarer comme varla première affectation d'une variable dans une section indépendante de code, qu'il y ait ou non une autre utilisation distincte du même nom de variable dans une autre partie de la même fonction. Pour moi, avoir à déclarer varest une verrue JS indésirable (il aurait été préférable d'avoir des variables par défaut sur local); Je ne considère pas qu'il soit de mon devoir de dupliquer également les limitations d'une [ancienne révision de] ANSI C en JavaScript.

(*: autre que dans les corps de fonction imbriqués)

bobince
la source
4
Je ne peux toujours pas décider si je suis avec Crockford ou non sur celui-ci. Déclarer des variables à l'intérieur de blocs ressemble un peu à l'utilisation d'instructions de fonction conditionnelles (ce qui est vilain) ... Cependant, je suis également d'accord avec vos points :) #confused
Daniel Vassallo
20
+1 Je ne suis pas d'accord avec le raisonnement de Crockford (cité dans la réponse de Daniel), en tant que développeurs JavaScript, nous ne devrions pas écrire de code pour que les autres programmeurs de la "famille C" puissent le comprendre. J'utilise souvent var inside si des blocs et des boucles parce que cela a plus de sens pour moi.
Andy E
4
-1 L'OP demande si les variables dans le corps de la boucle doivent être déclarées avant le début de la boucle. La valeur d'index de la boucle est clairement un cas spécial (et est hissée) et n'aide pas du tout l'OP.
mkoistinen
21
Les index de boucle ne sont pas un cas particulier, ils sont traités et hissés exactement de la même manière qu'une affectation normale.
bobince
31
+1 Crockford a tort à propos de celui-ci (et d'autres, mais je m'égare). L'exigence qui varn'est utilisée qu'en haut d'une fonction revient simplement à créer accidentellement une variable globale. Et avoir une masse de variables non liées toutes déclarées en un seul endroit n'a pas de sens sémantique, surtout lorsque certaines de ces variables peuvent ne jamais être utilisées.
MooGoo
64

En théorie, cela ne devrait pas faire de différence dans JavaScript, car le langage n'a pas de portée de bloc, mais seulement de portée de fonction.

Je ne suis pas sûr de l'argument de la performance, mais Douglas Crockford recommande toujours que les varinstructions soient les premières instructions dans le corps de la fonction. Citant des conventions de code pour le langage de programmation JavaScript :

JavaScript n'a pas de portée de bloc, donc la définition de variables dans des blocs peut confondre les programmeurs expérimentés avec d'autres langages de la famille C. Définissez toutes les variables en haut de la fonction.

Je pense qu'il a raison, comme vous pouvez le voir dans l'exemple suivant. La déclaration des variables en haut de la fonction ne doit pas amener les lecteurs à penser que la variable i est conservée dans la portée du forbloc de boucle:

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

  for (i = 0; i < 10; i++) {
    // ...
  }
}
Daniel Vassallo
la source
8
+1 pour dire des vérités OP sur la portée JS. Je me demande s'il faut voter contre les réponses qui disent le contraire!
dépensier
1
@Kieranmaine: Je n'ai pas dit que cela n'affecte pas les performances. Je viens de faire un argument pour les mettre en dehors des boucles, sans rapport avec les performances .... Je n'ai pas de références pour les arguments de performances, mais vous n'en avez pas cité non plus dans votre réponse :)
Daniel Vassallo
1
@Kieranmaine: avez-vous une source pour cela?
Andy E
5
@Kieranmaine: AFAIK même si vous déclarez des variables à l'intérieur d'une boucle, ecma- / javascriptles augmentera au moment de l'exécution. C'est ce qu'on appelle le "levage". Il ne devrait donc pas y avoir de différence.
jAndy
1
Comment les ES6 letaffectent-ils cette réponse?
jbyrd
58

La ECMA-/Javascriptlangue hoiststoute variable qui est déclarée nulle part au sommet d'une fonction. C'est parce que cette langue n'avoir et ne pas avoir comme beaucoup d' autres C comme langues. C'est aussi connu sous le nom de .function scopeblock scope
lexical scope

Si vous déclarez quelque chose comme

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

Cela permet hoistedde:

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

Cela ne fait donc aucune différence dans les performances (mais corrigez-moi si je me trompe totalement ici).
Un bien meilleur argument pour ne pas déclarer une variable ailleurs qu'en haut d'une fonction est la lisibilité . Déclarer une variable dans un for-looppourrait conduire à l'hypothèse erronée que cette variable n'est accessible que dans le corps de la boucle, ce qui est totalement faux . En fait, vous pouvez accéder à cette variable n'importe où dans la portée actuelle.

jAndy
la source
Même réponse de base que celle acceptée mais, à mon avis, plus lisible et à peu près aussi informative. Bon travail.
Anne Gunn
5
Comment les ES6 letaffectent-ils cette réponse?
jbyrd
13

L'année prochaine, tous les navigateurs disposeront de moteurs JS qui précompileront le code, de sorte que la différence de performances (qui provient de l'analyse répétée du même bloc de code et de l'exécution de l'affectation) devrait devenir négligeable.

De plus, n'optimisez jamais les performances, sauf si vous le devez. Garder les variables à proximité de l'endroit où vous en avez besoin la première fois maintient votre code propre. Du côté négatif, les gens qui sont habitués aux langues avec des étendues de bloc peuvent être confus.

Aaron Digulla
la source
6

Une autre considération, maintenant que nous avons letet constdans ES2015, est que vous pouvez maintenant étendre des variables spécifiquement au bloc de boucle. Donc, à moins que vous n'ayez besoin de la même variable en dehors de la boucle (ou si chaque itération dépend d'une opération effectuée sur cette variable dans l'itération précédente), il est probablement préférable de le faire:

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

Je viens de faire un simple test dans Chrome. Essayez le violon dans votre navigateur et voyez les résultats

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

Le résultat est que le dernier test prend ~ 8 secondes et les 2 précédents ne sont que ~ 2 secondes. Très répétable et indépendamment de la commande.

Donc, cela me prouve, qu'il faut toujours déclarer les vars en dehors de la boucle. Le cas le plus curieux pour moi est le premier où je déclare idans la déclaration for (). Celui-ci semble être aussi rapide que le 2ème test où je pré-déclare l'index.

mkoistinen
la source
14
@KP: les résultats ne sont prouvés que si vous les testez vous-même ou si un grand nombre de personnes les vérifient. @mkoistinen: J'ai construit un test plus juste, jsfiddle.net/GM8nk . Après avoir exécuté le script plusieurs fois dans Chrome 5, j'ai pu voir qu'il n'y avait pas de gagnant cohérent. Les trois variations ont mieux fonctionné que les autres après quelques rafraîchissements. -1 de moi, j'ai peur. Remarque, vous souhaiterez peut-être exécuter celui-ci dans d'autres navigateurs. IE et Fx n'aimaient pas 100 millions d'itérations.
Andy E
1
@AndyE. Wow, donc sur la base de ce test, IE aspire 100 fois plus? =)
mkoistinen
2
Les résultats sont partout pour moi, pas de gagnant clair pour tous les navigateurs, bien qu'il y ait parfois des différences de vitesse importantes. Bizarre. Je pense que le violon d'Andy est un meilleur test, mettant chaque candidat dans sa propre fonction ... certainement si le script original est exécuté en dehors d'une fonction, il ne devrait pas vraiment tester quoi que ce soit car il vardéclare comme globale une variable qui être global de toute façon.
bobince
4
Plus d'un an après le fait, mais SHAPOW
sdleihssirhc
2
Ce n'est pas le mien, mais j'ai pensé que certains d'entre vous seraient intéressés: jsperf.com/var-in-for-loop
m1.
1

JavaScript est un langage écrit en bas par C ou C ++, je ne sais pas trop lequel. Et l'un de ses objectifs est de ne pas avoir à manipuler la mémoire interne. Même en C ou C ++, vous n'aurez pas à vous soucier de savoir s'il consommera beaucoup de ressources lorsque des variables sont déclarées dans une boucle. Pourquoi devriez-vous vous en soucier en JavaScript?

Yan Yang
la source
1
C ou C ++ peut être au bas de JavaScript. Mais il ne faut pas oublier que le navigateur convertit JavaScript en langage sous-jacent (C, C ++). Les performances dépendent donc du navigateur
Kira
3
@Kira Il n'est pas réellement converti en C / C ++. Javascript est compilé en un ensemble d'instructions qui sont exécutées par le runtime JS (c'est-à-dire une machine virtuelle fonctionnant dans le navigateur). Le même principe s'applique à d'autres langages dynamiques tels que Python et Ruby.
Anthony E
@AnthonyE, Merci pour l'info. Je n'étais pas sûr de la conversion de JS en C ou C ++. J'ai donc utilisé peut-être dans mon commentaire
Kira
0

Eh bien, cela dépend de ce que vous essayez de réaliser ... si vous valuesupposez qu'il ne s'agit que d'une variable temporaire à l'intérieur du bloc de boucle, il est beaucoup plus clair d'utiliser le second formulaire. C'est aussi plus logique et verbeux.

Crozin
la source
J'ai trouvé que le fait de pousser toutes les déclarations de variables vers le haut - y compris les variables temporaires - peut en fait conduire à la confusion car il devient simplement «bruyant».
Daniel Sokolowski
0

Cela ne fait aucune différence si vous déclarez des variables à l'intérieur ou à l'extérieur de la boucle for. Voici l'exemple de code à tester.

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

Les résultats ont montré dans mon cas

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

Merci - MyFavs.in

myfavs.in
la source
dans les deux cas, vous déclarez j en dehors de la boucle! X_x
john ktejik
Je l'ai essayé dans Chromium 81 avec letau lieu de varet a a()tendance à être un peu plus lent (comme 120 vs 115 ms = ~ 6% = IMO insignifiant)
mikiqex
-1

La question ici est essentiellement de déclarer un var à l'intérieur d'une boucle. Pensez simplement à ce qui se passe si vous faites cela:

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

Pensez-vous que ce soit vrai? Non ... parce que vous ne voulez pas déclarer autant de fois une variable. Lorsque vous déclarez une variable à l'intérieur d'une boucle, ne déclare-t-elle pas autant de fois que la boucle s'exécute? De toute évidence, cela vous giflera lorsque vous serez en mode «utilisation stricte». Les gens sont en désaccord avec Crockford sans penser à la question d'origine.

Il est donc toujours bon de déclarer des variables en haut - 1. Pour la lisibilité, 2. Prendre de bonnes habitudes.

Vivek Pohre
la source
1
"Lorsque vous déclarez une variable à l'intérieur d'une boucle, ne déclare-t-elle pas autant de fois que la boucle s'exécute?" <- Non, ce n'est pas vrai. La déclaration de variable est hissée, donc tout ce qui vous reste est l'affectation.
Matthias
-2

En ce qui concerne les performances après l'exécution du test sur Chrome, Firefox et jsperf sur un système d'exploitation Linux, il semble y avoir une différence de performances entre la déclaration de variables dans une boucle et hors d'une boucle. C'est une petite différence, mais elle est également aggravée par le nombre d'itérations et le nombre de déclarations de variables.

Par conséquent, pour de meilleures performances, je devrais suggérer de déclarer des variables en dehors de la boucle. Ou mieux encore, déclarez vos variables en ligne. Voir l'exemple.

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

Remarquez comment les variables «al» et «av» se trouvent dans la ligne de déclaration de la boucle for. Cette déclaration en ligne m'a fourni des performances toujours meilleures. Même sur la déclaration de variables en dehors de la boucle. Encore une fois, la différence de performances est vraiment faible.

https://jsperf.com/outside-inline-for-loop-ase/1

AlexanderElias
la source
Pour moi, votre test a donné à l'intérieur de la boucle. Et quoi qu'il en soit, la différence est trop petite pour conclure, et la réponse acceptée explique clairement qu'il n'y a pas de différence
Ulysse BN
Comme les déclarations de variables sont hissées, il n'y a vraiment aucune différence.
trincot