In Defense of the Ternary Statement

Avatar of Burke Holland
Burke Holland am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Vor einigen Monaten war ich auf Hacker News (wie man das eben so macht) und stieß auf einen (inzwischen gelöschten) Artikel darüber, keine if-Anweisungen zu verwenden. Wenn Sie neu in dieser Idee sind (so wie ich es war), dann erwartet Sie ein echtes Vergnügen. Suchen Sie einfach nach „if statements“ auf Hacker News. Sie werden Artikel finden, die vorschlagen, dass Sie sie vielleicht nicht brauchen, Artikel, die sie als Code Smell bezeichnen und sogar den Quintessenz-„considered harmful“. Hören Sie, Sie wissen, dass ein Programmierkonzept legitim ist, wenn Leute vorschlagen, dass dessen Verwendung jemanden tatsächlich verletzen wird.

Und wenn Ihnen das nicht genug ist, gibt es immer noch die „Anti-If Campaign“. Wenn Sie beitreten, erhalten Sie ein schickes Banner und Ihren Namen auf der Website. WENN Sie beitreten. Oh, die süße, süße Ironie.

Als ich dieses bizarre „if-Anathema-Phänomen“ zum ersten Mal bemerkte, fand ich es interessant, aber wahrscheinlich nur weitere Leute, die sich im Internet aufregen. Man ist immer nur eine Google-Suche davon entfernt, jemanden zu finden, der sich über irgendetwas aufregt. Wie zum Beispiel diese Person, die Kätzchen hasst. KÄTZCHEN.

Einige Zeit später sah ich mir Linus Torvalds‘ TED-Interview an. In diesem Interview zeigt er zwei Folien. Die erste Folie enthält Code, den er als „schlechten Geschmack“ bezeichnet.

Und die zweite ist derselbe Code, aber in dem, was Linus als „guten Geschmack“ betrachten würde.

Ich erkenne an, dass Linus eine etwas polarisierende Figur ist und Sie vielleicht nicht mit der Formulierung „guter Geschmack“ vs. „schlechter Geschmack“ einverstanden sind. Aber ich denke, wir können uns universell darauf einigen, dass die zweite Folie einfach angenehmer für die Augen ist. Sie ist prägnant, hat weniger logische Pfade zu verfolgen und enthält keine if-Anweisung. Ich möchte, dass mein Code so aussieht. Es muss kein genialer Algorithmus sein (wird er nie sein), aber ich denke, er kann sauber sein, und erinnern Sie sich, was Billy Corgan von Smashing Pumpkins über Sauberkeit gesagt hat…

Sauberkeit ist Göttlichkeit. Und Gott ist leer. Genau wie ich.

– Billy Corgan, „Zero“

So düster! Aber was für ein erstaunliches Album.

Abgesehen davon, dass Ihr Code unübersichtlich aussieht, erfordern if-Anweisungen oder „Branching Logic“ (Verzweigungslogik), dass Ihr Gehirn zwei separate Pfade gleichzeitig zusammen mit allen Dingen, die auf diesen Pfaden auftreten könnten, im Gedächtnis behält und auswertet. Wenn Sie if-Anweisungen verschachteln, verschärft sich das Problem, da Sie einen Entscheidungsbaum erstellen und verfolgen, und Ihr Gehirn muss wie ein betrunkener Affe über diesen Baum hüpfen. Diese Art von Dingen macht Code schwer lesbar. Und denken Sie daran, Sie sollten Ihren Code so schreiben, dass Sie an den Idioten denken, der danach kommt und ihn warten muss. Und dieser Idiot sind wahrscheinlich Sie.

Als mein eigener Lieblingsidiot bemühe ich mich in letzter Zeit bewusst darum, if-Anweisungen in meinem JavaScript zu vermeiden. Das gelingt mir nicht immer, aber zumindest zwingt es mich, über die Lösung des Problems aus einem völlig anderen Blickwinkel nachzudenken. Es macht mich zu einem besseren Entwickler, weil es mich zwingt, einen Teil meines Gehirns zu aktivieren, der sonst auf einem Sitzsack sitzt und Erdnuss-M&Ms isst, während die if-Anweisung die ganze Arbeit macht.

