Question Pour-chacun sur un tableau en JavaScript?


Comment puis-je faire défiler toutes les entrées d'un tableau en utilisant JavaScript?

Je pensais que c'était quelque chose comme ça:

forEach(instance in theArray)

theArray est mon tableau, mais cela semble être incorrect.


3782
2018-02-17 13:51


origine


Réponses:


TL; DR

  • Ne pas utiliser for-in à moins que vous l'utilisiez avec des sauvegardes ou que vous soyez au moins conscient de la raison pour laquelle il pourrait vous mordre.
  • Vos meilleurs paris sont généralement

    • une for-of boucle (ES2015 + seulement),
    • Array#forEach (spec | MDN) (ou ses proches some et tel) (ES5 + seulement),
    • un simple démodé for boucle,
    • ou for-in avec des garanties.

Mais il y'à beaucoup plus à explorer, à lire ...


JavaScript a une sémantique puissante pour la mise en boucle de tableaux et d'objets de type tableau. J'ai divisé la réponse en deux parties: les options pour les tableaux réels, et les options pour les choses qui sont justecomme, comme le arguments objet, d'autres objets itérables (ES2015 +), des collections DOM, etc.

Je noterai rapidement que vous pouvez utiliser les options ES2015 à présent, même sur les moteurs ES5, par transpirer ES2015 à ES5. Rechercher "ES2015 transpiling" / "ES6 transpiling" pour plus d'informations

Bon, regardons nos options:

Pour les tableaux réels

Vous avez trois options dans ECMAScript 5 ("ES5"), la version la plus largement supportée en ce moment, et deux autres ECMAScript 2015 ("ES2015", "ES6"):

  1. Utilisation forEach et connexe (ES5 +)
  2. Utilisez un simple for boucle
  3. Utilisation for-in  correctement
  4. Utilisation for-of (utilisez implicitement un itérateur) (ES2015 +)
  5. Utiliser un itérateur explicitement (ES2015 +)

Détails:

1. Utilisation forEach et liés

Dans tout environnement vaguement moderne (donc IE8) où vous avez accès à Array fonctionnalités ajoutées par ES5 (directement ou en utilisant des polyfills), vous pouvez utiliser forEach (spec | MDN):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEach accepte une fonction de rappel et, facultativement, une valeur à utiliser comme this lors de l'appel de ce rappel (non utilisé ci-dessus). Le rappel est appelé pour chaque entrée du tableau, dans l'ordre, en ignorant les entrées inexistantes dans les tableaux clairsemés. Bien que je n'utilise qu'un seul argument ci-dessus, le callback est appelé avec trois: La valeur de chaque entrée, l'index de cette entrée, et une référence au tableau que vous itérez (au cas où votre fonction ne l'aurait pas déjà à portée de main) ).

À moins que vous ne preniez en charge des navigateurs obsolètes comme IE8 (que NetApps montre à un peu plus de 4% de part de marché à ce jour en septembre 2016), vous pouvez utiliser avec bonheur forEach dans une page Web à usage général sans cale. Si vous avez besoin de prendre en charge des navigateurs obsolètes, vous devez effectuer un calibrage / polyfilling forEach est facile à faire (recherche de "es5 shim" pour plusieurs options).

forEach a l'avantage que vous n'avez pas à déclarer les variables d'indexation et de valeur dans la portée contenant, car elles sont fournies en tant qu'arguments à la fonction d'itération, et si bien gérée juste à cette itération.

Si vous êtes préoccupé par le coût d'exécution d'un appel de fonction pour chaque entrée de tableau, ne soyez pas; détails.

