Pour chaque tableau en javascript?

Comment afficher toutes les entrées d'un tableau à l'aide de javascript?

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

 forEach(instance in theArray) 

theArray est mon tableau, mais cela semble faux.

4041
17 февр. Dante1986 fixé le 17 février 2012-02-17 16:51 '12 à 16:51 heures 2012-02-17 16:51
@ 32 réponses
  • 1
  • 2

Tl; DR

  • Ne l'utilisez pas si vous ne l'utilisez pas avec une protection ou, du moins, ne savez pas pourquoi il pourrait vous mordre.
  • Vos meilleurs paris sont généralement

    • boucle for-of (uniquement pour ES2015 +),
    • Array#forEach ( spec | MDN ) (ou ses proches, par exemple) (ES5 + uniquement)
    • simple démodé for boucles,
    • ou for-in entrée avec protection.

Mais il reste encore beaucoup à explorer, lisez la suite ...


JavaScript possède une sémantique puissante pour transformer de manière cyclique des tableaux et des objets de type tableau. J'ai divisé la réponse en deux parties: des options pour les tableaux authentiques et des options similaires à des tableaux, tels que l'objet arguments , d'autres objets itérables (ES2015 +), des collections DOM, etc.

Je remarque rapidement que vous pouvez maintenant utiliser les options ES2015, même sur les moteurs ES5, en envoyant ES2015 à ES5. Trouvez "ES2015 transpiling" / "ES6 transpiling" pour plus ...

Eh bien, regardez nos options:

Pour les tableaux réels

Vous avez trois options dans ECMAScript 5 ("ES5"), la version la plus largement prise en charge pour le moment, et deux autres ajoutées dans ECMAScript 2015 ("ES2015", "ES6"):

  1. Utilisez forEach et connexe (ES5 +)
  2. Utilisez simple for loop
  3. Utiliser correctement for-in
  4. Utiliser for-of (utiliser un itérateur implicite) (ES2015 +)
  5. Utiliser explicitement l'itérateur (ES2015 +)

Détails:

1. Utilisez forEach et connexes

Dans n'importe quel environnement indéfiniment moderne (enfin, pas dans IE8), où vous avez accès aux fonctions de Array ajoutées par ES5 (directement ou à l'aide de remplissages polyple), 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, éventuellement, une valeur utilisée de la sorte lors de l'appel de ce rappel (non utilisé ci-dessus). Le rappel est appelé pour chaque entrée du tableau pour ignorer les entrées inexistantes dans les tableaux fragmentés. Bien que je n’ai utilisé qu’un seul argument ci-dessus, le rappel est appelé avec trois: la valeur de chaque enregistrement, l’index de cet enregistrement et le lien vers le tableau que vous répétez (au cas où votre fonction ne l’aurait pas déjà).

Si vous ne prenez pas en charge les navigateurs existants, tels que IE8 (ce qui correspond à plus de 4% du marché de NetApps au moment d'écrire ces forEach en septembre 2016), vous pouvez utiliser joyeusement forEach sur une page Web universelle sans mise en page. Si vous devez prendre en charge des navigateurs obsolètes, il est facile d’effectuer l’opération de calage / polyfilling pour forEach (recherchez "es5 shim" pour plusieurs options).

forEach a l'avantage qu'il n'est pas nécessaire de déclarer les valeurs d'indexation et de variable dans la zone de contenu, car elles sont fournies en tant qu'arguments de la fonction d'itération et sont si bien orientées pour cette itération.

Si vous êtes préoccupé par le temps nécessaire pour exécuter un appel de fonction pour chaque enregistrement de tableau, ne le soyez pas; les détails

En outre, forEach est une fonction "en boucle de tous", mais ES5 a défini plusieurs autres "élaborations utiles à l'aide de fonctions de tableau et d'autres choses", notamment:

  • every (arrête la boucle la première fois que le rappel renvoie false ou quelque chose de faux)
  • some (arrête la boucle la première fois que le rappel retourne true ou quelque chose de plausible)
  • filter (crée un nouveau tableau contenant des éléments dans lesquels la fonction de filtrage renvoie true et omet ceux où elle renvoie false )
  • map (crée un nouveau tableau de valeurs de rappel de retour)
  • reduce (incrémente une valeur, ré-appelle un rappel, passe les valeurs précédentes, voir la spécification pour plus de détails, utile pour additionner le contenu d'un tableau et bien d'autres choses)
  • reduceRight (par exemple, reduce , mais fonctionne par ordre décroissant, pas par ordre croissant)

2. Utilisez une simple boucle for

Parfois, les anciennes 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 de la matrice ne change pas au cours du cycle et que ses performances sont un code sensible (peu probable), une version légèrement plus compliquée de la capture avec la longueur de l'avant peut être un peu plus rapide:

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

Et / ou compte à rebours:

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

Mais avec les mécanismes JavaScript modernes, vous devez rarement extraire ce dernier jus.

Dans ES2015 et les versions ultérieures, vous pouvez rendre vos variables d'index et de valeur locales for boucles for :

 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 lorsque vous faites cela, non seulement la value mais aussi l' index recréés pour chaque itération de la boucle, c'est-à-dire que les fermetures créées dans la balise de la boucle contiennent une référence à l' index (et à la value ) créé pour cette itération particulière:

 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 obtiendrez "Index is: 0", si vous avez cliqué sur le premier et "Index is: 4", si vous avez cliqué sur le dernier. Cela ne fonctionne pas si vous utilisez var au lieu de let .

3. Utilisez for-in correctement.

Vous allez amener les gens à vous dire d'utiliser for-in , mais ce n'est pas for-in que for-in . for-in écrit via les propriétés énumérées de l'objet, et non les indices de tableau. Ordre non garanti , même en ES2015 (ES6). ES2015 détermine effectivement l'ordre des propriétés d'un objet (à l'aide de [[OwnPropertyKeys]] , [[Enumerate]] , et des éléments qui les utilisent sous la forme Object.getOwnPropertyKeys ), mais il ne spécifie pas que l' Object.getOwnPropertyKeys suivra cet ordre. (Détails dans cette autre réponse .)