Bei dem Versuch, keine if-Anweisungen zu schreiben, habe ich meine Liebe dafür entdeckt, wie JavaScript es Ihnen ermöglicht, bedingte Logik mit ternären Anweisungen und logischen Operatoren zu komponieren. Was ich Ihnen jetzt vorschlagen möchte, ist, dass das Ternary ein schlechtes Ansehen bekommen hat und Sie es zusammen mit den Operatoren && und || verwenden können, um ziemlich prägnanten und lesbaren Code zu schreiben.

Das vielgeschmähte Ternary

Als ich anfing, als Programmierer zu arbeiten, sagten die Leute: „Benutze niemals ein Ternary. Sie sind zu kompliziert.“ Also habe ich sie nicht benutzt. Niemals. Ich habe nie ein Ternary benutzt. Ich habe nie einmal die Mühe unternommen zu hinterfragen, ob diese Leute richtig lagen oder nicht.

Ich glaube nicht, dass sie richtig lagen.

Ternaries sind nur einzeilige if-Anweisungen. Zu behaupten, sie seien in irgendeiner Form implizit zu kompliziert, ist einfach… nicht wahr. Ich meine, ich bin nicht der kühlste Donut in der Schachtel, aber ich habe überhaupt keine Probleme, ein einfaches Ternary zu verstehen. Ist es möglich, dass wir uns hier nur ein wenig selbst infantilisieren, wenn wir sagen, sie immer zu vermeiden? Ich denke, ein gut strukturiertes Ternary schlägt eine if-Anweisung jedes Mal.

Nehmen wir ein einfaches Beispiel. Sagen wir, wir haben eine Anwendung, in der wir testen wollen, ob der Benutzer angemeldet ist. Wenn er es ist, schicken wir ihn auf seine Profilseite. Andernfalls schicken wir ihn auf die Startseite. Hier ist die Standard-if-Anweisung, um das zu tun…

if (isLogggedIn) {
  navigateTo('profile');
}
else {
  navigateTo('unauthorized');
}

Das ist eine verdammt einfache Operation, die sich über sechs Zeilen erstreckt. SECHS ZEILEN. Denken Sie daran, dass Sie bei jeder Zeile Code, die Sie durchlaufen, an den Code oberhalb denken und daran, wie er den Code unterhalb beeinflusst.

Nun die ternäre Version…

isLoggedIn ? navigateTo('profile') : navigateTo('unauthorized');

Ihr Gehirn muss hier nur eine Zeile auswerten, nicht sechs. Sie müssen nicht zwischen Zeilen wechseln und sich merken, was auf der vorherigen Zeile stand.

Ein Nachteil des Ternary ist jedoch, dass Sie nicht nur für eine Bedingung auswerten können. Basierend auf dem vorherigen Beispiel, wenn Sie zur Profilseite navigieren möchten, wenn der Benutzer angemeldet ist, aber überhaupt keine Aktion ausführen, wenn er es nicht ist, funktioniert das hier nicht…

// !! Doesn't Compile !!
logggedIn ? navigateTo('profile')

Hier müssten Sie eine tatsächliche if-Anweisung schreiben. Oder etwa nicht?

Es gibt einen Trick, den Sie in JavaScript verwenden können, wenn Sie nur eine Seite der Bedingung auswerten möchten und keine if-Anweisung verwenden wollen. Das tun Sie, indem Sie die Funktionsweise von JavaScript mit den Operatoren || (oder) und && (und) nutzen.

loggedIn && navigateTo('profile');

Wie funktioniert das!?