Aditionellement, forEach est la fonction de "boucle à travers tous", mais ES5 a défini plusieurs autres fonctions utiles "faites votre chemin dans le tableau et faites les choses", y compris:

  • every (arrête de boucler la première fois que le rappel revient false ou quelque chose falsey)
  • some (arrête de boucler la première fois que le rappel revient true ou quelque chose de vrai)
  • filter (crée un nouveau tableau comprenant des éléments où la fonction de filtre retourne true et en omettant ceux où il retourne false)
  • map (crée un nouveau tableau à partir des valeurs renvoyées par le rappel)
  • reduce (construit une valeur en appelant plusieurs fois le callback, en passant les valeurs précédentes, voir la spécification pour les détails, utile pour additionner le contenu d'un tableau et bien d'autres choses)
  • reduceRight (comme reduce, mais fonctionne en ordre décroissant plutôt qu'en ordre croissant)

2. Utilisez un simple for boucle

Parfois, les vieilles méthodes sont les meilleures:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

Si la longueur du tableau ne change pas pendant la boucle, et que le code est sensible à la performance (peu probable), une version légèrement plus compliquée saisissant la longueur à l'avant pourrait être un minuscule peu plus vite:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

Et / ou compter à rebours:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

Mais avec les moteurs JavaScript modernes, il est rare que vous ayez besoin de ce dernier jus.

Dans ES2015 et supérieur, vous pouvez rendre vos variables d'index et de valeur locales à for boucle:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
}
//console.log(index); // Would cause "ReferenceError: index is not defined"
//console.log(value); // Would cause "ReferenceError: value is not defined"

Et quand vous faites cela, pas seulement value mais aussi index est recréé pour chaque itération de boucle, ce qui signifie que les fermetures créées dans le corps de la boucle gardent une référence à la index (et value) créé pour cette itération spécifique:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        alert("Index is: " + index);
    });
}

Si vous aviez cinq divs, vous obtiendriez "Index is: 0" si vous avez cliqué sur le premier et "Index is: 4" si vous avez cliqué sur le dernier. Cela fait ne pas travailler si vous utilisez var au lieu de let.

3. Utilisation for-in  correctement

Vous obtiendrez des gens vous disant d'utiliser for-in, mais ce n'est pas ça for-in est pour. for-in boucles à travers le propriétés énumérables d'un objet, pas les index d'un tableau. La commande n'est pas garantie, pas même en ES2015 (ES6). ES2015 définit un ordre de propriétés de l'objet (via [[OwnPropertyKeys]], [[Enumerate]]et les choses qui les utilisent comme Object.getOwnPropertyKeys), mais ne fait pas définir cela for-in suivra cet ordre. (Détails dans cette autre réponse.)

Toujours, il pouvez être utile, en particulier pour clairsemé tableaux, si vous utilisez des sauvegardes appropriées:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

Notez les deux contrôles:

  1. Que l'objet a son posséder propriété par ce nom (pas celui qu'il hérite de son prototype), et

  2. Que la clé est une chaîne numérique de base 10 dans sa forme de chaîne normale et sa valeur est <= 2 ^ 32 - 2 (qui est 4,294,967,294). D'où vient ce nombre? Cela fait partie de la définition d'un index de tableau dans la spécification. Les autres nombres (non-entiers, nombres négatifs, nombres supérieurs à 2 ^ 32-2) ne sont pas des index de tableau. La raison pour laquelle c'est 2 ^ 32 - 2 est-ce que cela fait que la plus grande valeur d'indice est inférieure à 2 ^ 32 - 1, qui est la valeur maximale d'un tableau length peut avoir. (Par exemple, la longueur d'un tableau correspond à un entier non signé de 32 bits.) (Props à RobG pour le signaler dans un commentaire sur mon blog que mon test précédent n'était pas tout à fait correct.)

C'est un petit peu de surcharge supplémentaire par boucle d'itération sur la plupart des baies, mais si vous avez un clairsemé array, il peut être un moyen plus efficace de faire une boucle car il ne boucle que pour les entrées qui existent réellement. Par exemple, pour le tableau ci-dessus, nous bouclons un total de trois fois (pour les clés "0", "10", et "10000"- souvenez-vous, ce sont des cordes), pas 10 001 fois.

Maintenant, vous ne voudrez pas écrire cela à chaque fois, alors vous pouvez le mettre dans votre boîte à outils:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

Et puis nous l'utiliserions comme ceci:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

Ou si vous êtes intéressé par un test "assez bon pour la plupart des cas", vous pouvez l'utiliser, mais même s'il est proche, ce n'est pas tout à fait correct:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

4. Utilisation for-of (utilisez implicitement un itérateur) (ES2015 +)

ES2015 ajoute itérateurs en JavaScript. La façon la plus simple d'utiliser les itérateurs est la nouvelle for-of déclaration. Cela ressemble à ceci:

var val;
var a = ["a", "b", "c"];
for (val of a) {
    console.log(val);
}

Sortie:

une
b
c

Sous les couvertures, ça obtient un itérateur à partir du tableau et des boucles à travers elle, en tirer les valeurs. Cela n'a pas le problème que l'utilisation for-in a, car il utilise un itérateur défini par l'objet (le tableau), et les tableaux définissent que leurs itérateurs parcourent leur entrées (pas leurs propriétés). contrairement à for-in dans ES5, l'ordre dans lequel les entrées sont visitées est l'ordre numérique de leurs index.

5. Utiliser un itérateur explicitement (ES2015 +)

Parfois, vous pouvez utiliser un itérateur explicitement. Vous pouvez le faire, aussi, même si c'est beaucoup plus clunkier que for-of. Cela ressemble à ceci:

var a = ["a", "b", "c"];
var it = a.values();
var entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

L'itérateur est un objet correspondant à la définition d'Iterator dans la spécification. Ses next méthode renvoie une nouvelle objet de résultat chaque fois que vous l'appelez. L'objet résultat a une propriété, done, nous dire si c'est fait, et une propriété value avec la valeur pour cette itération. (done est optionnel si ce serait false, value est optionnel si ce serait undefined.)

Le sens de value varie en fonction de l'itérateur; les tableaux supportent (au moins) trois fonctions qui retournent les itérateurs:

  • values(): C'est celui que j'ai utilisé ci-dessus. Il renvoie un itérateur où chaque value est l'entrée du tableau pour cette itération ("a", "b", et "c" dans l'exemple plus tôt).
  • keys(): Retourne un itérateur où chaque value est la clé de cette itération (donc pour notre a ci-dessus, ce serait "0", puis "1", puis "2").
  • entries(): Retourne un itérateur où chaque value est un tableau dans le formulaire [key, value] pour cette itération.

