Cet article vous apporte des connaissances pertinentes sur JavaScript. Il présente principalement l'itérateur et le générateur frontal JavaScript. Les amis dans le besoin peuvent s'y référer.
Entrée front-end (vue) au cours de maîtrise : entrez dans l'apprentissage
Iterator fournit un mécanisme d'interface unifié pour fournir un mécanisme d'accès unifié pour diverses structures de données différentes.
Définir Iterator consiste à fournir à un objet une méthode next(). Chaque fois que next() est appelé, un objet résultat sera renvoyé. L'objet résultat a deux attributs, la valeur représente la valeur actuelle et done indique si le parcours est terminé. .
fonction makeIterator(Array){ soit l'indice = 0 ; retour { suivant : fonction(){ retour ( Tableau.longueur > index ? {valeur : Tableau[index++]} : {fait : vrai} ) } } } laisser itérateur = makeIterator(['1','2']) console.log(iterator.next()); // {valeur : '1'} console.log(iterator.next()); // {valeur : '2'} console.log(iterator.next()); // {fait : vrai}
Le rôle de l'itérateur :
Fournir une interface d'accès unifiée et simple pour diverses structures de données ;
Permet aux membres d'une structure de données d'être organisés dans un certain ordre ;
pour consommation par pour...de
ES6 fournit l'instruction for of pour parcourir l'objet itérateur. Nous utiliserons l'instruction for of pour parcourir l'itérateur créé ci-dessus :
laisser itérateur = makeIterator(['1','2']) for (laisser la valeur de l'itérateur) { console.log(valeur); } // l'itérateur n'est pas itérable
Le résultat est une erreur indiquant que l'itérateur n'est pas itérable. Pourquoi ? ES6 stipule que l'interface Iterator par défaut est déployée dans la propriété Symbol.iterator de la structure de données. Si une structure de données a la propriété Symbol.iterator, la structure de données peut être parcourue.
Nous transformons le makeIterator personnalisé comme suit :
const MakeIterator = (Array) => ({ [Symbol.iterator](){ soit l'indice = 0 ; retour { suivant(){ laissez longueur = Array.length; si(index < longueur){ renvoyer {valeur : Array[index++]} }autre{ retourner {fait : vrai} } } } } }) for(laissez la valeur de MakeIterator([1,2])){ console.log(valeur) } // 1 // 2
Nous ajoutons une méthode return à MakeIterator. Si la boucle for...of se termine prématurément (généralement à cause d'une erreur ou d'une instruction break), la méthode return() sera appelée pour terminer le parcours.
Sur la base de cette fonctionnalité, si un objet doit nettoyer ou libérer des ressources avant de terminer le parcours, nous pouvons déployer la méthode return() et inclure la fermeture du fichier lorsque la lecture du fichier échoue.
const MakeIterator = (Array) => ({ [Symbol.iterator](){ soit l'indice = 0 ; retour { suivant(){ laissez longueur = Array.length; si(index < longueur){ renvoyer {valeur : Array[index++]} }autre{ retourner {fait : vrai} } }, retour(){ retourner {fait : vrai} } } } }) for(laissez la valeur de MakeIterator([1, 2, 3])){ console.log(valeur) // 1 // Méthode 1 casser; // Méthode 2 // lance une nouvelle erreur ('erreur'); }
tableau
Ensemble
Carte
Objets de type tableau, tels que les objets arguments, les objets DOM NodeList, les objets typedArray
// arguments fonction objet sum(){ pour(laisser la valeur des arguments){ console.log(valeur) } } somme(1,2) // 1 // 2 // objet typedArray let typeArry = new Int8Array(2); typeArry[0] = 1; typeArry[1] = 2; for(laisser la valeur de typeArry){ console.log(valeur) } // 1 // 2
Objet générateur
fonction*gen(){ rendement 1 ; rendement 2 ; } for(laisser la valeur de gen()){ console.log(valeur) }
Chaîne
Q : Pourquoi Object n'a-t-il pas d'itérateur natif ?
R : La raison pour laquelle Object ne déploie pas l'interface Iterator par défaut est qu'il n'est pas certain quelle propriété de l'objet est traversée en premier et quelle propriété est traversée plus tard.
Essentiellement, le traverseur est un processus linéaire. Pour toute structure de données non linéaire, déployer l'interface du traverseur équivaut à déployer une transformation linéaire.
Cependant, à proprement parler, l'interface de déploiement d'objets n'est pas nécessaire, car à l'heure actuelle, l'objet est en fait utilisé comme structure Map. ES5 n'a pas de structure Map, mais ES6 la fournit nativement.
Mission de déstructuration
let set = new Set().add('a').add('b').add('c'); soit [x,y] = ensemble ; // x='a'; y='b'
opérateur de propagation
var str = 'bonjour'; [...str] // ['h','e','l','l','o']
L'opérateur spread appelle l'interface Iterator, donc Object ne déploie pas l'interface Iterator, alors pourquoi l'opérateur... peut-il être utilisé ?
Raison : Il existe deux types d’opérateurs de spread
L'un est utilisé dans le cas des paramètres de fonction et de l'expansion de tableaux. Dans ce cas, l'objet doit être itérable (itérable).
L'autre est destiné à l'expansion d'objet, c'est-à-dire la forme {...obj}. Dans ce cas, l'objet doit être énumérable (énumérable).
soit obj1 = { nom : 'qianxun' } soit obj2 = { âge: 3 } // L'objet tableau est énumérable let obj = {...obj1, ...obj2} console.log(obj) //{nom : 'qianxun', âge : 3} // Les objets ordinaires ne sont pas itérables par défaut let obj = [...obj1, ...obj2] console.log(obj) // l'objet n'est pas itérable
fonction forOf(obj, cb){ laissez iteratorValue = obj[Symbol.iterator](); soit le résultat = iteratorValue.next() pendant que(!result.done){ cb(résultat.valeur) résultat = iteratorValue.next() } } forOf([1,2,3], (valeur)=>{ console.log(valeur) }) // 1 // 2 // 3
Conceptuellement
La fonction Générateur est une solution de programmation asynchrone fournie par ES6. La fonction Générateur est une machine à états qui encapsule plusieurs états internes ;
La fonction Générateur est également une fonction de génération d'objet traverseur, qui renvoie un objet traverseur après exécution.
officiel
1. Il y a un astérisque entre le mot-clé de fonction et le nom de la fonction ;
2. Les expressions de rendement sont utilisées dans le corps de la fonction pour définir différents états internes.
fonction* simpleGenerator(){ rendement 1 ; rendement 2 ; } simpleGénérateur()
Comme ci-dessus, nous avons créé un générateur simple et nous l'avons exploré avec deux questions :
Que se passe-t-il une fois la fonction Générateur exécutée ?
À quoi sert l’expression de rendement dans la fonction ?
fonction* simpleGenerator(){ console.log('bonjour tout le monde'); rendement 1 ; rendement 2 ; } laissez générateur = simpleGenerator(); // simpleGenerator {<suspendu}} console.log(generator.next()) // Bonjour le monde // {valeur : 1, terminé : faux} console.log(generator.next()) // {valeur : 2, terminé : faux}
La fonction générateur Generator renvoie un objet générateur après son exécution, tandis que la fonction ordinaire exécutera directement le code à l'intérieur de la fonction à chaque fois que la méthode suivante de l'objet générateur est appelée, la fonction sera exécutée jusqu'à ce que le prochain mot-clé rendement arrête l'exécution, et un objet {value : Value, done : Boolean}.
L'expression de rendement elle-même n'a pas de valeur de retour ou renvoie toujours undéfini. La méthode suivante peut prendre un paramètre, qui sera traité comme la valeur de retour de l'expression de rendement précédente. Grâce aux paramètres de la méthode suivante, différentes valeurs peuvent être injectées de l'extérieur vers l'intérieur à différentes étapes de la fonction Générateur pour ajuster le comportement de la fonction. Étant donné que les paramètres de la méthode suivante représentent la valeur de retour de l’expression de rendement précédente, la transmission des paramètres n’est pas valide la première fois que la méthode suivante est utilisée.
fonction somme(x){ fonction de retour (y) { renvoyer x + y ; } } console.log(somme(1)(2)) // Utilisez le paramètre suivant pour réécrire la fonction* sum(x){ soit y = donne x ; tandis que(vrai){ y = rendement x + y ; } } soit gen = somme (2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
Le rôle de l'expression de rendement : définir l'état interne et suspendre l'exécution. La différence entre l'expression de rendement et l'instruction de retour.
L'expression rendement signifie que la fonction suspend l'exécution et continue de s'exécuter en arrière à partir de cette position la prochaine fois, tandis que l'instruction return n'a pas la fonction de mémoire de position.
Dans une fonction, une seule instruction return peut être exécutée, mais plusieurs expressions de rendement peuvent être exécutées.
N'importe quelle fonction peut utiliser l'instruction return. L'expression rendement ne peut être utilisée que dans la fonction Générateur. Si elle est utilisée ailleurs, une erreur sera signalée.
Si l'expression de rendement participe à l'opération, mettez-la entre parenthèses ; si elle est utilisée comme paramètre de fonction ou placée à droite de l'expression d'affectation, les parenthèses peuvent ne pas être incluses.
fonction *gen () { console.log('bonjour' + rendement) × console.log('bonjour' + (rendement)) √ console.log('bonjour' + rendement 1) × console.log('bonjour' + (rendement 1)) √ foo (rendement 1) √ const param = rendement 2 √ }
Sur la base du fait que la fonction génératrice Generator peut prendre en charge plusieurs rendements, nous pouvons implémenter un scénario dans lequel une fonction a plusieurs valeurs de retour :
fonction* gen(num1, num2){ donne num1 + num2 ; rendement num1 - num2 ; } soit res = gen(2, 1); console.log(res.next()) // {valeur : 3, terminé : false} console.log(res.next()) // {valeur : 1, terminé : false}
Puisque la fonction Generator est la fonction de génération d'itérateur, le Generator peut être affecté à la propriété Symbol.iterator de l'objet, afin que l'objet ait l'interface Iterator. Le code d'implémentation du générateur est plus concis.
soit obj = { nom : 'qianxun', âge: 3, [Symbol.iterator] : function(){ laissez ça = ceci; let key = Object.keys (que) soit l'indice = 0 ; retour { suivant : fonction(){ return index <clés.longueur? {valeur : that[keys[index++]], done : false} : {valeur : non défini, terminé : vrai} } } } } pour(laisser la valeur de obj){ console.log(valeur) }
Générateur:
soit obj = { nom : 'qianxun', âge: 3, [Symbol.iterator] : fonction* (){ laissez les clés = Object.keys (this) pour(soit i=0; i< clés.longueur; i++){ donnez ceci[keys[i]]; } } } pour(laisser la valeur de obj){ console.log(valeur) }
La méthode
return()
peut renvoyer la valeur donnée et terminer la fonction du générateur de traversée.
fonction*gen() { rendement 1 ; rendement 2 ; rendement 3 ; } var g = gen(); g.next() // { valeur : 1, terminé : faux } // Si aucun paramètre n'est fourni lors de l'appel de la méthode return(), l'attribut value de la valeur de retour n'est pas défini g.return('foo') // { valeur : "foo", done : true } g.next() // { valeur : non définie, terminé : vrai }
S'il y a try...finally
dans la fonction Générateur et que le bloc de code try
est en cours d'exécution, alors return()
entraînera finally
. Après l'exécution, la fonction entière se terminera.
fonction* nombres () { rendement 1 ; essayer { rendement 2 ; rendement 3 ; } enfin { rendement 4 ; rendement 5 ; } rendement 6 ; } var g = nombres(); g.next() // { valeur : 1, terminé : faux } g.next() // { valeur : 2, terminé : faux } g.return(7) // { valeur : 4, terminé : false } g.next() // { valeur : 5, terminé : faux } g.next() // { valeur : 7, terminé : vrai }
Si vous souhaitez appeler une autre fonction Generator à l’intérieur de la fonction Generator. Nous devons terminer manuellement le parcours dans le corps de la fonction du premier. Si l'appel de fonction est imbriqué à plusieurs niveaux, l'écriture sera lourde et difficile à lire. ES6 fournit des expressions rendement* comme solution.
Déléguer à d'autres générateurs
fonction* g1() { rendement 2 ; rendement 3 ; } fonction* g2() { rendement 1 ; rendement* g1(); rendement 4 ; } itérateur const = g2(); console.log(iterator.next()); // { valeur : 1, terminé : false } console.log(iterator.next()); // { valeur : 2, terminé : false } console.log(iterator.next()); // { valeur : 3, terminé : false } console.log(iterator.next()); // { valeur : 4, terminé : false } console.log(iterator.next()); // { valeur : non définie, terminé : vrai }
Déléguer à d'autres objets itérables
fonction*gen(){ rendement* [1,2,3] } console.log(gen().next()) // {valeur : 1, terminé : false}
La fonction Generator renvoie un traverseur. ES6 stipule que ce traverseur est une instance de la fonction Generator et hérite des méthodes de l'objet Generator.prototype, mais ne peut pas obtenir les propriétés de celui-ci car il s'agit de l'objet global à ce moment, pas de l'instance. objet.
fonction*gen(){ ceci.a = 1 } gen.prototype.say = fonction(){ console.log('salut') } laissez obj = gen() console.log (obj instanceof gen) // vrai obj.say() // salut obj.suivant() console.log(obj.a) //non défini
Si vous souhaitez accéder aux propriétés de l'instance comme un constructeur, vous pouvez modifier ceci pour le lier à Generator.prototype.
fonction*gen(){ ceci.a = 1 } gen.prototype.say = fonction(){ console.log('salut') } laissez obj = gen.call(gen.prototype) console.log (obj instanceof gen) // vrai obj.say() // salut obj.suivant() console.log(obj.a) //1
fonction* StateMachine(état){ laisser la transition; tandis que(vrai){ if(transition === "INCREMENT"){ état++ ; }else if(transition === "DÉCREMENT"){ État--; } transition = état de rendement ; } } itérateur const = StateMachine(0); console.log(iterator.next()); // 0 console.log(iterator.next('INCREMENT')); // 1 console.log(iterator.next('DÉCRÉMENT')); // 0