Prinzipien der funktionalen Programmierung in JavaScript

Der Autor des Materials, dessen Übersetzung wir heute veröffentlichen, sagt, dass er nach langer Zeit in der objektorientierten Programmierung über die Komplexität von Systemen nachgedacht habe. Laut John Ousterhout ist Komplexität alles, was das Verständnis oder die Änderung von Software erschwert. Der Autor dieses Artikels entdeckte nach einigen Recherchen die Konzepte der funktionalen Programmierung wie Immunität und reine Funktionen. Durch die Verwendung solcher Konzepte können Sie Funktionen erstellen, die keine Nebenwirkungen haben. Die Verwendung dieser Funktionen vereinfacht die Systemunterstützung und bietet dem Programmierer einige weitere Vorteile .

Bild

Hier sprechen wir über funktionale Programmierung und einige ihrer wichtigen Prinzipien. All dies wird durch viele JavaScript-Codebeispiele veranschaulicht.

Was ist funktionale Programmierung?


Sie können auf Wikipedia nachlesen, was funktionale Programmierung ist. Wir sprechen nämlich über die Tatsache, dass die funktionale Programmierung ein Programmierparadigma ist, bei dem der Berechnungsprozess als Berechnung der Werte von Funktionen im mathematischen Verständnis des letzteren behandelt wird. Die funktionale Programmierung umfasst die Berechnung der Ergebnisse von Funktionen aus den Quelldaten und den Ergebnissen anderer Funktionen und impliziert keine explizite Speicherung des Programmstatus. Dementsprechend impliziert dies nicht die Variabilität dieses Zustands.

Anhand von Beispielen werden wir nun einige Ideen der funktionalen Programmierung analysieren.

Reine Funktionen


Reine Funktionen sind das erste grundlegende Konzept, das untersucht werden muss, um die Essenz der funktionalen Programmierung zu verstehen.

Was ist eine „reine Funktion“? Was macht eine Funktion „sauber“? Eine reine Funktion muss folgende Anforderungen erfüllen:

  • Es gibt immer das gleiche Ergebnis zurück, wenn dieselben Argumente übergeben werden (solche Funktionen werden auch als deterministisch bezeichnet).
  • Eine solche Funktion hat keine Nebenwirkungen.

Betrachten Sie die erste Eigenschaft reiner Funktionen, nämlich die Tatsache, dass sie, wenn sie dieselben Argumente an sie übergeben, immer dasselbe Ergebnis zurückgeben.

▍ Funktionsargumente und Rückgabewerte


Stellen Sie sich vor, wir müssen eine Funktion erstellen, die die Fläche eines Kreises berechnet. Eine Funktion, die nicht rein ist, würde als Parameter den Radius des Kreises ( radius ) verwenden, wonach sie den Wert der Berechnung des Ausdrucks radius * radius * PI :

 const PI = 3.14; function calculateArea(radius) { return radius * radius * PI; } calculateArea(10); //  314 

Warum kann diese Funktion nicht als rein bezeichnet werden? Tatsache ist, dass es eine globale Konstante verwendet, die nicht als Argument an sie übergeben wird.

Stellen Sie sich nun vor, dass einige Mathematiker zu dem Schluss gekommen sind, dass der Wert der Konstante PI die Zahl 42 , wodurch der Wert dieser Konstante geändert wurde.

Jetzt gibt eine Funktion, die nicht rein ist, wenn ihr der gleiche Eingabewert, die Zahl 10 , übergeben wird, den Wert 10 * 10 * 42 = 4200 . Es stellt sich heraus, dass die Funktion bei Verwendung des Wertes des radius Parameters wie im vorherigen Beispiel ein anderes Ergebnis zurückgibt. Lassen Sie uns das beheben:

 const PI = 3.14; function calculateArea(radius, pi) { return radius * radius * pi; } calculateArea(10, PI); //  314 

Wenn wir diese Funktion aufrufen, übergeben wir ihr immer das Argument pi . Infolgedessen funktioniert die Funktion nur mit dem, was beim Aufruf an sie übergeben wird, ohne auf globale Entitäten zurückzugreifen. Wenn wir das Verhalten dieser Funktion analysieren, können wir zu folgenden Schlussfolgerungen kommen:

  • Wenn die Funktionen das Argument mit einem radius von 10 und einem Argument pi von 3.14 , wird immer das gleiche Ergebnis zurückgegeben - 314 .
  • Bei einem Aufruf mit einem radius von 10 und einem pi von 42 wird immer 4200 .

Dateien lesen


Wenn unsere Funktion Dateien liest, ist sie nicht sauber. Tatsache ist, dass sich der Inhalt der Dateien ändern kann.

 function charactersCounter(text) { return `Character count: ${text.length}`; } function analyzeFile(filename) { let fileContent = open(filename); return charactersCounter(fileContent); } 

Zufallszahlengenerierung


Jede Funktion, die auf einem Zufallszahlengenerator basiert, kann nicht rein sein.

 function yearEndEvaluation() { if (Math.random() > 0.5) {   return "You get a raise!"; } else {   return "Better luck next year!"; } } 

Lassen Sie uns nun über Nebenwirkungen sprechen.

▍ Nebenwirkungen


Ein Beispiel für einen Nebeneffekt, der beim Aufrufen einer Funktion auftreten kann, ist die Änderung globaler Variablen oder Argumente, die als Referenz an Funktionen übergeben werden.

Angenommen, wir müssen eine Funktion erstellen, die eine Ganzzahl verwendet und diese Zahl um 1 erhöht. So könnte eine Implementierung einer ähnlichen Idee aussehen:

 let counter = 1; function increaseCounter(value) { counter = value + 1; } increaseCounter(counter); console.log(counter); // 2 

Es gibt einen globalen Variablenzähler. Unsere Funktion, die nicht rein ist, empfängt diesen Wert als Argument und überschreibt ihn, indem sie einen zum vorherigen Wert hinzufügt.

Die globale Variable ändert sich, ähnlich wie bei der funktionalen Programmierung ist dies nicht erwünscht.

In unserem Fall wird der Wert der globalen Variablen geändert. Wie kann die Funktion "boostCounter increaseCounter() unter diesen Bedingungen sauber gemacht werden? In der Tat ist es sehr einfach:

 let counter = 1; function increaseCounter(value) { return value + 1; } increaseCounter(counter); // 2 console.log(counter); // 1 

Wie Sie sehen können, gibt die Funktion 2 , aber der Wert des globalen Variablenzählers ändert sich nicht. Hier können wir schließen, dass die Funktion den an sie übergebenen Wert zurückgibt, erhöht um 1 , ohne etwas zu ändern.

Wenn Sie die beiden oben genannten Regeln zum Schreiben reiner Funktionen befolgen, wird die Navigation in Programmen, die mit solchen Funktionen erstellt wurden, erleichtert. Es stellt sich heraus, dass jede Funktion isoliert ist und die Teile des Programms außerhalb davon nicht beeinflusst.

Reine Funktionen sind stabil, konsistent und vorhersehbar. Wenn diese Funktionen dieselben Eingabedaten empfangen, geben sie immer dasselbe Ergebnis zurück. Dies erspart dem Programmierer den Versuch, die Möglichkeit von Situationen zu berücksichtigen, in denen die Übertragung von Funktionen derselben Parameter zu unterschiedlichen Ergebnissen führt, da dies mit reinen Funktionen einfach unmöglich ist.

▍ Stärken reiner Funktionen


Zu den Stärken reiner Funktionen gehört die Tatsache, dass mit ihnen geschriebener Code leichter zu testen ist. Insbesondere müssen Sie keine Stub-Objekte erstellen. Dies ermöglicht Unit-Tests von reinen Funktionen in verschiedenen Kontexten:

  • Wenn Parameter A an die Funktion übergeben wird, wird der Rückgabewert von B erwartet.
  • Wenn Parameter C an die Funktion übergeben wird, wird der Rückgabewert von D erwartet.

Als einfaches Beispiel für diese Idee können wir eine Funktion angeben, die ein Array von Zahlen akzeptiert, und es wird erwartet, dass sie sich bei jeder Nummer dieses Arrays um eins erhöht und ein neues Array mit den folgenden Ergebnissen zurückgibt:

 let list = [1, 2, 3, 4, 5]; function incrementNumbers(list) { return list.map(number => number + 1); } 

Hier übergeben wir ein Array von Zahlen an die Funktion. Anschließend verwenden wir die Array-Methode map() , mit der wir jedes Element des Arrays ändern und ein neues Array bilden können, das von der Funktion zurückgegeben wird. Wir rufen die Funktion auf, indem wir ihr ein Listenarray übergeben:

 incrementNumbers(list); //  [2, 3, 4, 5, 6] 

Von dieser Funktion wird erwartet, dass nach Annahme eines Arrays der Form [1, 2, 3, 4, 5] ein neues Array [2, 3, 4, 5, 6] . So funktioniert es.

Immunität


Die Immunität einer bestimmten Entität kann als die Tatsache beschrieben werden, dass sie sich im Laufe der Zeit nicht ändert, oder als die Unmöglichkeit, diese Entität zu ändern.

Wenn sie versuchen, ein unveränderliches Objekt zu ändern, ist dies nicht erfolgreich. Stattdessen müssen Sie ein neues Objekt erstellen, das die neuen Werte enthält.

Beispielsweise verwendet JavaScript häufig die for Schleife. Im Verlauf seiner Arbeit werden, wie unten gezeigt, veränderbare Variablen verwendet:

 var values = [1, 2, 3, 4, 5]; var sumOfValues = 0; for (var i = 0; i < values.length; i++) { sumOfValues += values[i]; } sumOfValues // 15 

Bei jeder Iteration der Schleife sumOfValues der Wert der Variablen i und der Wert der globalen Variablen (dies kann als Status des Programms angesehen werden) sumOfValues . Wie kann in einer solchen Situation die Unveränderlichkeit von Unternehmen aufrechterhalten werden? Die Antwort liegt in der Verwendung von Rekursion.

 let list = [1, 2, 3, 4, 5]; let accumulator = 0; function sum(list, accumulator) { if (list.length == 0) {   return accumulator; } return sum(list.slice(1), accumulator + list[0]); } sum(list, accumulator); // 15 list; // [1, 2, 3, 4, 5] accumulator; // 0 

Es gibt eine Funktion sum() , die ein Array von Zahlen akzeptiert. Diese Funktion ruft sich selbst auf, bis das Array leer ist (dies ist der Grundfall unseres rekursiven Algorithmus ). Bei jeder solchen "Iteration" addieren wir den Wert eines der Elemente des Arrays zum Parameter der accumulator , ohne den globalen variablen accumulator . In diesem Fall bleiben die list der globalen Variablen und der accumulator unverändert, und vor und nach dem Funktionsaufruf werden dieselben Werte in ihnen gespeichert.

Es ist zu beachten, dass Sie zur Implementierung eines solchen Algorithmus reduce Methode zum reduce Arrays verwenden können. Wir werden weiter unten darüber sprechen.

In der Programmierung ist die Aufgabe weit verbreitet, wenn es erforderlich ist, basierend auf einer bestimmten Vorlage eines Objekts seine endgültige Darstellung zu erstellen. Stellen Sie sich vor, wir haben eine Zeichenfolge, die in eine Ansicht konvertiert werden muss, die als Teil der URL verwendet werden kann, die zu einer bestimmten Ressource führt.

Wenn wir dieses Problem mit Ruby und den Prinzipien von OOP lösen, erstellen wir zuerst eine Klasse, beispielsweise UrlSlugify , und erstellen dann eine Methode für diese Klasse slugify! Hiermit wird die Zeichenfolge konvertiert.

 class UrlSlugify attr_reader :text def initialize(text)   @text = text end def slugify!   text.downcase!   text.strip!   text.gsub!(' ', '-') end end UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug" 