Pour les objets de type tableau

En dehors des vrais tableaux, il y a aussi semblable à un tableau objets qui ont un length propriété et propriétés avec des noms numériques: NodeList instances, le arguments objet, etc. Comment pouvons-nous parcourir leur contenu?

Utilisez l'une des options ci-dessus pour les tableaux

Au moins certaines, et peut-être la plupart, ou même la totalité, des approches de tableau ci-dessus s'appliquent fréquemment aussi bien aux objets de type tableau:

  1. Utilisation forEach et connexe (ES5 +)

    Les différentes fonctions sur Array.prototype sont "intentionnellement génériques" et peuvent généralement être utilisés sur des objets de type tableau via Function#call ou Function#apply. (Voir le Avertissement pour les objets fournis par l'hôte à la fin de cette réponse, mais c'est un problème rare.)

    Supposons que vous vouliez utiliser forEach sur un Nodede childNodes propriété. Tu ferais ceci:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    

    Si vous allez le faire beaucoup, vous pourriez vouloir saisir une copie de la référence de la fonction dans une variable pour la réutiliser, par exemple:

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    
  2. Utilisez un simple for boucle

    De toute évidence, un simple for La boucle s'applique aux objets de type tableau.

  3. Utilisation for-in  correctement

    for-in avec les mêmes garanties qu'avec un tableau, il devrait aussi fonctionner avec des objets de type tableau; La mise en garde pour les objets fournis par l'hôte sur # 1 ci-dessus peut s'appliquer.

  4. Utilisation for-of (utilisez implicitement un itérateur) (ES2015 +)

    for-of utilisera l'itérateur fourni par l'objet (le cas échéant); nous devrons voir comment cela se joue avec les différents objets de type tableau, en particulier ceux fournis par l'hôte. Par exemple, la spécification pour NodeList de querySelectorAll a été mis à jour pour prendre en charge l'itération. La spécification pour le HTMLCollection de getElementsByTagName n'était pas.

  5. Utiliser un itérateur explicitement (ES2015 +)

    Voir # 4, nous devrons voir comment les itérateurs se déroulent.

Créer un vrai tableau