Was wir hier tun, ist, JavaScript zu fragen: „Sind beide Dinge wahr?“ Wenn der erste Punkt falsch ist, gibt es keinen Grund für die JavaScript-Virtual-Machine, den zweiten auszuführen. Wir wissen bereits, dass nicht beide wahr sind, da einer von ihnen falsch ist. Wir nutzen die Tatsache aus, dass JavaScript sich nicht darum kümmert, den zweiten Punkt auszuwerten, wenn der erste falsch ist. Das ist gleichbedeutend mit der Aussage: „Wenn die erste Bedingung wahr ist, führe die zweite aus.“

Was, wenn wir das nun umdrehen wollen? Was, wenn wir nur zur Profilseite navigieren wollen, wenn der Benutzer **nicht** angemeldet ist? Sie könnten einfach ein ! vor die Variable loggedIn setzen, aber es gibt noch einen anderen Weg.

loggedIn || navigateTo('profile');

Das besagt: „Ist eines von beiden wahr?“ Wenn das erste falsch ist, muss es das zweite auswerten, um sicher zu sein. Wenn das erste jedoch wahr ist, wird das zweite niemals ausgeführt, da es bereits weiß, dass eines von beiden wahr ist; daher ist die gesamte Anweisung wahr.

Nun, ist das besser, als einfach das zu tun?

if (!loggedIn) navigateTo('profile');

Nein. In dieser Form nicht. Aber hier ist die Sache: Sobald Sie wissen, dass Sie die Operatoren && und || verwenden können, um Gleichheit außerhalb von if-Anweisungen auszuwerten, können Sie sie verwenden, um Ihren Code erheblich zu vereinfachen.

Hier ist ein komplexeres Beispiel. Sagen wir, wir haben eine Login-Funktion, an die wir ein Benutzerobjekt übergeben. Dieses Objekt kann null sein, also müssen wir den lokalen Speicher überprüfen, ob der Benutzer dort eine gespeicherte Sitzung hat. Wenn ja und wenn es sich um einen Administrator handelt, leiten wir ihn zu einem Dashboard. Andernfalls senden wir ihn zu einer Seite, die ihm mitteilt, dass er nicht autorisiert ist. Hier ist, wie das als einfache if-Anweisung aussieht.

function login(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  if (user) {
    if (user.loggedIn && user.isAdmin) {
      navigateTo('dashboard');
    }
    else {
      navigateTo('unauthorized');
    }
  }
  else {
    navigateTo('unauthorized');
  }
}

Autsch. Das ist kompliziert, weil wir viele Null-Bedingungsprüfungen auf dem user-Objekt durchführen. Ich möchte diesen Beitrag nicht zu sehr wie einen Strohmann gestalten, also vereinfachen wir das, da hier viel redundanter Code vorhanden ist, den wir wahrscheinlich in andere Funktionen refaktorieren würden.

function checkUser(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  return user;
}

function checkAdmin(user) {
  if (user.isLoggedIn && user.isAdmin) {
    navigateTo('dashboard');
  }
  else {
    navigateTo('unauthorized');
  }
}

function login(user) {
  if (checkUser(user)) {
    checkAdmin(user);
  }
  else {
    navigateTo('unauthorized');
  }
}

Die Hauptlogin-Funktion ist einfacher, aber das ist tatsächlich mehr Code und nicht unbedingt „sauberer“, wenn man das Ganze betrachtet und nicht nur die login-Funktion.

Ich möchte vorschlagen, dass wir all dies in zwei Zeilen erledigen können, wenn wir auf if-Anweisungen verzichten, das Ternary annehmen und logische Operatoren verwenden, um die Gleichheit zu bestimmen.

function login(user) {
  user = user || getFromLocalStorage('user');
  user && (user.loggedIn && user.isAdmin) ? navigateTo('dashboard') : navigateTo('unauthorized')
}

Das ist alles. All dieser Lärm, der von if-Anweisungen erzeugt wird, kollabiert auf zwei Zeilen. Wenn die zweite Zeile für Sie etwas lang und unleserlich wirkt, wickeln Sie sie so um, dass die Bedingungen auf einer eigenen Zeile stehen.