Wir haben den Algorithmus implementiert, und das ist wunderbar. Hier sehen wir einen zwingenden Ansatz für die Programmierung, wenn wir bei der Verarbeitung der Linie jeden Schritt ihrer Transformation malen. Zuerst reduzieren wir die Zeichen auf Kleinbuchstaben, entfernen dann unnötige Leerzeichen und ändern schließlich die verbleibenden Leerzeichen auf dem Bindestrich.

Während dieser Transformation tritt jedoch eine Mutation des Programmzustands auf.

Sie können das Mutationsproblem bewältigen, indem Sie Funktionen erstellen oder Funktionsaufrufe verketten. Mit anderen Worten, das von der Funktion zurückgegebene Ergebnis wird als Eingabe für die nächste Funktion und damit für alle Funktionen in einer Kette verwendet. In diesem Fall ändert sich die ursprüngliche Zeichenfolge nicht.

 let string = " I will be a url slug   "; function slugify(string) { return string.toLowerCase()   .trim()   .split(" ")   .join("-"); } slugify(string); // i-will-be-a-url-slug 

Hier verwenden wir die folgenden Funktionen, die in JavaScript durch Standard-String- und Array-Methoden dargestellt werden:

  • toLowerCase : Konvertiert Zeichenfolgen in toLowerCase .
  • trim : Entfernt Leerzeichen vom Anfang und Ende einer Zeile.
  • split : Teilt eine Zeichenfolge in Teile und platziert Wörter, die durch Leerzeichen in einem Array getrennt sind.
  • join : bildet eine Zeichenfolge mit Wörtern, die durch einen Bindestrich getrennt sind, basierend auf einem Array mit Wörtern.

Mit diesen vier Funktionen können Sie eine Funktion zum Konvertieren einer Zeichenfolge erstellen, die diese Zeichenfolge selbst nicht ändert.

Transparenz verknüpfen


Erstellen Sie ein Funktionsquadrat square() , das das Ergebnis der Multiplikation einer Zahl mit derselben Zahl zurückgibt:

 function square(n) { return n * n; } 

Dies ist eine reine Funktion, die für denselben Eingabewert immer denselben Ausgabewert zurückgibt.

 square(2); // 4 square(2); // 4 square(2); // 4 // ... 

Unabhängig davon, wie viele Zahlen 2 an sie übergeben werden, gibt diese Funktion beispielsweise immer die Zahl 4 . Infolgedessen stellt sich heraus, dass ein Aufruf des Formularquadrats square(2) durch die Nummer 4 . Dies bedeutet, dass unsere Funktion die Eigenschaft der referenziellen Transparenz hat.

Im Allgemeinen können wir sagen, dass eine Funktion, die immer dasselbe Ergebnis für dieselben an sie übergebenen Eingabewerte zurückgibt, referenzielle Transparenz aufweist.

▍ Reine Funktionen + unveränderliche Daten = referenzielle Transparenz


Mit der im Titel dieses Abschnitts vorgestellten Idee können Sie Funktionen auswendig lernen. Angenommen, wir haben eine Funktion wie diese:

 function sum(a, b) { return a + b; } 

Wir nennen es so:

 sum(3, sum(5, 8)); 

Das Aufrufen der sum(5, 8) ergibt immer 13 . Daher kann der obige Aufruf wie folgt umgeschrieben werden:

 sum(3, 13); 

Dieser Ausdruck ergibt wiederum immer 16 . Infolgedessen kann es durch eine numerische Konstante ersetzt und gespeichert werden .

Funktioniert als erstklassige Objekte


Die Idee, Funktionen als Objekte der ersten Klasse wahrzunehmen, besteht darin, dass solche Funktionen als Werte betrachtet werden können und mit ihnen als Daten arbeiten. Folgende Merkmale der Funktionen können unterschieden werden:

  • Verweise auf Funktionen können in Konstanten und Variablen gespeichert werden und über diese auf Funktionen zugreifen.
  • Funktionen können als Parameter an andere Funktionen übergeben werden.
  • Funktionen können von anderen Funktionen zurückgegeben werden.