D'autres fois, vous pouvez convertir un objet de type tableau en un véritable tableau. Faire cela est étonnamment facile:

  1. Utilisez le slice méthode de tableaux

    Nous pouvons utiliser le slice méthode des tableaux, qui, comme les autres méthodes mentionnées ci-dessus est "intentionnellement générique" et peut donc être utilisé avec des objets de type tableau, comme ceci:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);
    

    Ainsi, par exemple, si nous voulons convertir un NodeList dans un véritable tableau, nous pourrions le faire:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
    

    Voir le Avertissement pour les objets fournis par l'hôte au dessous de. En particulier, notez que cela échouera dans IE8 et plus tôt, ce qui ne vous permet pas d'utiliser les objets fournis par l'hôte comme this comme ça.

  2. Utilisation propagation de la syntaxe (...)

    Il est également possible d'utiliser ES2015 syntaxe de propagation avec les moteurs JavaScript qui prennent en charge cette fonctionnalité:

    var trueArray = [...iterableObject];
    

    Ainsi, par exemple, si nous voulons convertir un NodeList dans un vrai tableau, avec la syntaxe de propagation cela devient assez succinct:

    var divs = [...document.querySelectorAll("div")];
    
  3. Utilisation Array.from  (spec) | (MDN)

    Array.from (ES2015 +, mais facilement polyfilled) crée un tableau à partir d'un objet de type tableau, en passant d'abord les entrées via une fonction de mappage. Alors:

    var divs = Array.from(document.querySelectorAll("div"));
    

    Ou si vous souhaitez obtenir un tableau des noms de balises des éléments avec une classe donnée, vous utiliserez la fonction de mappage:

    // Arrow function (ES2015):
    var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Standard function (since `Array.from` can be shimmed):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });
    

Avertissement pour les objets fournis par l'hôte

Si tu utilises Array.prototype fonctionne avec fourni par l'hôte Objets de type tableau (listes DOM et autres éléments fournis par le navigateur plutôt que par le moteur JavaScript), vous devez vous assurer de tester dans vos environnements cibles pour vous assurer que l'objet fourni par l'hôte se comporte correctement. La plupart se comportent correctement (maintenant), mais il est important de tester. La raison en est que la plupart des Array.prototype les méthodes que vous voudrez utiliser dépendent de l'objet fourni par l'hôte pour donner une réponse honnête au résumé [[HasProperty]] opération. Au moment d'écrire ces lignes, les navigateurs font un très bon travail, mais la spécification 5.1 permettait la possibilité qu'un objet fourni par l'hôte ne soit pas honnête. C'est dedans §8.6.2, plusieurs paragraphes sous la grande table près du début de cette section), où il est dit:

Les objets hôtes peuvent implémenter ces méthodes internes de n'importe quelle manière sauf indication contraire; par exemple, une possibilité est que [[Get]] et [[Put]] pour un objet hôte particulier chercher et stocker des valeurs de propriété, mais en effet [[HasProperty]] génère toujours faux.

(Je ne pouvais pas trouver le verbiage équivalent dans la spécification ES2015, mais cela devrait toujours être le cas.) Encore une fois, à partir de cette écriture les objets de type tableau fournis par l'hôte commun dans les navigateurs modernes [NodeList instances, par exemple] faire manipuler [[HasProperty]] correctement, mais il est important de tester.)


5976
2018-02-17 13:53



modifier: Cette réponse est désespérément périmée. Pour une approche plus moderne, regardez les méthodes disponibles sur un tableau. Les méthodes d'intérêt pourraient être:

  • pour chaque
  • carte
  • filtre
  • Zip *: français
  • réduire
  • chaque
  • certains

La façon standard d'itérer un tableau dans JavaScript est une vanille for-boucle:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element i.
}

Notez, cependant, que cette approche n'est bonne que si vous avez un tableau dense, et que chaque index est occupé par un élément. Si le tableau est clairsemé, vous pouvez rencontrer des problèmes de performance avec cette approche, puisque vous allez parcourir de nombreux indices qui ne le sont pas. vraiment exister dans le tableau. Dans ce cas, un for .. in-loop pourrait être une meilleure idée. toutefois, vous devez utiliser les sauvegardes appropriées pour vous assurer que seules les propriétés souhaitées du tableau (c'est-à-dire les éléments du tableau) sont prises en for..in-loop sera également énuméré dans les navigateurs hérités, ou si les propriétés supplémentaires sont définies comme enumerable.

