Einführung
Es scheint, dass die Lodash- und Underscore- Bibliotheken mittlerweile allgegenwärtig sind und immer noch die supereffiziente Kompositionsmethode haben , die wir heute kennen .
Schauen wir uns die Compose-Funktion genauer an und sehen, wie sie Ihren Code lesbarer, wartbarer und eleganter macht.
Die Grundlagen
Wir werden viele Lodash-Funktionen behandeln, weil 1) wir keine eigenen grundlegenden Algorithmen schreiben werden - dies wird uns von dem ablenken, worauf ich mich konzentrieren möchte; und 2) Die Lodash-Bibliothek wird von vielen Entwicklern verwendet und kann problemlos durch Unterstriche, jede andere Bibliothek oder Ihre eigenen Algorithmen ersetzt werden.
Bevor wir uns einige einfache Beispiele ansehen, werfen wir einen Blick auf die Funktionsweise der Compose-Funktion und sehen, wie Sie bei Bedarf Ihre eigene Compose-Funktion implementieren.
var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
Dies ist die grundlegendste Implementierung. Hinweis: Funktionen in Argumenten werden von rechts nach links ausgeführt . Das heißt, die Funktion auf der rechten Seite wird zuerst ausgeführt und ihr Ergebnis wird an die Funktion auf der linken Seite übergeben.
Schauen wir uns nun diesen Code an:
function reverseAndUpper(str) {
var reversed = reverse(str);
return upperCase(reversed);
}
Die Funktion reverseAndUpper kehrt zuerst die angegebene Zeichenfolge um und konvertiert sie dann in Großbuchstaben. Wir können diesen Code mit der grundlegenden Compose-Funktion umschreiben:
var reverseAndUpper = compose(upperCase, reverse);
Die Funktion reverseAndUpper kann jetzt verwendet werden:
reverseAndUpper(''); //
Wir hätten das gleiche Ergebnis erzielen können, wenn wir den Code geschrieben hätten:
function reverseAndUpper(str) {
return upperCase(reverse(str));
}
Diese Option sieht eleganter aus und ist einfacher zu warten und wiederzuverwenden.
Die Möglichkeit, Funktionen schnell zu entwerfen und Datenverarbeitungs-Pipelines zu erstellen, ist in verschiedenen Situationen nützlich, wenn Sie Daten unterwegs transformieren müssen. Stellen Sie sich nun vor, Sie müssen eine Auflistung an eine Funktion übergeben, die Elemente der Auflistung transformieren und den Maximalwert zurückgeben, nachdem alle Pipeline-Schritte abgeschlossen wurden, oder eine bestimmte Zeichenfolge in einen Booleschen Wert konvertieren. Mit der Compose-Funktion können Sie mehrere Funktionen kombinieren und so eine komplexere Funktion erstellen.
Lassen Sie uns eine flexiblere Kompositionsfunktion implementieren, die eine beliebige Anzahl anderer Funktionen und Argumente enthalten kann. Die vorherige Compose-Funktion, die wir uns angesehen haben, funktioniert nur mit zwei Funktionen und verwendet nur das erste übergebene Argument. Wir können es so umschreiben:
var compose = function() {
var funcs = Array.prototype.slice.call();
return funcs.reduce(function(f,g) {
return function() {
return f(g.apply(this, ));
};
});
};
Mit einer solchen Funktion können wir Code wie folgt schreiben:
Var doSometing = compose(upperCase, reverse, doSomethingInitial);
doSomething('foo', 'bar');
Es gibt viele Bibliotheken, die die Compose-Funktion implementieren. Wir brauchen unsere Compose-Funktion, um zu verstehen, wie es funktioniert. Natürlich ist es in verschiedenen Bibliotheken unterschiedlich implementiert. Die Kompositionsfunktionen aus verschiedenen Bibliotheken machen dasselbe, so dass sie austauschbar sind. Nachdem wir nun verstanden haben, worum es bei der Compose-Funktion geht, sehen wir uns anhand der folgenden Beispiele die Funktion _.compose aus der Lodash-Bibliothek an.
Beispiele von
Fangen wir einfach an:
function notEmpty(str) {
return ! _.isEmpty(str);
}
Die Funktion notEmpty ist die Negation des von der Funktion _.isEmpty zurückgegebenen Werts.
Wir können das gleiche Ergebnis mit der Funktion _.compose aus der Lodash-Bibliothek erzielen. Schreiben wir die Funktion nicht:
function not(x) { return !x; }
var notEmpty = _.compose(not, _.isEmpty);
Jetzt können Sie die notEmpty-Funktion mit jedem Argument verwenden:
notEmpty('foo'); // true
notEmpty(''); // false
notEmpty(); // false
notEmpty(null); // false
Dies ist ein sehr einfaches Beispiel. Schauen wir uns etwas Komplizierteres an:
Die Funktion findMaxForCollection gibt den Maximalwert aus einer Sammlung von Objekten mit den Eigenschaften id und val (value) zurück.
function findMaxForCollection(data) {
var items = _.pluck(data, 'val');
return Math.max.apply(null, items);
}
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data);
Die Compose-Funktion kann verwendet werden, um dieses Problem zu lösen:
var findMaxForCollection = _.compose(function(xs) { return Math.max.apply(null, xs); }, _.pluck);
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data, 'val'); // 6
Hier gibt es viel zu tun.
_.pluck erwartet eine Auflistung als erstes Argument und eine Rückruffunktion als zweites. Ist es möglich, die Funktion _.pluck teilweise anzuwenden? In diesem Fall können Sie mithilfe von Curry die Reihenfolge der Argumente ändern.
function pluck(key) {
return function(collection) {
return _.pluck(collection, key);
}
}
Die Funktion findMaxForCollection muss noch etwas weiter optimiert werden. Lassen Sie uns unsere eigene Max-Funktion erstellen.
function max(xs) {
return Math.max.apply(null, xs);
}
Jetzt können wir unsere Kompositionsfunktion eleganter gestalten:
var findMaxForCollection = _.compose(max, pluck('val'));
findMaxForCollection(data);
Wir haben unsere eigene Zupffunktion geschrieben und können sie nur mit der Eigenschaft 'val' verwenden. Möglicherweise verstehen Sie nicht, warum Sie Ihre eigene Abrufmethode schreiben sollten, wenn Lodash bereits über eine vorgefertigte und praktische Funktion verfügt
_.pluck. Das Problem ist, dass es _.pluckeine Sammlung als erstes Argument erwartet und wir es anders machen wollen. Durch Ändern der Reihenfolge der Argumente können wir die Funktion teilweise anwenden, indem wir den Schlüssel als erstes Argument übergeben. Die zurückgegebene Funktion akzeptiert Daten.
Wir können unsere Probenahmemethode noch etwas verfeinern. Lodash hat eine praktische Methode
_.curry, mit der Sie unsere Funktion folgendermaßen schreiben können:
function plucked(key, collection) {
return _.pluck(collection, key);
}
var pluck = _.curry(plucked);
Wir wickeln einfach die ursprüngliche Zupffunktion ein, um die Argumente auszutauschen. Jetzt gibt Zupfen die Funktion zurück, bis alle Argumente verarbeitet wurden. Mal sehen, wie der endgültige Code aussieht:
function max(xs) {
return Math.max.apply(null, xs);
}
function plucked(key, collection) {
return _.pluck(collection, key);
}
var pluck = _.curry(plucked);
var findMaxForCollection = _.compose(max, pluck('val'));
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data); // 6
Die Funktion findMaxForCollection kann von rechts nach links gelesen werden. Das heißt, zuerst wird die Eigenschaft val jedes Elements in der Auflistung an die Funktion übergeben und anschließend das Maximum aller akzeptierten Werte zurückgegeben.
var findMaxForCollection = _.compose(max, pluck('val'));
Dieser Code ist einfacher zu pflegen, wiederverwendbar und viel eleganter. Schauen wir uns das letzte Beispiel an, um noch einmal die Eleganz der Funktionskomposition zu genießen.
Nehmen wir die Daten aus dem vorherigen Beispiel und fügen eine neue Eigenschaft mit dem Namen active hinzu. Jetzt sehen die Daten so aus:
var data = [{id: 1, val: 5, active: true},
{id: 2, val: 6, active: false },
{id: 3, val: 2, active: true }];
Rufen wir diese Funktion getMaxIdForActiveItems (Daten) auf . Es nimmt eine Sammlung von Objekten auf, filtert alle aktiven Objekte heraus und gibt den Maximalwert der gefilterten zurück.
function getMaxIdForActiveItems(data) {
var filtered = _.filter(data, function(item) {
return item.active === true;
});
var items = _.pluck(filtered, 'val');
return Math.max.apply(null, items);
}
Können Sie diesen Code eleganter gestalten? Es hat bereits die Max- und Zupffunktionen, daher müssen wir nur einen Filter hinzufügen:
var getMaxIdForActiveItems = _.compose(max, pluck('val'), _.filter);
getMaxIdForActiveItems(data, function(item) {return item.active === true; }); // 5
Bei der Funktion tritt
_.filterdas gleiche Problem auf wie bei _.pluck: Wir können diese Funktion nicht teilweise anwenden, da sie eine Sammlung als erstes Argument erwartet. Wir können die Reihenfolge der Argumente im Filter ändern, indem wir die ursprüngliche Funktion umbrechen:
function filter(fn) {
return function(arr) {
return arr.filter(fn);
};
}
Fügen wir eine Funktion hinzu
isActive, die ein Objekt nimmt und prüft, ob das aktive Flag auf true gesetzt ist.
function isActive(item) {
return item.active === true;
}
Eine Funktion
filtermit einer Funktion isActivekann teilweise angewendet werden, sodass nur Daten an die Funktion getMaxIdForActiveItems übergeben werden .
var getMaxIdForActiveItems = _.compose(max, pluck('val'), filter(isActive));
Jetzt müssen wir nur noch Daten übertragen:
getMaxIdForActiveItems(data); // 5
Jetzt hindert uns nichts daran, diese Funktion neu zu schreiben, sodass sie den Maximalwert unter inaktiven Objekten findet und zurückgibt:
var isNotActive = _.compose(not, isActive);
var getMaxIdForNonActiveItems = _.compose(max, pluck('val'), filter(isNotActive));
Fazit
Wie Sie vielleicht bemerkt haben, ist die Funktionszusammensetzung eine aufregende Funktion, die Ihren Code elegant und wiederverwendbar macht. Die Hauptsache ist, es richtig anzuwenden.
Links
lodash
Hey Unterstrich, du machst es falsch! (Hey Underscore, du machst alles falsch!)
@sharifsbeat
Weiterlesen: