Wie viele andere Entwickler arbeite ich an meiner Weiterbildung und lerne, was ich über ES6 kann. Eine Methode, dies zu tun, ist der Besuch von Workshops kluger Leute. Ich habe an Kyle Simpsons ES6: The Good Parts-Kurs teilgenommen und fand mich besonders an den praktischen Anwendungen eines Stücks ES6 interessiert, das mir zuvor nicht aufgefallen war: Destrukturierte Objekte als Parameter.
Einige Hintergründe
Wenn eine Anwendung wächst, können unsere Funktionsparameter aufgebläht werden. Dies ist aus einer Reihe von Gründen ein Problem
- Wir müssen uns die spezifische Reihenfolge dieser Parameter merken, wenn wir die Funktion aufrufen. Wenn es mehr als drei sind, erfordert es etwas Kontextwechsel, um sie zu vergleichen.
- Wir haben keine Standardwerte von Haus aus
- Um uns richtig selbst zu dokumentieren, verlassen wir uns stark auf die Benennung. Das Benennen von Argumenten in Code-Reviews macht so viel Spaß!
Wie lösen wir das mit ES6?
Wenn man sehr breit über die Übergabe von Objekten als Parameter nachdenkt, mag das auf den ersten Blick ziemlich einfach erscheinen und vielleicht sogar etwas umständlicher, da die Leute es nicht gewohnt sind, Parameter so zu lesen und auf diese Weise zu denken. Aber schauen Sie sich all die oben genannten Probleme an, die dies löst
- Sie müssen sich die spezifische Reihenfolge nicht mehr merken
- Wir können leicht optionale Parameter haben
- Wir können Standardwerte haben, was auch gut ist, weil es selbstdokumentierend ist
- Wir können Fallbacks für Fehlerbedingungen haben oder uns entscheiden, dies nicht zu tun, wenn wir einen Fehler anzeigen möchten
Destrukturierung kann uns helfen, Daten aus Arrays oder Objekten einfach in einzelne Variablen zu extrahieren und zu manipulieren und unsere Parameter aussagekräftiger zu machen. Dies hilft uns, unsere Absichten zukünftigen Lesern zu vermitteln und unterstützt die Wartung.
Was ist Destrukturierung?
Destrukturierte Objekte als Parameter kombinieren einige Dinge auf einmal. Beginnen wir mit dem Konzept der Destrukturierung als Ganzes, bevor wir diese spezielle Anwendung zeigen. Es gibt viele Verwendungszwecke und Anwendungen für die Destrukturierung, vielleicht am häufigsten angewendet, wenn Sie ein großes JSON-Objekt oder eine API haben und aussagekräftige Variablen daraus extrahieren möchten. Destrukturierung ist in ES5 möglich, aber aufgrund einer Kombination vieler Funktionen in ES6 ist die Destrukturierung viel besser lesbar und wertvoller.
Wir werden nur über die Destrukturierung von Objekten (nicht von Arrays) sprechen, aber es ist wichtig zu beachten, dass beides möglich ist. Wir beginnen mit einem sehr einfachen und knappen Vergleich der Objekt-Destrukturierung in ES5 vs. ES6
So destrukturieren wir ein Objekt in ES5
var o = {a: 1, b: 2, c: 3};
var chico = o.a,
harpo = o.b,
groucho = o.c;
console.log(chico, harpo, groucho);
// 1 2 3
Und nun sein ES6-Gegenstück
const o = {chico: 1, harpo: 2, groucho: 3};
const { chico, harpo, groucho } = o;
console.log(chico, harpo, groucho);
// 1 2 3
Dies hat einige wirklich nützliche praktische Anwendungen. Wir können Top-Level-Variablen aus Objekten erstellen, ein paar Eigenschaften auf einmal, und sie sogar extrahieren, wenn sie verschachtelt sind.
const o = {
chico: 1,
harpo: 2,
groucho: 3,
othermarx : {
zeppo: 4,
isthatamarxbrother: {
gummo: 5,
polly: 6
}
}
};
const { gummo, polly } = o.othermarx.isthatamarxbrother;
console.log(gummo, polly);
Wir können sie sogar umbenennen, indem wir etwas wie const { gummo, polly:cousin } = o.othermarx.isthatamarxbrother; tun.
Dieser Artikel wird nicht die gesamte Destrukturierung behandeln, damit wir uns auf eine praktische Anwendung konzentrieren können. Wenn Sie mehr darüber erfahren möchten, wie sie sich auf Arrays in ES6 anwendet und einige andere schöne Verwendungen, hier sind einige gute Ressourcen
Wie verwenden wir das in einer Funktion?
Beginnen wir mit einem wirklich einfachen Beispiel einer Funktion mit einigen Parametern. Sagen wir, wir brauchen ein Manifest mit vielen Standardversanddaten. Diese Daten bleiben meistens gleich, weichen aber von Tag zu Tag ab. Standardwerte sind hier sehr sinnvoll, aber wir müssten etwas wie das hier schreiben
function shipmentES5Defaults(params) {
params = params === undefined ? {} : params;
var items = params.items === undefined ? 'bananas' : params.items;
var number = params.number === undefined ? 5: params.number;
var pkg = params.pkg === undefined ? 'crates' : params.pkg;
console.log("We have a shipment of " + items + " in " + number + " " + pkg + ".");
}
shipmentES5Defaults();
// We have a shipment of bananas in 5 crates.
shipmentES5Defaults({
items: 'cherries',
pkg: 'bags'
});
// We have a shipment of cherries in 5 bags.
Das ist gut, denn wir haben unsere Standardwerte und geben nicht `undefined` zurück, wenn etwas nicht angegeben ist.
Aber in ES6 können wir das gerade behandelte destrukturierte Objekt als unsere Parameter verwenden, was uns eine schnelle und einfache Möglichkeit bietet, Standardwerte bereitzustellen. Abgesehen davon, dass es nützlich ist, ist es auch ein wenig selbstdokumentierend. Das ist ziemlich hilfreich und eine wirklich schöne Art, unsere Funktionen mit Blick auf Lesbarkeit zu schreiben. Ganz zu schweigen davon, dass das ES6-Template-Literal die Zeichenkette sowohl zum Lesen als auch zum Schreiben sehr einfach macht.
function shipmentES6({ items = 'bananas', number = 5, package = 'boxes' } = {}) {
console.log(`We have a shipment of ${items} in ${number} ${package}.`);
};
shipmentES6({ package: 'crates' });
// -> We have a shipment of bananas in 5 crates.
shipmentES6({ items: 'tomatoes', number: 18 });
// -> We have a shipment of tomatoes in 18 boxes.
shipmentES6();
// -> We have a shipment of bananas in 5 boxes.
Abgesehen von all diesen Vorteilen, wenn Sie und Ihre Kollegen oder Mitarbeiter sich an diesen Schreibstil gewöhnen, dann, wenn Sie keinen Standardwert übergeben, kommunizieren Sie auch, dass es keine Standardwerte gibt und Sie lieber einen Fehler auslösen lassen.
Bleiben Sie dran für unseren nächsten Beitrag, in dem wir diese Art, Parameter zu schreiben, verwenden werden, um ein SVG-Diagramm ohne externe Bibliotheken zu erstellen.
Warum hast du geschrieben
anstelle von
Ich wette, Sie finden es verwirrend. ES6 bietet einige interessante Punkte, ist aber meiner Meinung nach zu verwirrend und auf den ersten Blick nicht sehr klar verständlich.
Sie könnten auch ein Standardobjekt außerhalb der Funktion definieren, das als alle Ihre Standardfunktionen für Sendungen verwendet wird. Das Übergeben dieser innerhalb der Funktion und das Definieren vieler Parameter macht die Funktion lächerlich lang.
Ähm, kann ich meinen eigenen Beitrag nicht bearbeiten?
Übrigens gibt es ein kleines Problem bei params.number || 5, da das Übergeben von 0 als "falsy" gilt, daher wird 5 verwendet.
Ich denke, Sie finden es verwirrend, weil Sie es nicht gewohnt sind. Glauben Sie mir, mir ist es auch passiert. Dann habe ich es versucht und jetzt fühlt es sich völlig natürlich und verständlich an. Ich vermisse die Destrukturierung sehr, wenn ich an ES5-Apps arbeite.
Weil es nicht dasselbe ist: Wenn
params.number0 ist, wird es ignoriert und auf 5 gesetzt.Der Trick
this || thatfunktioniert nur, wenn Sie keine "falsy" Werte erwarten.So sollten Funktionen überall funktionieren. Sobald man es sieht, erscheint es so offensichtlich
Ich sehe, wie ES6 einige Dinge einfacher macht, aber ich würde es nicht als vollständigen Drop-in-Ersatz für strukturierte Parameter verwenden.
Ich habe einen Kompromiss zwischen reiner Destrukturierung und statischen Parametern gefunden, den ich von der Arbeit mit (bitte steinigen Sie mich nicht) Adobe Flash vor vielen Jahren übernommen habe. Es ist eine Kombination aus strukturiert und destrukturiert.
Zum Beispiel sind alle Parameter, die erforderlich sind, zuerst und zweitens usw. Normalerweise nach Wichtigkeit geordnet. Dies sind Parameter, die einfach keine Standardwerte haben können, aber übergeben werden müssen, damit die Funktion ordnungsgemäß funktioniert.
Dann werden alle optionalen Parameter (die ich wahrscheinlich mit Standardwerten versehen würde) als letzter Parameter übergeben. Und dieser wird mit einem Standardwert kombiniert.
(Verzeihen Sie das jQuery, es ist nur der einfachste Weg, dies zu demonstrieren)
Mit ES6 erlaubt mir dies einfach, die `.extend()`-Funktion von jQuery oder mein eigenes System des Optionsmergings zu verwerfen. Aber das bedeutet nicht, dass ich mich ausschließlich auf unstrukturierte Parameter verlassen würde.
Die Verwendung der gemischten Technik ermöglicht es mir, Funktionalität zu erweitern oder zu entfernen, ohne die Parameter zu ändern. Und ich habe gelernt, dass dies bei einer Codebasis, an der man über 10 Jahre arbeitet, notwendig ist.
Dies bietet auch sofortiges Feedback für Fehler bei fehlenden erforderlichen Feldern. Etwas, das vollkommen unstrukturierte Parameter nicht tun. Die Verwendung von nur unstrukturierten Parametern zwingt Sie, Ihre eigene Fehlerprüfung für fehlende erforderliche Parameter in jeder Funktion zu schreiben. (Es sei denn, ich verpasse hier eine magische Funktion in der Art und Weise, wie ES6 die Standardwerte verarbeitet?)
In ES6 könnte man das auch ohne jQuery machen.
Rahul: Was Sie beschreiben, ist noch nicht Teil einer Spezifikation, es ist noch in Stufe 2 https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/README.md
Ich denke, das kann sehr nützlich sein. Danke.
Ich liebte dieses Muster, als es zuerst in CoffeeScript eingeführt wurde. In letzter Zeit sehe ich es jedoch als Geruchssignal.
Erstes Problem: Die Verwendung von Objekt-Destrukturierung bewirkt, dass Ihre Funktion mit mehreren Parametern nur einen einzigen Parameter (den Objekt-Hash) akzeptiert. Dies macht Ihre Funktionen weniger komponierbar, da sie nicht teilweise angewendet werden können.
Zweites Problem: Wenn zu viele Parameter vorhanden sind, deren Reihenfolge leicht vergessen werden kann (oder viele optionale Parameter), ist dies ein Zeichen dafür, dass entweder die Funktion zu groß ist und das Prinzip der einzigen Verantwortung verletzt. Oder alternativ, dass einige der Parameter eng korreliert sind und tatsächlich zusammen übergeben werden sollten. (Es gibt wahrscheinlich ein Wertobjekt, das sich unter den primitiv besessenen Parametern versteckt.)
Schließlich, in dem Fall, dass Sie wirklich viele nicht verwandte Parameter haben und die Funktion wirklich eng fokussiert ist, haben Sie wahrscheinlich ein Options-Objekt und keine Menge locker korrelierter Parameter. Und wenn Sie ein Options-Objekt haben, werden Sie es wahrscheinlich mit einem anderen Konfigurations-Objekt zusammenführen und/oder Standardwerte mit
_.defaultsanwenden; dann macht die Destrukturierung in separate Variablen den Sinn zunichte.Dies *ist* ein Options-Objekt. Das Objekt wird später destrukturiert, aber es beginnt als eines.
Auch Ihr Standardobjekt wird innerhalb der Funktionsparameter definiert, sodass Sie drei Schritte in einem erhalten: Parameterdefinition, Standardwertdefinition, Anwendung von Standardwerten.
Sie können eine fehlerwerfende Funktion als Standardparameter dort festlegen, um eine obligatorische Option zu markieren.
Die im Beitrag gezeigten Beispiele sind definitiv keine Optionen, da sie das Verhalten der Funktion nicht ändern. Sie sind die Daten, mit denen die Funktion arbeitet.
Beispiel: Eine Funktion, die eine Nachricht zum Drucken entgegennimmt. Die Nachricht ist keine Option, sie sind die Daten. Eine Option wäre, ob die Nachricht farblich hervorgehoben oder formatiert werden soll. Offensichtlich ist diese Unterscheidung eher kontextabhängig. Ein Heuristikum ist jedoch, ob die Parameter/Optionen jemals sinnvoll über eine Konfigurationsdatei bereitgestellt würden. (Wenn nicht, sind es wahrscheinlich keine Optionen!)
Zugegeben, um auf Ihren Punkt einzugehen, habe ich einfach angenommen, dass andere Leute meine Meinung teilen, dass Optionen keine frei schwebenden Variablen sein sollten, sondern zur Klarheit an ein Options-Objekt gebunden sein sollten. Dadurch wird der "Options"-Fall von den ersten beiden von mir beschriebenen Szenarien unterschieden.
Die Syntax ist nett, und ich liebe Destrukturierung definitiv. Aber meistens finde ich seine Verwendung ein Signal dafür, dass die Funktionsdefinition schlecht konzipiert war.
Ich muss Jason beim Geruchssignal zustimmen. Es ist ein großartiges Werkzeug für die Werkzeugkiste, aber es sollte sparsam verwendet werden. Das Beispiel im Artikel ist viel besser geschrieben als
Warum würden Sie den Parameter `item` jemals standardisieren? Offensichtlich ist es nur ein Beispiel, aber manchmal schauen die Leute sich solche Beispiele an und nehmen sie einfach als "den Weg, es jetzt zu tun".
Danke für dieses Beispiel! Benannte Parameter sind großartig und machen den Code, der die Funktion aufruft, viel klarer. Ein (kleiner?) Nachteil ist, dass, wenn eine Funktion ein Objekt anstelle von Parametern akzeptiert, Sie Parameter für diese Funktion nicht (einfach) teilweise anwenden können.
Ihr
Hutist schrecklich.@Gandalf – danke fürs Teilen, es sind diese Kommentare, die das Web großartig machen.