One place for hosting & domains

      Generatoren

      Grundlegendes zu Generatoren in JavaScript


      Die Autorin wählte den Open Internet/Free Speech Fund, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Im ECMAScript 2015 wurden Generatoren in die JavaScript-Sprache eingeführt. Ein Generator ist ein Prozess, der angehalten und wieder aufgenommen und mehrere Werte liefern kann. Ein Generator in JavaScript besteht aus einer Generator-Funktion, die ein iterierbares Generator-Objekt zurückgibt.

      Generatoren können den Zustand aufrechterhalten, was eine effiziente Methode zur Erstellung von Iteratoren darstellt. Sie sind in der Lage, mit unendlichen Datenströmen umzugehen, was zur Implementierung von infinitem Scrollen auf dem Frontend einer Webanwendung zum Betrieb mit Schallwellendaten und mehr verwendet werden kann. Außerdem können Generatoren, die mit sogenannten Promises (Versprechen) verwendet werden, die async/await-Funktionalität imitieren, was es uns ermöglicht, mit asynchronem Code auf einer direkteren und lesbaren Weise umzugehen. Obwohl async/await eine häufigere Methode ist, mit gebräuchlichen, einfachen asynchronen Anwendungsfällen wie dem Abrufen von Daten von einer API umzugehen, verfügen Generatoren über fortgeschrittenere Funktionen, die es lohnenswert machen, ihre Verwendung zu erlernen.

      Dieser Artikel behandelt die Erstellung von Generator-Funktionen, die Iterierung über Generator-Objekten, den Unterschied zwischen yield und return innerhalb eines Generators sowie andere Aspekte bezüglich des Arbeitens mit Generatoren.

      Generator-Funktionen

      Eine Generator-Funktion ist eine Funktion, die ein Generator-Objekt zurückgibt. Sie wird definiert durch das Schlüsselwort function gefolgt von einem Asterisk (*), so wie nachstehend gezeigt:

      // Generator function declaration
      function* generatorFunction() {}
      

      Gelegentlich befindet sich der Asterisk neben dem Funktionsnamen, im Gegensatz zum Schlüsselwort, wie z. B. function *generatorFunction(). Das funktioniert genauso, aber function* ist eine allgemein bekanntere Syntax.

      Generator-Funktionen können auch in einer Expression definiert sein, wie reguläre Funktionen:

      // Generator function expression
      const generatorFunction = function*() {}
      

      Generatoren können sogar die Methoden von object und class sein:

      // Generator as the method of an object
      const generatorObj = {
        *generatorMethod() {},
      }
      
      // Generator as the method of a class
      class GeneratorClass {
        *generatorMethod() {}
      }
      

      Die Beispiele in diesem Artikel verwenden die Deklarationssyntax von Generator-Funktionen.

      Anmerkung: Im Gegensatz zu regulären Funktionen können Generatoren weder mit dem Schlüsselwort new konstruiert noch in Verbindung mit Pfeilfunktionen verwendet werden.

      Nachdem Sie nun wissen, wie Sie Generator-Funktionen deklarieren, sehen Sie sich die iterierbaren Generator-Objekte an, die diese zurückgeben.

      Generator-Objekte

      Traditionell laufen Funktionen in JavaScript bis zum Abschluss; das Aufrufen einer Funktion gibt einen Wert zurück, wenn sie beim Schlüsselwort return ankommt. Wenn das Schlüsselwort return ausgelassen wird, wird eine Funktion implizit als undefined zurückgegeben.

      Im folgenden Code z. B. deklarieren wir eine sum()-Funktion, die einen Wert zurückgibt, der die Summe von zwei ganzzahligen Argumenten ist:

      // A regular function that sums two values
      function sum(a, b) {
        return a + b
      }
      

      Das Aufrufen der Funktion gibt einen Wert zurück, der die Summe der Argumente ist:

      const value = sum(5, 6) // 11
      

      Eine Generator-Funktion hingegen gibt nicht sofort einen Wert zurück, sondern ein iterierbares Generator-Objekt. Im folgenden Beispiel deklarieren wir eine Funktion und geben ihr einen einzelnen Rückgabewert, wie einer Standardfunktion:

      // Declare a generator function with a single return value
      function* generatorFunction() {
        return 'Hello, Generator!'
      }
      

      Wenn wir die Generator-Funktion aufrufen, gibt sie das Generator-Objekt zurück, das wir einer Variable zuweisen können:

      // Assign the Generator object to generator
      const generator = generatorFunction()
      

      Wenn es eine reguläre Funktion wäre, würden wir erwarten, dass uns generator die in der Funktion zurückgegebene Zeichenfolge angibt. Was wir jedoch tatsächlich erhalten, ist ein Objekt im Zustand suspended. Das Aufrufen von generator ergibt daher ein Output, das dem Folgenden ähnelt:

      Output

      generatorFunction {<suspended>} __proto__: Generator [[GeneratorLocation]]: VM272:1 [[GeneratorStatus]]: "suspended" [[GeneratorFunction]]: ƒ* generatorFunction() [[GeneratorReceiver]]: Window [[Scopes]]: Scopes[3]

      Das von der Funktion zurückgegebene Generator-Objekt ist ein Iterator. Ein Iterator ist ein Objekt, das über eine next()-Methode verfügt, die dazu dient, durch eine Sequenz von Werten zu iterieren. Die next()-Methode gibt ein Objekt mit value– und done-Eigenschaften zurück. value repräsentiert den zurückgegebenen Wert und done zeigt an, ob der Iterator alle seine Werte durchlaufen hat oder nicht.

      Mit diesem Wissen rufen wir nur next() auf unserem generator auf und erhalten den aktuellen Wert und den Zustand des Iterators:

      // Call the next method on the Generator object
      generator.next()
      

      Dadurch erhalten wir folgendes Output:

      Output

      {value: "Hello, Generator!", done: true}

      Der durch das Aufrufen von next() zurückgegebene Wert ist Hello, Generator! und der Zustand von done ist true, da dieser Wert von einem return stammt, das den Iterator ausgeschlossen hat. Da der Iterator ausgeführt ist, ändert sich der Status der Generator-Funktion von suspended zu closed. Ein erneutes Aufrufen von generator ergibt Folgendes:

      Output

      generatorFunction {<closed>}

      Bisher haben wir nur gezeigt, dass eine Generator-Funktion eine komplexere Methode sein kann, um den return-Wert einer Funktion zu erhalten. Aber Generator-Funktionen verfügen auch über einzigartige Eigenschaften, die sie von normalen Funktionen unterscheiden. Im nächsten Abschnitt erfahren wir mehr über den yield-Operator und sehen, wie ein Generator die Ausführung anhalten und wieder aufnehmen kann.

      yield-Operatoren

      Generatoren implementieren ein neues Schlüsselwort in JavaScript: yield.yield kann eine Generator-Funktion anhalten und den Wert, der auf yield folgt, zurückgeben. Es bietet somit eine einfache Methode, durch Werte zu iterieren.

      In diesem Beispiel halten wir die Generator-Funktion dreimal mit verschiedenen Werten an und geben am Ende einen Wert zurück. Anschließend weisen wir unser Generator-Objekt der generator-Variablen zu.

      // Create a generator function with multiple yields
      function* generatorFunction() {
        yield 'Neo'
        yield 'Morpheus'
        yield 'Trinity'
      
        return 'The Oracle'
      }
      
      const generator = generatorFunction()
      

      Wenn wir nun next() in der Generator-Funktion aufrufen, wird sie jedes Mal anhalten, wenn sie auf yield trifft. done wird auf false gesetzt nach jedem yield, was anzeigt, dass der Generator nicht abgeschlossen hat. Trifft sie auf ein return oder wenn keine yields mehr in der Funktion zu finden sind, wechselt done auf true und der Generator schließt ab.

      Verwenden Sie die next()-Methode viermal in einer Reihe:

      // Call next four times
      generator.next()
      generator.next()
      generator.next()
      generator.next()
      

      Dies ergibt die folgenden vier Zeilen an Output in der Reihenfolge:

      Output

      {value: "Neo", done: false} {value: "Morpheus", done: false} {value: "Trinity", done: false} {value: "The Oracle", done: true}

      Beachten Sie, dass ein Generator kein return benötigt; bei dessen Auslassung gibt die letzte Iteration {value: undefined, done: true} zurück, ebenso wie etwaige weitere Anrufe an next(), nachdem ein Generator abgeschlossen hat.

      Iterierung über einen Generator

      Mithilfe der next()-Methode haben wir manuell über das Generator-Objekt iteriert und dabei alle value– und done-Eigenschaften des kompletten Objekts erhalten. Doch so wie Array, Map und Set folgt ein Generator dem Iterationsprotokoll und kann durch for...of iteriert werden:

      // Iterate over Generator object
      for (const value of generator) {
        console.log(value)
      }
      

      Dadurch erhalten wir:

      Output

      Neo Morpheus Trinity

      Auch der Spread-Operator kann dazu verwendet werden, die Werte eines Generators einem Array zuzuweisen.

      // Create an array from the values of a Generator object
      const values = [...generator]
      
      console.log(values)
      

      Dadurch ergibt sich das folgende Array:

      Output

      (3) ["Neo", "Morpheus", "Trinity"]

      Sowohl Spread als auch for...of beziehen nicht das return in die Werte mit ein (in diesem Fall wäre das Ergebnis 'The Oracle').

      Anmerkung: Während beide Methoden wirksam sind, um mit finiten Generatoren zu arbeiten, ist es nicht möglich, spread oder for...of direkt zu verwenden, ohne eine unendliche Schleife zu erzeugen, wenn ein Generator mit einem infiniten Datenstrom umgeht.

      Schließen eines Generators

      Wie wir sehen, kann in einem Generator beim Iterieren durch seine Werte sein done-Merkmal auf true und sein Zustand auf closed gesetzt sein. Es gibt zwei zusätzliche Möglichkeiten, um einen Generator sofort zu annullieren: mit der return()-Methode und mit der throw()-Methode.

      Mit return() kann der Generator an jedem Punkt beendet werden, so wie es bei einem return-Statement im Funktionskörper der Fall wäre. Sie können ein Argument in return() einfügen oder es für einen undefinierten Wert freihalten.

      Um return() anzuwenden, erstellen wir einen Generator mit einigen yield-Werten, aber ohne return, in der Funktionsdefinition:

      function* generatorFunction() {
        yield 'Neo'
        yield 'Morpheus'
        yield 'Trinity'
      }
      
      const generator = generatorFunction()
      

      Das erste next() ergibt 'Neo', mit done auf false gesetzt. Wenn wir direkt danach eine return()-Methode auf das Generator-Objekt anwenden, erhalten wir nun den übergebenen Wert und done ist auf true gesetzt. Jeder zusätzliche Aufruf an next() ergibt die Standardantwort Generator abgeschlossen, mit einem undefinierten Wert.

      Um dies zu demonstrieren, führen Sie die folgenden drei Methoden auf generator aus:

      generator.next()
      generator.return('There is no spoon!')
      generator.next()
      

      Dadurch ergeben sich diese drei Ergebnisse:

      Output

      {value: "Neo", done: false} {value: "There is no spoon!", done: true} {value: undefined, done: true}

      Die return()-Methode hat das Generator-Objekt zum Abschließen und zum Ignorieren von etwaigen anderen yield-Schlüsselwörtern gezwungen. Dies ist besonders nützlich, wenn Sie bei der asynchronen Programmierung Funktionen annullierbar machen müssen – z. B. das Unterbrechen einer Web-Anfrage, wenn ein Benutzer eine andere Aktion ausführen möchte – da es nicht möglich ist, ein Promise direkt zu annullieren.

      Wenn der Körper einer Generator-Funktion eine Möglichkeit hat, Fehler abzufangen und mit ihnen umzugehen, können Sie die throw()-Methode verwenden, um einen Fehler in den Generator zu injizieren. Das startet den Generator, injiziert den Fehler und beendet den Generator.

      Um dies zu demonstrieren, geben wir ein try...catch in den Körper der Generator-Funktion ein und zeichnen einen Fehler auf, wenn dieser gefunden wird:

      // Define a generator function with a try...catch
      function* generatorFunction() {
        try {
          yield 'Neo'
          yield 'Morpheus'
        } catch (error) {
          console.log(error)
        }
      }
      
      // Invoke the generator and throw an error
      const generator = generatorFunction()
      

      Jetzt führen wir die next()-Methode aus, gefolgt von throw():

      generator.next()
      generator.throw(new Error('Agent Smith!'))
      

      Dadurch erhalten wir folgendes Output:

      Output

      {value: "Neo", done: false} Error: Agent Smith! {value: undefined, done: true}

      Wir haben mit throw() einen Fehler in den Generator injiziert, der vom try...catch aufgefangen und in der Konsole aufgezeichnet wurde.

      Methoden und Zustände von Generator-Objekten

      Die folgende Tabelle zeigt eine Liste von Methoden, die auf Generator-Objekte angewendet werden können:

      Methode Beschreibung
      next() Gibt den nächsten Wert in einem Generator zurück
      return() Gibt einen Wert in einem Generator zurück und beendet den Generator
      throw() Injiziert einen Fehler und beendet den Generator

      Die nächste Tabelle listet die möglichen Zustände eines Generator-Objekts auf:

      Zustand Beschreibung
      suspended Der Generator hat die Ausführung angehalten, ist aber nicht beendet
      closed Der Generator wurde beendet, indem er entweder auf einen Fehler gestoßen, zurückgekehrt oder durch alle Werte iteriert ist

      yield Delegation

      Außer dem regulären yield-Operator können Generatoren auch die yield*-Expression verwenden, um weitere Werte an einen anderen Generator zu delegieren. Wenn yield* in einem Generator auftritt, geht es in den delegierten Generator und beginnt, durch alle yields zu iterieren, bis der Generator beendet ist. Dies kann dazu genutzt werden, verschiedene Generator-Funktionen zu trennen, um Ihren Code semantisch zu organisieren, während alle ihre yields in der richtigen Reihenfolge iterierbar bleiben.

      Um dies zu demonstrieren, können wir zwei Generator-Funktionen erstellen, von denen eine eine yield*-Operation auf die andere ausführt:

      // Generator function that will be delegated to
      function* delegate() {
        yield 3
        yield 4
      }
      
      // Outer generator function
      function* begin() {
        yield 1
        yield 2
        yield* delegate()
      }
      

      Als Nächstes iterieren wir durch die Generator-Funktion begin():

      // Iterate through the outer generator
      const generator = begin()
      
      for (const value of generator) {
        console.log(value)
      }
      

      Dadurch erhalten wir folgende Werte in der Reihenfolge, in der sie generiert werden:

      Output

      1 2 3 4

      Der äußere Generator lieferte die Werte 1 und 2, delegierte dann an den anderen Generator mit yield*, der 3 und 4 zurückgab.

      yield* kann auch an jedes Objekt delegieren, das iterierbar ist, wie z. B. ein Array oder ein Map. Die Yield-Delegation kann bei der Organisation von Code hilfreich sein, da jede Funktion innerhalb eines Generators, die yield verwenden will, auch ein Generator sein muss.

      Infinite Datenströme

      Eine der nützlichen Aspekte von Generatoren ist die Fähigkeit, mit unendlichen Datenströmen und -sammlungen zu arbeiten. Dies kann durch das Erstellen einer Endlosschleife innerhalb einer Generator-Funktion demonstriert werden, die eine Zahl um eins erhöht.

      Im folgenden Code-Block definieren wir diese Generator-Funktion und initiieren dann den Generator:

      // Define a generator function that increments by one
      function* incrementer() {
        let i = 0
      
        while (true) {
          yield i++
        }
      }
      
      // Initiate the generator
      const counter = incrementer()
      

      Iterieren Sie nun durch die Werte mit next():

      // Iterate through the values
      counter.next()
      counter.next()
      counter.next()
      counter.next()
      

      Dadurch erhalten Sie folgendes Output:

      Output

      {value: 0, done: false} {value: 1, done: false} {value: 2, done: false} {value: 3, done: false}

      Die Funktion gibt in der Endlosschleife aufeinanderfolgende Werte zurück, während das done-Merkmal auf false bleibt, wodurch sichergestellt wird, dass der Vorgang nicht beendet wird.

      Mit Generatoren müssen Sie sich nicht um die Erstellung einer Endlosschleife kümmern, da Sie die Ausführung nach Belieben anhalten und wieder aufnehmen können. Sie müssen jedoch immer noch vorsichtig sein, wie Sie den Generator aufrufen. Wenn Sie Spread oder for...of bei einem infiniten Datenstrom anwenden, iterieren Sie plötzlich immer noch über eine Endlosschleife, was zum Absturz der Umgebung führt.

      Für ein komplexeres Beispiel eines infiniten Datenstroms können wir eine Fibonacci-Generator-Funktion erstellen. Die Fibonacci-Sequenz, die die beiden vorherigen Werte kontinuierlich zusammenrechnet, kann mit einer Endlosschleife innerhalb eines Generators wie folgt geschrieben werden:

      // Create a fibonacci generator function
      function* fibonacci() {
        let prev = 0
        let next = 1
      
        yield prev
        yield next
      
        // Add previous and next values and yield them forever
        while (true) {
          const newVal = next + prev
      
          yield newVal
      
          prev = next
          next = newVal
        }
      }
      

      Um dies zu testen, können wir für eine Endlosschleife eine endliche Zahl verwenden und die Fibonacci-Sequenz in die Konsole eingeben.

      // Print the first 10 values of fibonacci
      const fib = fibonacci()
      
      for (let i = 0; i < 10; i++) {
        console.log(fib.next().value)
      }
      

      Dadurch ergibt sich Folgendes:

      Output

      0 1 1 2 3 5 8 13 21 34

      Die Fähigkeit, mit unendlichen Datensätzen zu arbeiten, ist ein Teil dessen, was Generatoren so leistungsstark macht. Dies kann nützlich sein für Beispiele wie infinites Scrollen auf dem Frontend einer Webanwendung.

      Übergeben von Werten in Generatoren

      In diesem Artikel haben wir Generatoren als Iteratoren verwendet und Werte in jeder Iteration geliefert. Neben der Erzeugung von Werten können Generatoren auch Werte aus next() konsumieren. In diesem Fall beinhaltet yield einen Wert.

      Es ist wichtig zu beachten, dass das erste aufgerufene next() keinen Wert übergibt, sondern nur den Generator startet. Um das zu demonstrieren, können wir den Wert von yield aufzeichnen und einige Male next() mit einigen Werten aufrufen.

      function* generatorFunction() {
        console.log(yield)
        console.log(yield)
      
        return 'The end'
      }
      
      const generator = generatorFunction()
      
      generator.next()
      generator.next(100)
      generator.next(200)
      

      Dadurch ergibt sich folgendes Output:

      Output

      100 200 {value: "The end", done: true}

      Es ist auch möglich, den Generator mit einem Anfangswert zu versehen. Im folgenden Beispiel erstellen wir eine for-Schleife und übergeben jeden Wert in die next()-Methode, versehen die ürsprüngliche Funktion aber auch mit einem Argument:

      function* generatorFunction(value) {
        while (true) {
          value = yield value * 10
        }
      }
      
      // Initiate a generator and seed it with an initial value
      const generator = generatorFunction(0)
      
      for (let i = 0; i < 5; i++) {
        console.log(generator.next(i).value)
      }
      

      Wir rufen den Wert von next() ab und geben der nächsten Iteration einen neuen Wert, der dem vorherigen Wert plus Zehn entspricht. Dadurch ergibt sich Folgendes:

      Output

      0 10 20 30 40

      Eine weitere Möglichkeit des Startens eines Generators besteht darin, den Generator mit einer Funktion zu umgeben, die vor jeder anderen Handlung immer einmal next() anruft.

      async/await mit Generatoren

      Eine asynchrone Funktion ist ein in ES6+ JavaScript verfügbarer Funktionstyp, der das Arbeiten mit asynchronen Daten erleichtert, indem er diese synchron erscheinen lässt. Generatoren verfügen über eine umfangreichere Palette von Fähigkeiten als asynchrone Funktionen, sind jedoch in der Lage, ein ähnliches Verhalten zu replizieren. Die Implementierung asynchroner Programmierung auf diese Weise kann die Flexibilität Ihres Codes erhöhen.

      In diesem Abschnitt zeigen wir ein Beispiel zur Reproduktion von async/await mit Generatoren.

      Wir erstellen eine asynchrone Funktion, die die Fetch API verwendet, um Daten über die JSONPlaceholder API (die JSON-Beispieldaten zu Testzwecken bereitstellt) zu erhalten, und die Rückmeldung in der Konsole aufzeichnet.

      Beginnen Sie mit dem Definieren einer asynchronen Funktion namens getUsers, die Daten von der API abruft und eine Reihe von Objekten zurückgibt, und rufen Sie dann getUsers auf:

      const getUsers = async function() {
        const response = await fetch('https://jsonplaceholder.typicode.com/users')
        const json = await response.json()
      
        return json
      }
      
      // Call the getUsers function and log the response
      getUsers().then(response => console.log(response))
      

      Dadurch ergeben sich JSON-Daten, die ähnlich sind wie folgt:

      Output

      [ {id: 1, name: "Leanne Graham" ...}, {id: 2, name: "Ervin Howell" ...}, {id: 3, name": "Clementine Bauch" ...}, {id: 4, name: "Patricia Lebsack"...}, {id: 5, name: "Chelsey Dietrich"...}, ...]

      Mit Generatoren können wir etwas nahezu Identisches erstellen, das die Schlüsselwörter async/await nicht verwendet. Es verwendet stattdessen eine neue, von uns erstellte Funktion und liefert yield-Werte anstatt von await-Promises.

      Im folgenden Code-Block definieren wir eine Funktion namens getUsers, die unsere neue asyncAlt-Funktion (wir schreiben diese später) nutzt, um async/await zu imitieren.

      const getUsers = asyncAlt(function*() {
        const response = yield fetch('https://jsonplaceholder.typicode.com/users')
        const json = yield response.json()
      
        return json
      })
      
      // Invoking the function
      getUsers().then(response => console.log(response))
      

      Wie wir sehen können, sieht diese fast genauso aus wie die async/await-Implementierung – außer dass eine Generator-Funktion vorhanden ist, die Werte liefert.

      Nun können wir eine asyncAlt-Funktion erstellen, die einer asynchronen Funktion ähnelt. asyncAlt hat eine Generator-Funktion als Parameter – das ist unsere Funktion, die die Promises liefert, die fetch zurückgibt. asyncAlt gibt selbst eine Funktion zurück und löst jedes Promise, das es findet, bis hin zum letzten:

      // Define a function named asyncAlt that takes a generator function as an argument
      function asyncAlt(generatorFunction) {
        // Return a function
        return function() {
          // Create and assign the generator object
          const generator = generatorFunction()
      
          // Define a function that accepts the next iteration of the generator
          function resolve(next) {
            // If the generator is closed and there are no more values to yield,
            // resolve the last value
            if (next.done) {
              return Promise.resolve(next.value)
            }
      
            // If there are still values to yield, they are promises and
            // must be resolved.
            return Promise.resolve(next.value).then(response => {
              return resolve(generator.next(response))
            })
          }
      
          // Begin resolving promises
          return resolve(generator.next())
        }
      }
      

      Das ergibt dasselbe Output wie bei der async/await-Version:

      Output

      [ {id: 1, name: "Leanne Graham" ...}, {id: 2, name: "Ervin Howell" ...}, {id: 3, name": "Clementine Bauch" ...}, {id: 4, name: "Patricia Lebsack"...}, {id: 5, name: "Chelsey Dietrich"...}, ...]

      Beachten Sie, dass diese Implementierung demonstriert, wie Generatoren anstelle von async/await verwendet werden können. Sie ist jedoch kein produktreifer Entwurf. Sie verfügt weder über eine Einrichtung zur Fehlerbehandlung noch über die Fähigkeit, Parameter in die gelieferten Werte zu übergeben. Obwohl diese Methode Ihrem Code mehr Flexibilität verleihen kann, ist async/await oft die bessere Wahl, da es Implementierungsdetails abstrahiert, sodass Sie sich auf das Schreiben von produktivem Code konzentrieren können.

      Zusammenfassung

      Generatoren sind Prozesse, die Ausführungen anhalten und wieder aufnehmen können. Sie sind ein leistungsfähiger, vielseitiger Bestandteil von JavaScript, obwohl sie nicht häufig verwendet werden. In diesem Tutorial haben Sie mehr über Generator-Funktionen und Generator-Objekte, für Generatoren verfügbare Methoden, yield– und yield*-Operatoren sowie Generatoren mit finiten und infiniten Datensätzen erfahren. Behandelt wurde auch eine Möglichkeit, asynchronen Code ohne geschachtelte Callbacks oder lange Promise-Ketten zu implementieren.

      Wenn Sie mehr über die Syntax von JavaScript lernen möchten, besuchen Sie unsere Tutorials Grundlegendes zu This, Bind, Call und Apply in JavaScript sowie Grundlegendes zu den Objekten Map und Set in JavaScript.



      Source link