function login(user) {
  user = user || getFromLocalStorage("user");
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}

Wenn Sie sich Sorgen machen, dass die nächste Person nicht weiß, wie die Operatoren && und || in JavaScript funktionieren, fügen Sie einige Kommentare, etwas Weißraum und einen glücklichen Baum hinzu. Entfesseln Sie Ihren inneren Bob Ross.

function login(user) {
  // if the user is null, check local storage to
  // see if there is a saved user object there
  user = user || getFromLocalStorage("user");
  
  // Make sure the user is not null, and is also
  // both logged in and an admin. Otherwise, DENIED. 🌲
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}

Andere Dinge, die Sie tun können

Wo wir gerade dabei sind, hier sind einige andere Tricks, die Sie mit JavaScript-Bedingungen anstellen können.

Zuweisung

Einer meiner Lieblingstricks (den ich oben verwendet habe) ist eine Einzeiler, um zu prüfen, ob ein Element null ist, und es dann neu zuzuweisen, wenn es das ist. Das tun Sie mit einem ||-Operator.

user = user || getFromLocalStorage('user');

Und Sie können so weitermachen bis ins Unendliche…

user = user || getFromLocalStorage('user') || await getFromDatabase('user') || new User();

Das funktioniert auch mit dem Ternary…

user = user ? getFromLocalStorage('user') : new User();

Mehrere Bedingungen

Sie können mehrere Bedingungen an ein Ternary übergeben. Wenn wir zum Beispiel protokollieren wollen, dass der Benutzer sich angemeldet hat, und dann navigieren, können wir das tun, ohne all das in eine andere Funktion abstrahieren zu müssen. Wickeln Sie es in Klammern und fügen Sie ein Komma hinzu.

isLoggedIn ? (log('Logged In'), navigateTo('dashboard')) : navigateTo('unauthorized');

Das funktioniert auch mit Ihren && und || Operatoren…

isLoggedIn && (log('Logged In'), navigateTo('dashboard'));

Verschachtelung von ternären Ausdrücken

Sie können Ihre ternären Ausdrücke verschachteln. In seinem exzellenten Artikel über Ternary demonstriert Eric Elliot dies mit dem folgenden Beispiel…

const withTernary = ({
  conditionA, conditionB
}) => (
  (!conditionA)
    ? valueC
    : (conditionB)
    ? valueA
    : valueB
);

Das Interessanteste, was Eric dort tut, ist die Negation der ersten Bedingung, damit Sie nicht am Ende Fragezeichen und Doppelpunkte nebeneinander haben, was die Lesbarkeit erschwert. Ich würde das noch einen Schritt weiter gehen und etwas Einrückung hinzufügen. Ich habe auch die geschweiften Klammern und ein explizites Return hinzugefügt, weil das Sehen einer Klammer und dann sofort einer weiteren mein Gehirn dazu bringt, eine Funktionsaufrufung zu erwarten, die nie kommt.

const withTernary = ({ conditionA, conditionB }) => {
  return (
    (!conditionA)
      ? valueC  
      : (conditionB)
        ? valueA
        : valueB
  )
}

Als allgemeine Regel denke ich, dass Sie in Erwägung ziehen sollten, Ternaries oder if-Anweisungen nicht zu verschachteln. Jeder der obigen Artikel auf Hacker News wird Sie zu derselben Schlussfolgerung bringen. Obwohl ich Sie nicht beschämen will, sondern nur vorschlagen, dass Sie sich vielleicht (und nur vielleicht) später selbst danken werden, wenn Sie es nicht tun.


Das ist mein Plädoyer für das missverstandene Ternary und logische Operatoren. Ich denke, sie helfen Ihnen, sauberen, lesbaren Code zu schreiben und if-Anweisungen komplett zu vermeiden. Nun, wenn wir Linus Torvalds dazu bringen könnten, all dies als „guten Geschmack“ zu genehmigen. Ich könnte früh in Rente gehen und den Rest meines Lebens in Frieden leben.