Dans ECMAScript 5 Il y aura une méthode forEach sur le prototype de tableau, mais elle n'est pas prise en charge dans les navigateurs hérités. Donc, pour pouvoir l'utiliser de manière cohérente, vous devez soit avoir un environnement qui le supporte (par exemple, Node.js pour JavaScript côté serveur), ou utilisez un "Polyfill". Le Polyfill pour cette fonctionnalité est, cependant, trivial et puisqu'il rend le code plus facile à lire, c'est un bon polyfill à inclure.


451
2018-02-17 13:55



Si vous utilisez le jQuery bibliothèque, vous pouvez utiliser jQuery.each:

$.each(yourArray, function(index, value) {
  // do your stuff here
});

MODIFIER : 

Selon la question, l'utilisateur veut du code en javascript au lieu de jquery, donc l'édition est

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}

205
2018-02-17 14:01



Boucle arrière

Je pense que le sens inverse pour la boucle mérite une mention ici:

for (var i = array.length; i--; ) {
     // process array[i]
}

Avantages:

  • Vous n'avez pas besoin de déclarer un temporaire len variable, ou comparer par rapport à array.length à chaque itération, ce qui pourrait être une optimisation minute.
  • Retrait des frères et soeurs du DOM dans l'ordre inverse est généralement plus efficace. (Le navigateur doit faire moins de décalage des éléments dans ses tableaux internes.)
  • Si vous modifier le tableau en boucle, à ou après l'index je (par exemple, vous retirez ou insérez un objet à array[i]), alors une boucle avant sauterait l'élément qui s'est décalé vers la gauche en position jeou retraitez le jel'article qui a été déplacé vers la droite. Dans une boucle traditionnelle, vous pourrait mettre à jour je pour pointer vers l'élément suivant qui nécessite un traitement - 1, mais inverser simplement la direction de l'itération est souvent un plus simple et solution plus élégante.
  • De même, lors de la modification ou de la suppression imbriqué Éléments DOM, le traitement en inverse peut contourner les erreurs. Par exemple, pensez à modifier innerHTML d'un nœud parent avant de gérer ses enfants. Au moment où le nœud enfant est atteint, il sera détaché du DOM, après avoir été remplacé par un enfant nouvellement créé lorsque le innerHTML du parent a été écrit.
  • C'est plus court taper, et lis, que certaines des autres options disponibles. Bien qu'il perde forEach() et à ES6 for ... of.

Désavantages:

  • Il traite les éléments dans l'ordre inverse. Si vous construisiez un nouveau tableau à partir des résultats, ou si vous imprimiez des choses à l'écran, naturellement la sortie sera inversée par rapport à l'ordre original.
  • Insertion répétée de frères et soeurs dans le DOM en tant que premier enfant afin de conserver leur commande est moins efficace. (Le navigateur continuerait à devoir changer les choses correctement.) Pour créer des nœuds DOM efficacement et dans l'ordre, il suffit de faire une boucle en avant et d'ajouter normalement (et aussi d'utiliser un "fragment de document").
  • La boucle inverse est déroutant aux développeurs juniors. (Vous pouvez considérer cela comme un avantage, en fonction de vos perspectives.)

Dois-je toujours l'utiliser?

Certains développeurs utilisent l'inverse pour la boucle par défaut, sauf s'il y a une bonne raison de faire une boucle en avant.

Bien que les gains de performance sont généralement insignifiants, il crie:

"Fais juste ça à chaque article de la liste, je me fiche de la commande!"

Cependant, dans la pratique, c'est ne pas en fait une indication fiable de l'intention, car elle est indiscernable de ces occasions où faire se soucient de la commande, et vraiment faire avoir besoin faire une boucle en sens inverse. Donc, en fait, une autre construction serait nécessaire pour exprimer avec précision l'intention de "ne pas se soucier", ce qui est actuellement indisponible dans la plupart des langues, y compris ECMAScript, mais qui pourrait être appelé, par exemple, forEachUnordered().

Si l'ordre n'a pas d'importance, et Efficacité est une préoccupation (dans la boucle la plus intérieure d'un jeu ou d'un moteur d'animation), alors il peut être acceptable d'utiliser la boucle inverse pour votre modèle go-to. Rappelez-vous juste que voir un inverse pour la boucle dans le code existant ne signifie pas nécessairement que l'ordre n'est pas pertinent!

Il est préférable d'utiliser forEach ()