Toutefois, cela peut être utile, en particulier pour les tableaux fragmentés , si vous prenez les précautions 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 vérifications:

  1. Que l'objet possède sa propre propriété sous ce nom (et non celui qu'il hérite de son prototype), et

  2. La clé est une chaîne numérique de base 10 dans sa forme de chaîne habituelle et sa valeur est <= 2 ^ 32 - 2 (ce qui correspond à 4 294 967 294). D'où vient ce numéro? Cela fait partie de la définition d'index de tableau dans la spécification . Les autres nombres (nombres non entiers, nombres négatifs, nombres supérieurs à 2 ^ 32 - 2) ne sont pas des indices de tableau. La cause de 2 ^ 32 - 2 est la valeur d'index la plus élevée inférieure à 2 ^ 32 - 1 , qui est la valeur maximale de la length tableau. (Par exemple, la longueur du tableau correspond à un entier non signé de 32 bits.) (Confirmation de RobG d'indiquer dans un commentaire sur mon message de blog que mon précédent test n'était pas tout à fait correct).

Cela représente un tout petit supplément de temps supplémentaire pour chaque itération de boucles sur la plupart des tableaux, mais si vous avez un tableau fragmenté, cela peut s'avérer un moyen plus efficace de boucler, car ce ne sont que des boucles pour des enregistrements existants. Par exemple, pour le tableau ci-dessus, nous effectuons une boucle trois fois (pour les clés "0" , "10" et "10000" - rappelez-vous que ce sont des lignes) et non pas 10 001 fois.

Maintenant, vous ne voulez pas l'écrire à chaque fois, vous pouvez donc 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 ensuite, nous allons l'utiliser comme ceci:

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

Ou, si vous êtes seulement intéressé par le test “assez bon pour la plupart des cas”, vous pouvez l'utiliser, mais tant qu'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. Utilisez for-of (utilisez un itérateur implicite) (ES2015 +)

ES2015 ajoute des itérateurs à JavaScript. Le moyen le plus simple d'utiliser des itérateurs est le nouvel opérateur for-of . Cela ressemble à ceci:

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

Conclusion:

 un b c

Sous les couvertures, qui obtient un itérateur du tableau et le traverse, en tire des valeurs. L'utilisation de for-in ne pose pas de problème, car elle utilise un itérateur défini par un objet (tableau) et les tableaux déterminent que leurs itérateurs répètent leurs enregistrements (et non leurs propriétés). Contrairement à for-in dans ES5, l'ordre dans lequel les enregistrements sont visionnés est l'ordre numérique de leurs index.

5. Utilisez explicitement l'itérateur (ES2015 +)

Parfois, vous pouvez utiliser explicitement un itérateur. Vous pouvez le faire aussi, bien que ce soit beaucoup plus encombrant que for-of reste. Cela ressemble à ceci:

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

Un itérateur est un objet qui correspond à la définition d'Iterator dans la spécification. Sa méthode next retourne un nouvel objet résultat à chaque fois que vous l'appelez. L'objet de résultat a une propriété, done , nous indique si cela a été fait et la value propriété avec la valeur de cette itération. ( done optionnel s'il est false , value est optionnel s'il n'est pas undefined ).