Das heißt, es geht darum, Funktionen als Werte zu betrachten und sie wie Daten zu behandeln. Mit diesem Ansatz können Sie verschiedene Funktionen kombinieren, um neue Funktionen zu erstellen, die neue Funktionen implementieren.

Stellen Sie sich vor, wir haben eine Funktion, die zwei übergebene numerische Werte hinzufügt, diese dann mit 2 multipliziert und das zurückgibt, was sich herausgestellt hat:

 function doubleSum(a, b) { return (a + b) * 2; } 

Jetzt schreiben wir eine Funktion, die die Sekunde vom ersten an sie übergebenen numerischen Wert subtrahiert, das Geschehene mit 2 multipliziert und den berechneten Wert zurückgibt:

 function doubleSubtraction(a, b) { return (a - b) * 2; } 

Diese Funktionen haben eine ähnliche Logik, sie unterscheiden sich nur darin, welche Art von Operationen sie mit den an sie übergebenen Zahlen ausführen. Wenn wir Funktionen als Werte betrachten und sie als Argumente an andere Funktionen übergeben können, bedeutet dies, dass wir eine Funktion erstellen können, die eine andere Funktion akzeptiert und verwendet, die die Merkmale der Berechnungen beschreibt. Diese Überlegungen ermöglichen es uns, die folgenden Konstruktionen zu erreichen:

 function sum(a, b) { return a + b; } function subtraction(a, b) { return a - b; } function doubleOperator(f, a, b) { return f(a, b) * 2; } doubleOperator(sum, 3, 1); // 8 doubleOperator(subtraction, 3, 1); // 4 

Wie Sie sehen können, hat die Funktion doubleOperator() jetzt einen Parameter f , und die Funktion, die sie darstellt, wird zum Verarbeiten der Parameter a und b . Mit den an die Funktion substraction() Funktionen sum() und substraction() können Sie das Verhalten der Funktion doubleOperator() steuern und entsprechend der darin implementierten Logik ändern.

Funktionen höherer Ordnung


Unter Funktionen höherer Ordnung verstehen wir Funktionen, die durch mindestens eines der folgenden Merkmale gekennzeichnet sind:

  • Eine Funktion verwendet eine andere Funktion als Argument (es kann mehrere solcher Funktionen geben).
  • Die Funktion gibt als Ergebnis ihrer Arbeit eine andere Funktion zurück.

Möglicherweise sind Sie bereits mit den Standard-JS-Array-Methoden filter() , map() und reduce() vertraut. Reden wir über sie.

▍ Filtern von Arrays und der filter () -Methode


Angenommen, wir haben eine bestimmte Sammlung von Elementen, die wir nach Attributen der Elemente dieser Sammlung filtern und eine neue Sammlung bilden möchten. Die Funktion filter() erwartet ein Kriterium für die Bewertung der Elemente, anhand dessen bestimmt wird, ob ein Element in die resultierende Sammlung aufgenommen werden soll oder nicht. Dieses Kriterium wird durch die an sie übergebene Funktion definiert, die true zurückgibt true wenn die Funktion filter() ein Element in die endgültige Auflistung aufnehmen soll, andernfalls false .

Stellen Sie sich vor, wir haben ein Array von Ganzzahlen und möchten es filtern, indem wir ein neues Array erhalten, das nur gerade Zahlen aus dem ursprünglichen Array enthält.

Imperativer Ansatz


Wenn Sie einen zwingenden Ansatz zur Lösung dieses Problems mit JavaScript anwenden, müssen Sie die folgende Abfolge von Aktionen implementieren:

  • Erstellen Sie ein leeres Array für neue Elemente (nennen wir es sogar evenNumbers ).
  • Iterieren Sie über das ursprüngliche Array von Ganzzahlen (nennen wir es numbers ).
  • evenNumbers Sie die im Zahlenarray gefundenen geraden Zahlen in das Array evenNumbers .

