Dieser Artikel vermittelt Ihnen relevantes Wissen über JavaScript. Er stellt hauptsächlich den JavaScript-Front-End-Generator und -Generator vor. Ich hoffe, dass er hilfreich ist.
Front-End-Einstieg (vue) zum Mastery-Kurs: Einstieg ins Lernen
Iterator bietet einen einheitlichen Schnittstellenmechanismus, um einen einheitlichen Zugriffsmechanismus für verschiedene Datenstrukturen bereitzustellen.
Die Definition von Iterator besteht darin, einem Objekt eine next()-Methode bereitzustellen. Bei jedem Aufruf von next() wird ein Ergebnisobjekt zurückgegeben. Das Ergebnisobjekt verfügt über zwei Attribute: „Value“ stellt den aktuellen Wert dar und „Done“ gibt an, ob die Durchquerung abgeschlossen ist .
Funktion makeIterator(Array){ sei Index = 0; zurückkehren { weiter: function(){ zurückkehren ( Array.length > index ? {Wert: Array[index++]}: {erledigt: wahr} ) } } } let iterator = makeIterator(['1','2']) console.log(iterator.next()); // {Wert: '1'} console.log(iterator.next()); // {Wert: '2'} console.log(iterator.next()); // {done: true}
Die Rolle des Iterators:
Bereitstellung einer einheitlichen und einfachen Zugriffsschnittstelle für verschiedene Datenstrukturen;
Ermöglicht die Anordnung der Mitglieder einer Datenstruktur in einer bestimmten Reihenfolge;
zum Verzehr von für...von
ES6 stellt die for of-Anweisung zum Durchlaufen des Iteratorobjekts bereit. Wir werden die for of-Anweisung verwenden, um den oben erstellten Iterator zu durchlaufen:
let iterator = makeIterator(['1','2']) for (Wert des Iterators) { console.log(Wert); } // Iterator ist nicht iterierbar
Das Ergebnis ist eine Fehlermeldung, die besagt, dass der Iterator nicht iterierbar ist. ES6 schreibt vor, dass die Standard-Iterator-Schnittstelle in der Eigenschaft Symbol.iterator der Datenstruktur bereitgestellt wird. Wenn eine Datenstruktur über die Eigenschaft Symbol.iterator verfügt, kann die Datenstruktur durchlaufen werden.
Wir transformieren den benutzerdefinierten makeIterator wie folgt:
const MakeIterator = (Array) => ({ [Symbol.iterator](){ sei Index = 0; zurückkehren { nächste(){ let length = Array.length; if(index < Länge){ Rückgabe {Wert: Array[index++]} }anders{ return {done: true} } } } } }) for(let value of MakeIterator([1,2])){ console.log(Wert) } // 1 // 2
Wir fügen MakeIterator eine Return-Methode hinzu. Wenn die for...of-Schleife vorzeitig beendet wird (normalerweise aufgrund eines Fehlers oder einer Break-Anweisung), wird die Return()-Methode aufgerufen, um den Durchlauf zu beenden.
Basierend auf dieser Funktion können wir, wenn ein Objekt vor Abschluss des Durchlaufs Ressourcen bereinigen oder freigeben muss, die return()-Methode bereitstellen und das Schließen der Datei einschließen, wenn das Lesen der Datei fehlschlägt.
const MakeIterator = (Array) => ({ [Symbol.iterator](){ sei Index = 0; zurückkehren { nächste(){ let length = Array.length; if(index < Länge){ Rückgabe {Wert: Array[index++]} }anders{ return {done: true} } }, zurückkehren(){ return {done: true} } } } }) for(let value of MakeIterator([1, 2, 3])){ console.log(Wert) // 1 // Methode 1 brechen; // Methode 2 // throw new Error('error'); }
Array
Satz
Karte
Array-ähnliche Objekte, z. B. Argumentobjekte, DOM-NodeList-Objekte, typisierteArray-Objekte
// Argumente Objektfunktion sum(){ for(Wert der Argumente festlegen){ console.log(Wert) } } Summe(1,2) // 1 // 2 // typedArray-Objekt let typeArry = new Int8Array(2); typeArry[0] = 1; typeArry[1] = 2; for(let value of typeArry){ console.log(Wert) } // 1 // 2
Generatorobjekt
Funktion*gen(){ Ausbeute 1; Ausbeute 2; } for(let value of gen()){ console.log(Wert) }
Zeichenfolge
F: Warum verfügt Object nicht über einen nativen Iterator?
A: Der Grund, warum Object die Iterator-Schnittstelle nicht standardmäßig bereitstellt, liegt darin, dass unklar ist, welche Eigenschaft des Objekts zuerst und welche Eigenschaft später durchlaufen wird.
Im Wesentlichen ist der Traverser ein linearer Prozess. Für jede nichtlineare Datenstruktur entspricht die Bereitstellung der Traverser-Schnittstelle der Bereitstellung einer linearen Transformation.
Streng genommen ist die Objektbereitstellungs-Traverser-Schnittstelle jedoch nicht erforderlich, da das Objekt zu diesem Zeitpunkt tatsächlich als Kartenstruktur verwendet wird. ES5 verfügt nicht über eine Kartenstruktur, ES6 stellt sie jedoch nativ bereit.
Destrukturierender Auftrag
let set = new Set().add('a').add('b').add('c'); let [x,y] = set; // x='a';
Spread-Operator
var str = 'Hallo'; [...str] // ['h','e','l','l','o']
Der Spread-Operator ruft die Iterator-Schnittstelle auf, sodass Object die Iterator-Schnittstelle nicht bereitstellt. Warum kann also der ...-Operator verwendet werden?
Grund: Es gibt zwei Arten von Spread-Operatoren
Einer wird im Fall von Funktionsparametern und Array-Erweiterungen verwendet. In diesem Fall muss das Objekt iterierbar (iterierbar) sein.
Die andere dient der Objekterweiterung, also der Form {...obj}. In diesem Fall muss das Objekt aufzählbar (aufzählbar) sein.
sei obj1 = { Name: 'qianxun' } sei obj2 = { Alter: 3 } // Das Array-Objekt ist aufzählbar let obj = {...obj1, ...obj2} console.log(obj) //{Name: 'qianxun', Alter: 3} // Gewöhnliche Objekte sind standardmäßig nicht iterierbar let obj = [...obj1, ...obj2] console.log(obj) // Objekt ist nicht iterierbar
Funktion forOf(obj, cb){ let iteratorValue = obj[Symbol.iterator](); let result = iteratorValue.next() while(!result.done){ cb(Ergebniswert) result = iteratorValue.next() } } forOf([1,2,3], (Wert)=>{ console.log(Wert) }) // 1 // 2 // 3
Konzeptionell
Die Generatorfunktion ist eine von ES6 bereitgestellte asynchrone Programmierlösung. Die Generatorfunktion ist eine Zustandsmaschine, die mehrere interne Zustände kapselt;
Die Generatorfunktion ist auch eine Funktion zur Generierung von Traverserobjekten, die nach der Ausführung ein Traverserobjekt zurückgibt.
formell
1. Zwischen dem Funktionsschlüsselwort und dem Funktionsnamen steht ein Sternchen.
2. Ertragsausdrücke werden innerhalb des Funktionskörpers verwendet, um verschiedene interne Zustände zu definieren.
Funktion* simpleGenerator(){ Ausbeute 1; Ausbeute 2; } simpleGenerator()
Wie oben haben wir einen einfachen Generator erstellt und ihn mit zwei Fragen untersucht:
Was passiert, nachdem die Generatorfunktion ausgeführt wurde?
Was bewirkt der Yield-Ausdruck in der Funktion?
Funktion* simpleGenerator(){ console.log('Hallo Welt'); Ausbeute 1; Ausbeute 2; } let generator = simpleGenerator(); // simpleGenerator {<suspended}} console.log(generator.next()) // Hallo Welt // {Wert: 1, erledigt: falsch} console.log(generator.next()) // {Wert: 2, erledigt: falsch}
Die Generatorfunktion gibt nach der Ausführung ein Generatorobjekt zurück, während die normale Funktion den Code innerhalb der Funktion jedes Mal direkt ausführt, wenn die nächste Methode des Generatorobjekts aufgerufen wird. Die Funktion wird ausgeführt, bis das nächste yield-Schlüsselwort die Ausführung stoppt ein {value: Value, done: Boolean}-Objekt.
Der Yield-Ausdruck selbst hat keinen Rückgabewert oder gibt immer undefiniert zurück. Die nächste Methode kann einen Parameter annehmen, der als Rückgabewert des vorherigen Ertragsausdrucks behandelt wird. Durch die Parameter der nächsten Methode können in verschiedenen Phasen der Generatorfunktion unterschiedliche Werte von außen nach innen injiziert werden, um das Funktionsverhalten anzupassen. Da die Parameter der nächsten Methode den Rückgabewert des vorherigen Ertragsausdrucks darstellen, ist die Übergabe von Parametern bei der ersten Verwendung der nächsten Methode ungültig.
Funktion sum(x){ Rückgabefunktion(y){ gib x + y zurück; } } console.log(sum(1)(2)) // Nächsten Parameter verwenden, um Funktion neu zu schreiben* sum(x){ sei y = ergibt x; while(true){ y = Ertrag x + y; } } sei gen = sum(2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
Die Rolle des Yield-Ausdrucks: Definieren des internen Status und Anhalten der Ausführung. Der Unterschied zwischen Yield-Ausdruck und Return-Anweisung
Der Yield-Ausdruck bedeutet, dass die Funktion die Ausführung pausiert und beim nächsten Mal von dieser Position aus rückwärts ausgeführt wird, während die Return-Anweisung nicht die Funktion des Positionsspeichers hat.
In einer Funktion kann nur eine Return-Anweisung ausgeführt werden, es können jedoch mehrere Yield-Ausdrücke ausgeführt werden.
Jede Funktion kann die Return-Anweisung verwenden. Der Yield-Ausdruck kann nur in der Generator-Funktion verwendet werden. Bei Verwendung an anderer Stelle wird ein Fehler gemeldet.
Wenn der Yield-Ausdruck an der Operation beteiligt ist, setzen Sie ihn in Klammern; wenn er als Funktionsparameter verwendet oder auf der rechten Seite des Zuweisungsausdrucks platziert wird, dürfen die Klammern nicht enthalten sein.
Funktion *gen () { console.log('hello' + yield) × console.log('hello' + (yield)) √ console.log('hello' + yield 1) × console.log('hello' + (rendite 1)) √ foo(Ertrag 1) √ const param = Ausbeute 2 √ }
Basierend auf der Tatsache, dass die Generatorfunktion Generator mehrere Erträge unterstützen kann, können wir ein Szenario implementieren, in dem eine Funktion mehrere Rückgabewerte hat:
Funktion* gen(num1, num2){ ertrag num1 + num2; Ausbeute num1 - num2; } sei res = gen(2, 1); console.log(res.next()) // {Wert: 3, erledigt: false} console.log(res.next()) // {Wert: 1, erledigt: false}
Da es sich bei der Generatorfunktion um die Iterator-Generierungsfunktion handelt, kann der Generator der Symbol.iterator-Eigenschaft des Objekts zugewiesen werden, sodass das Objekt über die Iterator-Schnittstelle verfügt. Der Implementierungscode des Generators ist prägnanter.
sei obj = { Name: 'qianxun', Alter: 3, [Symbol.iterator]: function(){ lass das = dies; letkeys = Object.keys(that) sei Index = 0; zurückkehren { weiter: function(){ Index <keys.length zurückgeben? {Wert: that[keys[index++]], done: false}: {Wert: undefiniert, erledigt: wahr} } } } } for(let value of obj){ console.log(Wert) }
Generator:
sei obj = { Name: 'qianxun', Alter: 3, [Symbol.iterator]: function* (){ letkeys = Object.keys(this) for(let i=0; i< keys.length; i++){ yield this[keys[i]]; } } } for(let value of obj){ console.log(Wert) }
Die
return()
Methode kann den angegebenen Wert zurückgeben und die Traversal-Generator-Funktion beenden.
function*gen() { Ausbeute 1; Ausbeute 2; Ausbeute 3; } var g = gen(); g.next() // { value: 1, done: false } // Wenn beim Aufruf der Methode return() keine Parameter angegeben werden, ist das Wertattribut des Rückgabewerts undefiniert g.return('foo') // { value: "foo", done: true } g.next() // { Wert: undefiniert, erledigt: wahr }
Wenn es try...finally
-Codeblock in der Generator-Funktion gibt und der try
-Codeblock ausgeführt wird, bewirkt return()
-Methode, dass finally
Codeblock sofort eingegeben wird. Nach der Ausführung wird die gesamte Funktion beendet.
Funktion* Zahlen () { Ausbeute 1; versuchen { Ausbeute 2; Ausbeute 3; } Endlich { Ausbeute 4; Ausbeute 5; } Ausbeute 6; } var g = Zahlen(); g.next() // { value: 1, done: false } g.next() // { value: 2, done: false } g.return(7) // { value: 4, done: false } g.next() // { Wert: 5, erledigt: false } g.next() // { value: 7, done: true }
Wenn Sie eine andere Generatorfunktion innerhalb der Generatorfunktion aufrufen möchten. Wir müssen den Durchlauf innerhalb des Funktionskörpers des ersteren manuell abschließen. Wenn der Funktionsaufruf auf mehreren Ebenen verschachtelt ist, ist das Schreiben umständlich und schwierig zu lesen. ES6 bietet Yield*-Ausdrücke als Lösung.
An andere Generatoren delegieren
Funktion* g1() { Ausbeute 2; Ausbeute 3; } Funktion* g2() { Ausbeute 1; yield* g1(); Ausbeute 4; } const iterator = g2(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 4, done: false } console.log(iterator.next()); // { Wert: undefiniert, erledigt: true }
An andere iterierbare Objekte delegieren
Funktion*gen(){ Ertrag* [1,2,3] } console.log(gen().next()) // {Wert: 1, erledigt: false}
Die Generator-Funktion gibt einen Traverser zurück. ES6 legt fest, dass dieser Traverser eine Instanz der Generator-Funktion ist und die Methoden des Generator.prototype-Objekts erbt, die Eigenschaften dafür jedoch nicht abrufen kann, da es sich zu diesem Zeitpunkt um das globale Objekt und nicht um die Instanz handelt Objekt.
Funktion*gen(){ this.a = 1 } gen.prototype.say = function(){ console.log('hi') } sei obj = gen() console.log(obj Instanz von Gen) // wahr obj.say() // Hallo obj.next() console.log(obj.a) //undefiniert
Wenn Sie wie ein Konstruktor auf Instanzeigenschaften zugreifen möchten, können Sie dies ändern, um es an Generator.prototype zu binden.
Funktion*gen(){ this.a = 1 } gen.prototype.say = function(){ console.log('hi') } sei obj = gen.call(gen.prototype) console.log(obj Instanz von Gen) // wahr obj.say() // Hallo obj.next() console.log(obj.a) //1
Funktion* StateMachine(state){ Übergang lassen; while(true){ if(transition === "INCREMENT"){ Zustand++; }else if(transition === "DECREMENT"){ Zustand--; } Übergang = Ertragszustand; } } const iterator = StateMachine(0); console.log(iterator.next()); // 0 console.log(iterator.next('INCREMENT')); // 1 console.log(iterator.next('DECREMENT')); // 0