Verständnis von Iterator, Generator?

Iterator

Iterator ist der einfachste und am besten verständliche.

Einfach ausgedrückt werden unsere häufig verwendeten for-of-Schleifen durch Aufrufen einer speziellen Funktion Iterator des Schleifenobjekts implementiert. Diese Funktion war jedoch zuvor verborgen und wir konnten nicht darauf zugreifen. Nachdem wir sie von Symbol eingeführt haben, können wir Symbol.iterator zum Lesen von und verwenden Schreiben Sie diese spezielle Funktion direkt.

Bei der Schleifenanweisung ist es ihm egal, um welches Objekt es sich in der Schleife handelt. Er ist nur für den Aufruf der Funktion data[Symbol.iterator] und die anschließende Schleife entsprechend dem Rückgabewert verantwortlich. Daher kann jedes Objekt in einer Schleife ausgeführt werden, solange es eine Standard-Iterator-Schnittstelle bereitstellt. Lassen Sie uns jetzt beispielsweise benutzerdefinierte Daten erstellen:

var students = {
    
    }
students[Symbol.iterator] = function() {
    
    
  let index = 1;
  return {
    
     next() {
    
    
    return {
    
    done: index>100, value: index++} }
  }
}

for(var i of students) {
    
     console.log(i); }

Zusätzlich zu dieser Methode können wir über Generator auch eine Iterator-Schnittstelle implementieren.

Grundlegende Syntax des Generators

Generator ist eine neue Syntax, die von ES6 eingeführt wurde. Generator ist eine Funktion, die die Ausführung anhalten und fortsetzen kann. Zur einfachen Verwendung kann es als Iterator verwendet werden, um einige Durchlaufoperationen durchzuführen. Für eine komplexere Verwendung kann es einen Zustand intern speichern und zu einer Zustandsmaschine werden.

Die grundlegende Syntax des Generators besteht aus zwei Teilen:

Fügen Sie vor dem Funktionsnamen ein Sternchen hinzu.
Verwenden Sie yield das Schlüsselwort „return value“ innerhalb der Funktion
. Hier ist ein einfaches Beispiel:

function * count() {
    
    
  yield 1
  yield 2
  return 3
}
var c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }

Da Generator auch über eine Symbol.iterator-Schnittstelle verfügt, kann er auch von einer for-Schleife aufgerufen werden:

function * count() {
    
    
  yield 1
  yield 2
  return 3
}
var c = count()
for (i of c) console.log(i) // 1, 2

Allerdings gibt es hier einen Unterschied: Wenn Sie next aufrufen, können Sie 3 erhalten, aber wenn Sie for verwenden, wird die letzte Return-Anweisung ignoriert. Das heißt, die for-Schleife ignoriert die return-Anweisung im Generator.

Darüber hinaus kann die yeild*-Syntax verwendet werden, um einen anderen Generator in einem Generator aufzurufen, siehe yield* MDN

Generator VS Iterator

Der Generator kann als flexiblerer Iterator angesehen werden und kann sich gegenseitig ersetzen. Da der Generator jedoch jederzeit über Yield angehalten werden kann, ist er für die Prozesssteuerung und Statusverwaltung sehr praktisch, während der Iterator möglicherweise mehr schreiben muss Code macht das Gleiche:

Zum Beispiel dieser Inorder-Traversal-Code auf Stack Overflow:

function* traverseTree(node) {
    
    
    if (node == null) return;
    yield* traverseTree(node.left);
    yield node.value;
    yield* traverseTree(node.right);
}

Die Implementierung derselben Funktion mit einem Iterator wird viel schwieriger.

Der Generator ist auch die beste Wahl für die Implementierung einfacher Zustandsautomaten, da er innerhalb der Funktion nachgibt, sodass der aktuelle Zustand nicht verloren geht:

function * clock () {
    
    
  yield 'tick'
  yield 'tock'
}

Wenn es sich bei derselben Funktion um eine gewöhnliche Funktion handelt, kann der Status nicht innerhalb der Funktion gespeichert werden, da diese Funktion jedes Mal aufgerufen wird. Daher muss eine Variable außerhalb der Funktion verwendet werden, um den aktuellen Status zu speichern:

let tick = false
function clock() {
    
    
  tick = !tick
  return tick ? 'tick' : 'tock'
}

Tatsächlich verwendet Babel beim Kompilieren des Generators auch einen Kontext, um den aktuellen Status zu speichern. Sie können sich den von Babel kompilierten Code ansehen, in dem _context der aktuelle Status ist. Hier wird der Wert von _context.next verwendet, um zu steuern, wann Als nächstes aufrufen sollte sein: Welcher Prozess soll eingegeben werden:


var _marked = /*#__PURE__*/regeneratorRuntime.mark(clock);

function clock() {
    
    
  return regeneratorRuntime.wrap(function clock$(_context) {
    
    
    while (1) {
    
    
      switch (_context.prev = _context.next) {
    
    
        case 0:
          _context.next = 2;
          return 'tick';

        case 2:
          _context.next = 4;
          return 'tock';

        case 4:
        case 'end':
          return _context.stop();
      }
    }
  }, _marked, this);
}

Zusammenfassen

  • Iterator ist eine Schleifenschnittstelle. Alle Daten, die diese Schnittstelle implementieren, können von der for- oder Schleife durchlaufen werden
  • Generator ist eine Funktion, die die Ausführung anhalten und fortsetzen kann. Sie kann die Funktion von Iterator vollständig realisieren und eignet sich sehr gut für die Implementierung einer einfachen Zustandsmaschine, da sie den Kontext speichern kann. Darüber hinaus ist es durch die Zusammenarbeit einiger Prozesssteuerungscodes einfacher, asynchrone Vorgänge durchzuführen.
  • Async/Await ist syntaktischer Zucker für Generatoren zur Durchführung asynchroner Vorgänge. Und dieser syntaktische Zucker wird am häufigsten verwendet, beispielsweise der berühmte Koa

Guess you like

Origin blog.csdn.net/hyqhyqhyqq/article/details/130708321