So sieht die Implementierung dieses Algorithmus aus:

 var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = []; for (var i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) {   evenNumbers.push(numbers[i]); } } console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10] 

Außerdem können wir eine Funktion schreiben (nennen wir sie even() ), die, wenn die Zahl gerade ist, true zurückgibt und wenn sie ungerade ist, false zurückgibt und sie dann an die Array-Methode filter() übergibt, die jedes Element des Arrays überprüft , bildet ein neues Array, das nur gerade Zahlen enthält:

 function even(number) { return number % 2 == 0; } let listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10] 

Hier ist übrigens die Lösung für ein interessantes Problem in Bezug auf die Array-Filterung , das ich bei der Arbeit an funktionalen Programmieraufgaben für Hacker Rank abgeschlossen habe . Aufgrund der Problembedingung war es erforderlich, ein Array von Ganzzahlen herauszufiltern und nur die Elemente anzuzeigen, die kleiner als ein bestimmter Wert von x .

Eine zwingende Lösung für dieses Problem in JavaScript könnte folgendermaßen aussehen:

 var filterArray = function(x, coll) { var resultArray = []; for (var i = 0; i < coll.length; i++) {   if (coll[i] < x) {     resultArray.push(coll[i]);   } } return resultArray; } console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0] 

Das Wesentliche des imperativen Ansatzes ist, dass wir die Abfolge der von der Funktion ausgeführten Aktionen skizzieren. Wir beschreiben nämlich die Suche des Arrays, vergleichen das aktuelle Element des Arrays mit x und platzieren dieses Element im resultArray Array, wenn es den Test besteht.

Deklarativer Ansatz


Wie kann man zu einem deklarativen Ansatz zur Lösung dieses Problems und der entsprechenden Verwendung der filter() -Methode wechseln, bei der es sich um eine Funktion höherer Ordnung handelt? Zum Beispiel könnte es so aussehen:

 function smaller(number) { return number < this; } function filterArray(x, listOfNumbers) { return listOfNumbers.filter(smaller, x); } let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0]; filterArray(3, numbers); // [2, 1, 0] 

In diesem Beispiel ist es möglicherweise ungewöhnlich, das this in der Funktion smaller() verwenden, aber hier ist nichts kompliziert. Das this ist das zweite Argument für die Methode filter() . In unserem Beispiel ist dies die Nummer 3 die durch den x Parameter von filterArray() . Diese Nummer wird dadurch angezeigt.

Der gleiche Ansatz kann verwendet werden, wenn das Array Entitäten enthält, die eine ziemlich komplexe Struktur haben, z. B. Objekte. Angenommen, wir haben ein Array, in dem Objekte gespeichert sind, die die Namen der durch die Eigenschaft name dargestellten Personen und Informationen über das Alter dieser durch die Eigenschaft age dargestellten Personen enthalten. So sieht ein Array aus:

 let people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; 

Wir möchten dieses Array filtern, indem wir nur diejenigen Objekte auswählen, bei denen es sich um Personen handelt, deren Alter 21 Jahre überschritten hat. So lösen Sie dieses Problem:

 function olderThan21(person) { return person.age > 21; } function overAge(people) { return people.filter(olderThan21); } overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }] 

Hier haben wir ein Array mit Objekten, die Menschen darstellen. Wir überprüfen die Elemente dieses Arrays mit der Funktion olderThan21() . In diesem Fall beziehen wir uns bei der Überprüfung auf die Eigenschaft age jedes Elements und prüfen, ob der Wert dieser Eigenschaft 21 überschreitet. Wir übergeben diese Funktion an die filter() -Methode, die das Array filtert.

▍ Verarbeitung von Array-Elementen und der map () -Methode


Die map() -Methode wird zum Konvertieren von Array-Elementen verwendet. Er wendet die an jedes Element des Arrays übergebene Funktion an und erstellt dann ein neues Array, das aus den geänderten Elementen besteht.