En général pour le code de niveau supérieur où clarté et sécurité sont de plus grandes préoccupations, je recommanderais d'utiliser Array::forEach comme motif par défaut:

  • C'est clair à lire.
  • Cela indique que je ne va pas être déplacé dans le bloc (ce qui est toujours une surprise possible se cachant dans de longues for et while boucles.)
  • Il vous donne une portée libre pour les fermetures.
  • Il réduit la fuite des variables locales et la collision accidentelle avec (et la mutation) des variables externes.

Ensuite, quand vous voyez l'inverse pour la boucle dans votre code, c'est un indice qu'il est inversé pour une bonne raison (peut-être l'une des raisons décrites ci-dessus). Et voir une boucle traditionnelle vers l'avant peut indiquer que le déplacement peut avoir lieu.

(Si la discussion de l'intention n'a aucun sens pour vous, alors vous et votre code pourriez bénéficier de regarder la conférence de Crockford sur Style de programmation et votre cerveau.)


Comment ça marche?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

Vous remarquerez que i-- est la clause du milieu (où nous voyons habituellement une comparaison) et la dernière clause est vide (où nous voyons habituellement i++). Cela signifie que i-- est également utilisé comme condition pour la suite. Fondamentalement, il est exécuté et vérifié avant chaque itération.

  • Comment peut-il commencer à array.length sans exploser?

    Car i-- fonctionne avant chaque itération, à la première itération, nous allons effectivement accéder à l'élément à array.length - 1 ce qui évite tout problème avec Array-out-of-bounds  undefined articles.

  • Pourquoi n'arrête-t-il pas d'itérer avant l'index 0?

    La boucle arrête d'itérer lorsque la condition i-- évalue à une valeur de falsey (quand elle donne 0).

    L'astuce est que contrairement --i, le suivi i-- décrémente l'opérateur i mais donne la valeur avant le décrément. Votre console peut le démontrer:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Donc, sur la dernière itération, je était auparavant 1 et le i-- l'expression change à 0 mais donne en réalité 1 (vérité), et ainsi la condition passe. Sur la prochaine itération i-- changements je à -1 mais donne 0 (falsey), provoquant l'exécution de tomber immédiatement hors du fond de la boucle.

    Dans les avant traditionnels pour la boucle, i++ et ++i sont interchangeables (comme le souligne Douglas Crockford). Cependant, à l'inverse pour la boucle, parce que notre décrément est aussi notre expression de condition, nous devons rester avec i-- si nous voulons traiter l'élément à l'index 0.


Anecdotes

Certaines personnes aiment dessiner une petite flèche dans le sens inverse for boucle, et se termine par un clin d'oeil:

for (var i = array.length; i --> 0 ;) {

Les crédits vont à WYL pour m'avoir montré les avantages et les horreurs de l'inverse pour la boucle.


88
2018-05-02 14:21



Certains Cutilisation des langues de style foreach faire une boucle dans les énumérations. En JavaScript, cela est fait avec le for..in structure en boucle:

var index,
    value;
for (index in obj) {
    value = obj[index];
}

Il y a une prise. for..in bouclera à travers chacun des membres énumérable de l'objet, et les membres sur son prototype. Pour éviter de lire les valeurs héritées du prototype de l'objet, vérifiez simplement si la propriété appartient à l'objet:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Aditionellement, ECMAScript 5 a ajouté un forEach méthode pour Array.prototypequi peut être utilisé pour énumérer sur un tableau en utilisant un calback (le polyfill est dans les docs, donc vous pouvez toujours l'utiliser pour les navigateurs plus anciens):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

Il est important de noter que Array.prototype.forEach ne se casse pas lorsque le rappel revient false. jQuery et Underscore.js fournir leurs propres variations sur each pour fournir des boucles qui peuvent être court-circuitées.


71
2018-02-17 14:00



Si vous voulez faire une boucle sur un tableau, utilisez le standard en trois parties for boucle.

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

Vous pouvez obtenir des optimisations de performance en mettant en cache myArray.length ou itérer à l'envers.


30
2018-02-17 13:55



UNE pour chaque la mise en oeuvre (voir dans jsFiddle):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);

25
2018-04-10 00:26



Si cela ne vous dérange pas de vider le tableau:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

x contiendra la dernière valeur de y et il sera retiré du tableau. Vous pouvez aussi utiliser shift() qui donnera et enlèvera le premier article de y.


