La gestion de chaque élément d'une collection est une opération très courante. JavaScript fournit de nombreuses façons d'itérer une collection, de simple pour et pour chaque boucle à map (), filter () et compréhension du tableau (dérivation du tableau). Dans JavaScript 1.7, les itérateurs et les générateurs apportent de nouveaux mécanismes itératifs dans la syntaxe JavaScript de base, et fournissent également des mécanismes pour personnaliser le comportement de… dans et pour chaque boucles.
Itérateur
Un iterator est un objet qui accède à un élément d'une séquence de collecte à chaque fois et suit la position actuelle des itérations dans cette séquence. Dans JavaScript Iterator est un objet qui fournit une méthode suivante (), qui renvoie l'élément suivant de la séquence. Cette méthode lance une exception d'arrêt lorsque tous les éléments de la séquence sont traversés.
Une fois l'objet Iterator créé, il peut être appelé implicitement en répétant explicitement Next (), ou en utilisant JavaScript pour… dans et pour chaque boucle.
Un itérateur simple qui itère sur des objets et des tableaux peut être créé à l'aide d'Iterator ():
La copie de code est la suivante:
var lang = {name: 'javascript', anniversaire: 1995};
var it = iterator (lang);
Une fois l'initialisation terminée, la méthode suivante () peut être appelée pour accéder aux paires de valeurs de clé de l'objet à tour de rôle:
La copie de code est la suivante:
var paire = it.next (); // Les paires de valeurs clés sont ["nom", "javascript"]
paire = it.next (); // La paire de valeurs clés est ["anniversaire", 1995]
paire = it.next (); // Une exception de «l'arrêt» a été lancée
Le FOR… en boucle peut être utilisé pour remplacer l'appel explicite à la méthode suivante (). Lorsque l'exception de l'arrêt est lancée, la boucle se terminera automatiquement.
La copie de code est la suivante:
var it = iterator (lang);
pour (var paire dedans)
imprimer (paire); // une paire de valeurs de clé [clé, valeur] dans elle est sortie à chaque fois
Si vous voulez seulement parcourir la valeur clé de l'objet, vous pouvez passer le deuxième paramètre dans la fonction iterator (), avec la valeur true:
La copie de code est la suivante:
var it = iterator (lang, true);
pour (clé var dedans)
imprimer (clé); // seule valeur de clé de sortie
Un avantage de l'utilisation d'Iterator () pour accéder aux objets est que les propriétés personnalisées ajoutées à l'objet.prototype ne sont pas incluses dans l'objet de séquence.
Iterator () peut également être utilisé sur un tableau:
La copie de code est la suivante:
var langs = ['javascript', 'python', 'haskell'];
var it = iterator (langs);
pour (var paire dedans)
imprimer (paire); // Seule itération Sortie [index, langage] Paire de valeurs de clé
Tout comme la traversée d'un objet, le résultat du passage True dans la traversée en deuxième paramètre sera l'indice du tableau:
La copie de code est la suivante:
var langs = ['javascript', 'python', 'haskell'];
var it = iterator (langs, true);
pour (var i dedans)
imprimer (i); // Sortie 0, puis 1, puis 2
Utilisez le mot-clé LET pour attribuer des index et des valeurs pour bloquer les variables séparément dans la boucle, et vous pouvez également détruire l'affectation (affectation destructrice):
La copie de code est la suivante:
var langs = ['javascript', 'python', 'haskell'];
var it = itérateurs (langs);
pour (laisse [je, lang])
print (i + ':' + lang); // sortie "0: javascript" etc.
Déclarer un itérateur personnalisé
Certains objets représentant une collection d'éléments doivent être itérés de manière spécifiée.
1. Ilétériser sur un objet représentant une plage (plage) doit renvoyer le nombre contenu dans cette plage un par un.
2.
3. Itérer sur un objet représentant les résultats de la requête de la base de données doit renvoyer la ligne par ligne, même si l'ensemble des résultats n'a pas été chargé dans un seul tableau.
4. Les itérateurs agissant sur une séquence mathématique infinie (comme la séquence de Fibonacci) devraient renvoyer les résultats les uns après les autres sans créer de structure de données de longueur infinie.
JavaScript vous permet d'écrire du code qui personnalise la logique itérative et de l'appliquer à un objet
Nous créons un objet de plage simple avec deux valeurs:
La copie de code est la suivante:
Plage de fonctions (faible, haut) {
this.low = bas;
this.high = high;
}
Maintenant, nous créons un itérateur personnalisé qui renvoie une séquence de tous les entiers dans la gamme. L'interface Iterator nous oblige à fournir une méthode suivante () pour retourner l'élément suivant dans la séquence ou lancer une exception d'arrêt.
La copie de code est la suivante:
Fonction Rangeiterator (plage) {
this.Range = range;
this.current = this.range.low;
}
Rangeiterator.prototype.next = function () {
if (this.current> this.range.high)
lancer une étape;
autre
Renvoie ce.current ++;
};
Notre plageiterator est instancié par une instance de plage tout en maintenant une propriété actuelle pour suivre l'emplacement de la séquence actuelle.
Enfin, pour que Rangeiterator soit combiné avec la plage, nous devons ajouter une méthode spéciale __iterator__ à Range. Lorsque nous essayons d'itérer sur une plage, il sera appelé et devrait renvoyer une instance de Rangeiterator qui implémente la logique itérative.
La copie de code est la suivante:
Range.prototype .__ iterator__ = fonction () {
retourner un nouveau rangeiterator (ceci);
};
Après avoir terminé notre itérateur personnalisé, nous pouvons parcourir une instance de portée:
La copie de code est la suivante:
Var Range = nouvelle plage (3, 5);
pour (var i à portée)
imprimer (i); // Sortie 3, puis 4, puis 5
Générateur: une meilleure façon de construire des itérateurs
Bien que les itérateurs personnalisés soient un outil utile, vous devez les planifier soigneusement lors de leur création car ils doivent être explicitement entretenus.
Le générateur offre des fonctions puissantes: il vous permet de définir une fonction qui contient votre propre algorithme itératif, et il peut automatiquement maintenir son état.
Un générateur est une fonction spéciale qui peut être utilisée comme usine d'itérateur. Si une fonction contient une ou plusieurs expressions de rendement, elle s'appelle un générateur (Note du traducteur: Node.js doit également être représenté par * avant le nom de la fonction).
Remarque: Le mot clé de rendement ne peut être utilisé que pour les blocs de code dans HTML qui sont inclus dans <Script Type = "Application / JavaScript; version = 1.7"> (ou ultérieure). Les balises de script XUL (Langue d'interface utilisateur XML) ne nécessitent pas de spécification de ce bloc de code spécial pour accéder à ces fonctionnalités.
Lorsqu'une fonction de générateur est appelée, le corps de fonction ne sera pas exécuté immédiatement, il renverra un objet générateur-itérateur. Chaque fois que la méthode suivante () du générateur-itérateur est appelée, le corps de fonction s'exécutera à l'expression du rendement suivant puis renverra son résultat. Lorsque la fonction se termine ou rencontre une instruction RETOUR, une exception d'arrêt sera lancée.
Utilisez un exemple pour mieux illustrer:
La copie de code est la suivante:
fonction SimpleGenerator () {
rendement "d'abord";
rendement "deuxième";
Rendre "Troisième";
pour (var i = 0; i <3; i ++)
rendement i;
}
var g = SimpleGenerator ();
print (g.next ()); // Sortie "d'abord"
print (g.next ()); // Sortie "seconde"
print (g.next ()); // Sortie "Troisième"
print (g.next ()); // sortie 0
print (g.next ()); // Sortie 1
print (g.next ()); // Sortie 2
print (g.next ()); // Jetez une exception d'arrêt
Les fonctions du générateur peuvent être utilisées directement par une classe comme méthode __iterator__ et peuvent réduire efficacement la quantité de code où les itérateurs personnalisés sont nécessaires. Réécrivons la plage à l'aide du générateur:
La copie de code est la suivante:
Plage de fonctions (faible, haut) {
this.low = bas;
this.high = high;
}
Range.prototype .__ iterator__ = fonction () {
pour (var i = this.low; i <= this.high; i ++)
rendement i;
};
Var Range = nouvelle plage (3, 5);
pour (var i à portée)
imprimer (i); // Sortie 3, puis 4, puis 5
Tous les générateurs ne se termineront pas, vous pouvez créer un générateur qui représente une séquence infinie. Le générateur suivant implémente une séquence Fibonacci, dans laquelle chaque élément est la somme des deux premiers:
La copie de code est la suivante:
fonction fibonacci () {
var fn1 = 1;
var fn2 = 1;
tandis que (1) {
Var Current = Fn2;
fn2 = fn1;
fn1 = fn1 + courant;
courant de rendement;
}
}
Var Sequence = fibonacci ();
print (Sequence.Next ()); // 1
print (Sequence.Next ()); // 1
print (Sequence.Next ()); // 2
print (Sequence.Next ()); // 3
print (Sequence.Next ()); // 5
print (Sequence.Next ()); // 8
print (Sequence.Next ()); // 13
Les fonctions du générateur peuvent prendre des paramètres et utiliseront ces paramètres lorsque la fonction est appelée pour la première fois. Le générateur peut être terminé (ce qui le fait lancer une exception d'arrêt) en utilisant l'instruction RETOUR. La variante Fibonacci () suivante prend un paramètre limite facultatif qui termine la fonction lorsque la condition est déclenchée.
La copie de code est la suivante:
fonction fibonacci (limite) {
var fn1 = 1;
var fn2 = 1;
tandis que (1) {
Var Current = Fn2;
fn2 = fn1;
fn1 = fn1 + courant;
if (limit && actuel> limite)
retour;
courant de rendement;
}
}
Fonctionnalités avancées du générateur
Le générateur peut calculer la valeur de rendement de rendement en fonction des exigences, ce qui le rend représente les exigences de calcul de séquence précédemment coûteuses, même la séquence infinie indiquée ci-dessus.
En plus de la méthode suivante (), l'objet générateur-itérateur a également une méthode Send (), qui peut modifier l'état interne du générateur. La valeur réalisée pour envoyer () sera traitée comme le résultat de la dernière expression de rendement et le générateur sera interrompu. Avant de passer une valeur spécifiée à l'aide de la méthode Send (), vous devez appeler Suivant () au moins une fois pour démarrer le générateur.
Le générateur Fibonacci suivant utilise la méthode Send () pour redémarrer la séquence:
La copie de code est la suivante:
fonction fibonacci () {
var fn1 = 1;
var fn2 = 1;
tandis que (1) {
Var Current = Fn2;
fn2 = fn1;
fn1 = fn1 + courant;
var reset = courant de rendement;
if (réinitialiser) {
fn1 = 1;
fn2 = 1;
}
}
}
Var Sequence = fibonacci ();
print (Sequence.Next ()); // 1
print (Sequence.Next ()); // 1
print (Sequence.Next ()); // 2
print (Sequence.Next ()); // 3
print (Sequence.Next ()); // 5
print (Sequence.Next ()); // 8
print (Sequence.Next ()); // 13
print (Sequence.Send (true)); // 1
print (Sequence.Next ()); // 1
print (Sequence.Next ()); // 2
print (Sequence.Next ()); // 3
Remarque: Fait intéressant, appeler Send (Undefined) est exactement le même que appeler Next (). Cependant, lorsque la méthode Send () est appelée pour démarrer un nouveau générateur, une exception TypeError sera lancée sauf non définie.
Vous pouvez appeler la méthode de lancer et passer une valeur aberrante qu'elle devrait lancer pour forcer le générateur à lancer une exception. Cette exception sera jetée à partir du contexte actuel et a interrompu le générateur, similaire à l'exécution actuelle du rendement, mais remplacée par une instruction de valeur de lancer.
Si le rendement n'est pas rencontré pendant le processus de lancement de l'exception, l'exception sera transmise jusqu'à ce que la méthode Throw () soit appelée, et ensuite appeler Next () entraînera le lancement de l'exception de l'arrêt.
Le générateur a une méthode étroite () pour forcer le générateur à se terminer. Fin un générateur aura les effets suivants:
1. Toutes les phrases enfin valides dans le générateur seront exécutées
2. Si le mot final lance une exception, sauf l'arrêt, l'exception sera transmise à l'appelant de la méthode close ().
3. Le générateur se terminera
Expressions de générateurs
Un inconvénient évident de la dérivation du tableau est qu'ils provoquent la construction de l'ensemble du réseau en mémoire. La surcharge de l'entrée à la dérivation est insignifiante lorsque sa surcharge est un petit tableau lui-même - cependant, des problèmes peuvent survenir lorsque le réseau d'entrée est grand ou lors de la création d'un nouveau générateur de tableau coûteux (ou infini).
Le générateur permet à l'informatique paresseuse de calculer les éléments au besoin en cas de besoin. Les expressions de générateurs sont syntaxiquement presque les mêmes que la dérivation du tableau - il utilise des parenthèses au lieu de crochets (et des utilisations pour ... en plutôt que pour chaque ... in) - mais il crée un générateur au lieu d'un tableau afin que le calcul puisse être retardé. Vous pouvez le considérer comme une brève syntaxe pour créer un générateur.
Supposons que nous ayons un itérateur pour itérer une énorme séquence d'entiers. Nous devons créer un nouvel itérateur pour itérer les nombres uniformes. Une dérivation du tableau créera un tableau entier contenant tous les nombres uniformes en mémoire:
La copie de code est la suivante:
var doubles = [i * 2 pour (i dedans)];
L'expression du générateur créera un nouvel itérateur et calculera la valeur pair si nécessaire en cas de besoin:
La copie de code est la suivante:
var it2 = (i * 2 pour (i dedans));
print (it2.next ()); // le premier numéro pair dedans
print (it2.next ()); // le deuxième nombre pair dedans
Lorsqu'un générateur est utilisé comme paramètre d'une fonction, les parenthèses sont utilisées comme appel de fonction, ce qui signifie que les parenthèses les plus externes peuvent être omises:
La copie de code est la suivante:
var result = dosomething (i * 2 pour (i dedans));
Fin.