Lassen Sie uns die Experimente mit dem bereits bekannten Personenfeld fortsetzen. Jetzt werden wir dieses Array nicht basierend auf der Eigenschaft von Altersobjekten filtern. Wir müssen auf dieser Grundlage eine Liste von Zeilen der Form TK is 26 years old erstellen, die TK is 26 years old . Bei diesem Ansatz werden die Zeilen, in die sich die Elemente verwandeln, gemäß der Vorlage p.name is p.age years old p.name , wobei p.name und p.age die Werte der entsprechenden Eigenschaften der Elemente des people .

Ein zwingender Ansatz zur Lösung dieses Problems in JavaScript sieht folgendermaßen aus:

 var people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; var peopleSentences = []; for (var i = 0; i < people.length; i++) { var sentence = people[i].name + " is " + people[i].age + " years old"; peopleSentences.push(sentence); } console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

Wenn Sie auf einen deklarativen Ansatz zurückgreifen, erhalten Sie Folgendes:

 function makeSentence(person) { return `${person.name} is ${person.age} years old`; } function peopleSentences(people) { return people.map(makeSentence); } peopleSentences(people); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

Tatsächlich besteht die Hauptidee hier darin, dass Sie mit jedem Element des ursprünglichen Arrays etwas tun und es dann in einem neuen Array platzieren müssen.

Hier ist eine weitere Aufgabe mit dem Hacker-Rang, die der Aktualisierung der Liste gewidmet ist . Wir sprechen nämlich davon, die Werte der Elemente eines vorhandenen numerischen Arrays in ihre absoluten Werte zu ändern. Wenn Sie beispielsweise ein Array [1, 2, 3, -4, 5] es die Form [1, 2, 3, 4, 5] da der Absolutwert von -4 4 .

Hier ist ein Beispiel für eine einfache Lösung dieses Problems, wenn wir ein Array durchlaufen und die Werte seiner Elemente in ihre absoluten Werte ändern.

 var values = [1, 2, 3, -4, 5]; for (var i = 0; i < values.length; i++) { values[i] = Math.abs(values[i]); } console.log(values); // [1, 2, 3, 4, 5] 

Math.abs() die Werte der Elemente des Arrays zu konvertieren, wird die Math.abs() -Methode verwendet. Die geänderten Elemente werden an die Stelle geschrieben, an der sie sich vor der Konvertierung befanden.

.

, , , . . , , , .

, , map() . ?

, abs() , , .

 Math.abs(-1); // 1 Math.abs(1); // 1 Math.abs(-2); // 2 Math.abs(2); // 2 

, , .

, , Math.abs() map() . , ? map() . :

 let values = [1, 2, 3, -4, 5]; function updateListMap(values) { return values.map(Math.abs); } updateListMap(values); // [1, 2, 3, 4, 5] 

, , , , , .

▍ reduce()


reduce() .

. , -. Product 1 , Product 2 , Product 3 Product 4 . .

, . Zum Beispiel könnte es so aussehen:

 var orders = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; var totalAmount = 0; for (var i = 0; i < orders.length; i++) { totalAmount += orders[i].amount; } console.log(totalAmount); // 120 

reduce() , ( sumAmount() ), , reduce() :

 let shoppingCart = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount; function getTotalAmount(shoppingCart) { return shoppingCart.reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

shoppingCart , , sumAmount() , ( order , amount ), — currentTotalAmount .

reduce() , getTotalAmount() , sumAmount() , 0 .

map() reduce() . «»? , map() shoppingCart , amount , reduce() sumAmount() . :

 const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

getAmount() amount . map() , , , [10, 30, 20, 60] . , reduce() , .

▍ filter(), map() reduce()


, , filter() , map() reduce() . , , .

-. , :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] 

. :

  • type , , books .
  • , .
  • .

, :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] const byBooks = (order) => order.type == "books"; const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .filter(byBooks)   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 70 

Zusammenfassung


JavaScript-. , .

Liebe Leser! ?



Source: https://habr.com/ru/post/de434112/


All Articles