25
2018-03-10 02:37



Je sais que c'est un vieux message, et il y a déjà beaucoup de bonnes réponses. Pour un peu plus de complétude je me suis dit que j'en ajouterais un autre en utilisant AngularJS. Bien sûr, cela ne s'applique que si vous utilisez Angular, évidemment, néanmoins je voudrais le mettre de toute façon.

angular.forEach prend 2 arguments et un troisième argument optionnel. Le premier argument est l'objet (tableau) à parcourir, le deuxième argument est la fonction d'itérateur, et le troisième argument facultatif est le contexte d'objet (fondamentalement appelé dans la boucle «ceci».

Il existe différentes façons d'utiliser la boucle forEach de angular. Le plus simple et probablement le plus utilisé est

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Une autre méthode utile pour copier des éléments d'un tableau à un autre est

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

Bien que vous n'ayez pas à faire cela, vous pouvez simplement faire ce qui suit et c'est équivalent à l'exemple précédent:

angular.forEach(temp, function(item) {
    temp2.push(item);
});

Maintenant, il y a des avantages et des inconvénients de l'utilisation du angular.forEach fonction par opposition à la vanille intégrée for boucle.

Avantages

  • Lisibilité facile
  • Facilité d'écriture
  • Si disponible, angular.forEach utilisera la boucle ES5 forEach. Maintenant, je vais arriver à l'efficacité dans la section des inconvénients, comme les boucles forEach sont beaucoup plus lent que les boucles for. Je mentionne cela comme un pro parce que c'est bien d'être cohérent et standardisé.

Considérez les 2 boucles imbriquées suivantes, qui font exactement la même chose. Disons que nous avons 2 tableaux d'objets et que chaque objet contient un tableau de résultats, chacun ayant une propriété Value qui est une chaîne (ou autre). Et disons que nous devons répéter sur chacun des résultats et s'ils sont égaux, alors effectuez une action:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

Certes, il s'agit d'un exemple hypothétique très simple, mais j'ai écrit triple embedded pour les boucles en utilisant la deuxième approche et il était très difficile à lire, et écrire d'ailleurs.

Les inconvénients

  • Efficacité. angular.forEachet le natif forEach, d'ailleurs, sont à la fois tellement de plus lent que la normale for boucle .... à propos 90% plus lent. Donc, pour les grands ensembles de données, le mieux de s'en tenir à l'indigène for boucle.
  • Pas de pause, continuer ou retourner le support. continue est réellement soutenu par "accident", de continuer dans un angular.forEach vous mettez simplement un return; déclaration dans la fonction comme angular.forEach(array, function(item) { if (someConditionIsTrue) return; }); ce qui le fera continuer hors de la fonction pour cette itération. Cela est également dû au fait que le natif forEach ne supporte pas le break ou continue non plus.

Je suis sûr qu'il y a d'autres avantages et inconvénients, et n'hésitez pas à ajouter tout ce que vous voulez. Je pense que, en bout de ligne, si vous avez besoin d'efficacité, s'en tenir à l'indigène for boucle pour vos besoins en boucle. Mais, si vos ensembles de données sont plus petits et qu'une certaine efficacité est acceptable pour abandonner en échange de la lisibilité et de l'écriture, alors jetez un angular.forEach dans ce mauvais garçon.


25
2018-06-20 22:56



Une solution facile maintenant serait d'utiliser le bibliothèque underscore.js. Il fournit de nombreux outils utiles, tels que each et déléguera automatiquement le travail au natif forEach si disponible.

Un exemple CodePen de la façon dont cela fonctionne est:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Voir également


24
2017-07-17 09:07



Probablement le for(i = 0; i < array.length; i++) La boucle n'est pas le meilleur choix. Pourquoi? Si vous avez ceci:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

La méthode appellera de array[0] à array[2]. Tout d'abord, cela va d'abord référencer les variables que vous n'avez même pas, ensuite vous n'aurez pas les variables dans le tableau, et troisièmement cela rendra le code plus audacieux. Regardez ici, c'est ce que j'utilise:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

Et si vous voulez que ce soit une fonction, vous pouvez le faire:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

Si vous voulez casser, un peu plus de logique:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

Exemple:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Il retourne:

//Hello
//World
//!!!

21
2017-11-02 02:23