La value varie en fonction de l'itérateur. Les tableaux prennent en charge (au moins) trois fonctions renvoyant des itérateurs:

  • values() : C'est celui que j'ai utilisé ci-dessus. Il renvoie un itérateur, où chaque value est un enregistrement de tableau pour cette itération ( "a" , "b" et "c" dans l'exemple ci-dessus).
  • keys() : Retourne un itérateur, où chaque value est la clé de cette itération (donc pour la précédente, il y aura a "0" , puis "1" et ensuite "2" ).
  • entries() : retourne un itérateur, où chaque value est un tableau sous la forme [key, value] pour cette itération.

Pour des objets comme un tableau

En plus des tableaux réels, il existe également des objets de type tableau qui ont la propriété length et des propriétés avec des noms numériques: occurrences NodeList , un objet arguments , etc. Comment pouvons-nous NodeList leur contenu?

Utilisez l'un des paramètres ci-dessus pour les tableaux.

Certains au moins, et peut-être même la plupart, voire tous les tableaux ci-dessus sont souvent utilisés aussi bien pour des objets du type tableau:

  1. Utilisez forEach et connexe (ES5 +)

    Les diverses fonctions de Array.prototype sont "intentionnellement génériques" et peuvent généralement être utilisées pour des objets tels qu'un tableau par le biais de Function#call Function#apply ou Function#apply . (Voir Clause de non-responsabilité pour les objets fournis par l'hôte à la fin de cette réponse, mais il s'agit d'un problème rare.)

    Supposons que vous souhaitiez utiliser forEach dans les Node forEach du Node childNodes . Vous feriez ceci:

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

    Si vous envisagez de le faire souvent, vous pouvez obtenir une copie de la référence de la fonction dans une variable à 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 simple for loop

    De toute évidence, une simple boucle for est appliquée aux objets comme un tableau.

  3. Utiliser correctement for-in

    for-in avec les mêmes garanties qu'un tableau, devrait fonctionner avec des objets similaires à un tableau; Des réservations peuvent être nécessaires pour les articles fournis par l'hôte au numéro 1 ci-dessus.

  4. Utiliser for-of (utiliser un itérateur implicite) (ES2015 +)

    for-of utilisera l'itérateur fourni par l'objet (le cas échéant); nous devrons voir comment cela se produit avec divers objets de type tableau, en particulier avec les fichiers hôtes. Par exemple, la spécification de NodeList partir de querySelectorAll été mise à jour pour prendre en charge l'itération. La spécification pour la collection HTMLCollection de getElementsByTagName pas.

  5. Utiliser explicitement l'itérateur (ES2015 +)

    Voir le numéro 4, nous devons voir comment les itérateurs se déroulent.

Créer un vrai tableau

Dans d'autres cas, vous pouvez convertir un objet tel qu'un tableau en un vrai tableau. Faire ceci est étonnamment facile:

  1. Utilisez la méthode du tableau de slice

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

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

    Ainsi, par exemple, si nous voulons convertir une NodeList de NodeList en un tableau réel, nous pourrions procéder comme NodeList :

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

    Voir "Attention pour les objets fournis par l'hôte" ci-dessous. Notez en particulier que cela ne fonctionnera pas dans IE8 et les versions antérieures, ce qui ne permet pas l'utilisation d'objets fournis par l'hôte de this .

  2. Utiliser la syntaxe de distribution ( ... )

    Il est également possible d'utiliser la syntaxe de distribution ES2015 avec des mécanismes JavaScript prenant en charge cette fonctionnalité:

     var trueArray = [...iterableObject]; 

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

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

    Array.from ( Array.from +, mais facilement rempli de poly) crée un tableau à partir d'un objet, tel qu'un tableau, si nécessaire, en passant d'abord les enregistrements par la fonction correspondante. Donc:

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

    Ou, si vous souhaitez obtenir un tableau de noms de balises pour les éléments d'une classe donnée, vous devez utiliser 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; }); 

Attention pour les installations fournies par l'hôte

Si vous utilisez des fonctions Array.prototype avec des objets comme des hôtes (listes DOM et autres éléments fournis par le navigateur plutôt que par le moteur JavaScript), vous devez vous assurer de tester vos environnements cible pour vous assurer que l'objet hôte fourni se comporte bien. est correct. La plupart d'entre eux se comportent correctement (maintenant), mais il est important de vérifier. La raison en est que la plupart des méthodes Array.prototype vous souhaitez probablement utiliser dépendent de l'objet fourni par l'hôte, donnant une réponse honnête à l'abstrait [[HasProperty]] . Au moment de la rédaction de ce document, les navigateurs ont très bien réussi ce travail, mais la spécification 5.1 suggérait que l'objet fourni par l'hôte n'était peut-être pas juste. Ceci est au § 8.6.2 , quelques paragraphes en dessous du grand tableau au début de cette section, où il est écrit:

Les objets hôtes peuvent implémenter ces méthodes internes de quelque manière que ce soit, sauf indication contraire. Par exemple, une possibilité est que [[Get]] et [[Put]] pour un objet hôte particulier récupère et enregistre effectivement les valeurs de propriété, mais [[HasProperty]] génère toujours false .

(Je ne pouvais pas trouver un libellé équivalent dans la spécification ES2015, mais il doit encore l'être.) Comme pour l'écriture d'un hôte commun du tableau de type d'objet fourni dans les navigateurs modernes [les instances NodeList , par exemple], rendent le [[HasProperty]] correct, mais important. vérifier.)

6292
17 февр. La réponse est donnée par TJ Crowder le 17 février. 2012-02-17 16:53 '12 à 16h53 2012-02-17 16:53

Edit : cette réponse est désespérément obsolète. Pour une approche plus moderne, examinez les méthodes disponibles dans le tableau . Les méthodes d'intérêt peuvent être:

  • pour chacun
  • carte
  • le filtre
  • fermeture à glissière
  • réduire
  • tous les
  • un petit peu

La méthode standard pour itérer un tableau en JavaScript est for -loop vanilla:

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

Notez cependant que cette approche n’est valable que si vous avez un tableau dense et que chaque index est occupé par un élément. Si le tableau est clairsemé, cette approche risque de poser des problèmes de performances, car vous allez parcourir un grand nombre d’index qui n’existent pas dans le tableau. Dans ce cas, il vaut mieux utiliser for.. in -loop. Toutefois, vous devez prendre les précautions appropriées pour vous assurer que seules les propriétés requises du tableau (c’est-à-dire des éléments de tableau) sont en for..in , car for..in -loop sera également répertorié dans les anciens navigateurs ou si les propriétés supplémentaires sont définies comme enumerable .

Dans ECMAScript 5 , la méthode forEach sera utilisée pour le prototype de tableau, mais elle n'est pas prise en charge par les navigateurs hérités. Par conséquent, pour pouvoir l'utiliser de manière cohérente, vous devez disposer d'un environnement qui le prend en charge (par exemple, Node.js pour JavaScript côté serveur) ou utiliser le "Polyfill". Polyfill pour cette fonctionnalité, cependant, est trivial, et puisqu'il facilite la lecture du code, il devrait être inclus dans Polyfill.

470
17 февр. Réponse donnée par PatrikAkerstrand le 17 février 2012-02-17 16:55 '12 à 16h55 2012-02-17 16:55

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

 var length = yourArray.length; for (var i = 0; i < length; i++) { // Do something with yourArray[i]. } 
214
17 февр. La réponse est donnée à Poonam le 17 février. 2012-02-17 17:01 '12 à 17:01 2012-02-17 17:01

Boucle de retour

Je pense que l'inverse pour le cycle mérite une mention ici:

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

Avantages:

  • Vous n'avez pas besoin de déclarer une variable temporaire len ou de la comparer avec array.length à chaque itération, ce qui peut être une optimisation minutieuse.
  • Supprimer les frères et sœurs du DOM dans l'ordre inverse est généralement plus efficace . (Le navigateur doit déplacer moins d'éléments dans les tableaux internes.)
  • Si vous modifiez un tableau pendant une boucle, à ou après un index I (par exemple, vous supprimez ou insérez un élément dans le array[i] ), une boucle directe ignore un élément décalé à gauche vers la position i ou revérifier le e élément décalé à droite. Dans la boucle traditionnelle, vous pouvez mettre à jour i pour indiquer le prochain élément à traiter, à savoir 1, mais le simple changement de la direction de l'itération est souvent une solution plus simple et plus élégante .
  • De même, lors du changement ou de la suppression d'éléments DOM imbriqués , le traitement inverse peut contourner les erreurs . Par exemple, envisagez de modifier le code innerHTML du nœud parent avant de traiter ses enfants. Au moment où le noeud enfant est atteint, il sera séparé du DOM et le remplacera par un nouveau descendant lors de l'écriture de innerHTML parent.
  • plus court et lu que certaines autres options disponibles. Bien qu'il perde à forEach() et à ES6 for ... of .

Inconvénients:

  • Il traite les éléments dans l'ordre inverse. Если вы строили новый массив из результатов или печатали на экране, естественно вывод будет отменен относительно исходного порядка.
  • Неоднократно вставляя братьев и сестер в DOM в качестве первого ребенка, чтобы сохранить их порядок, менее эффективен . (В браузере все равно придется перекладывать вещи.) Чтобы создавать узлы DOM эффективно и упорядоченно, просто переходите вперед и добавьте как обычно (а также используйте "фрагмент документа" ).
  • Обратный цикл запутывает для младших разработчиков. (Вы можете считать это преимущество, в зависимости от вашего прогноза.)

Должен ли я всегда использовать его?