
Manchmal haben wir alle schlechten Code geschrieben (und einige schreiben), und ich hoffe, wir alle arbeiten daran, unsere Fähigkeiten zu verbessern und nicht nur Artikel wie diesen zu lesen.
Warum müssen wir guten Code schreiben, nicht nur produktiven Code?
Während die Leistung Ihres Produkts oder Ihrer Website wichtig ist, ist auch das Aussehen Ihres Codes wichtig. Der Grund dafür ist, dass nicht nur das Gerät Ihren Code liest .
Erstens müssen Sie früher oder später Ihren eigenen Code erneut lesen, und wenn dies der Fall ist, hilft Ihnen nur gut geschriebener Code, zu verstehen, was Sie geschrieben haben, oder herauszufinden, wie Sie ihn beheben können.
Zweitens, wenn Sie in einem Team arbeiten oder mit anderen Entwicklern zusammenarbeiten, lesen alle Mitglieder des Teams Ihren Code und versuchen, ihn so zu interpretieren, wie sie es verstehen. Um es ihnen einfacher zu machen, ist es wichtig, beim Benennen von Variablen und Funktionen bestimmte Regeln zu befolgen, die Länge jeder Zeile zu begrenzen und die Struktur Ihres Codes beizubehalten.
Schauen wir uns zum Schluss ein konkretes Beispiel an.
Teil 1: Wie identifiziere ich schlechten Code?
Der einfachste Weg, schlechten Code zu identifizieren, besteht meiner Meinung nach darin, zu versuchen, den Code so zu lesen, als wäre es ein Satz oder eine Phrase .
Schauen Sie sich zum Beispiel diesen Code an:

Screenshot einer fehlerhaften Version von traverseUpUntil
Die oben dargestellte Funktion akzeptiert ein Element und eine bedingte Funktion und gibt den nächsten übergeordneten Knoten zurück, der die bedingte Funktion erfüllt.
const traverseUpUntil = (el, f) => {
Basierend auf der Tatsache, dass der Code wie einfacher Text gelesen werden sollte, weist die erste Zeile drei grobe Fehler auf.
- Funktionsparameter werden nicht wie Wörter gelesen .
- Angenommen,
el
kann verstanden werden, da ein solcher Name normalerweise zur Bezeichnung eines Elements verwendet wird, der Name des Parameters f
überhaupt nichts erklärt. - Der Name der Funktion kann folgendermaßen gelesen werden: "Schalter, bis el f passiert", was wahrscheinlich am besten als "Schalter, bis f für el geht" gelesen wird. Der beste Weg, dies zu tun, besteht natürlich darin, die Funktion als
el.traverseUpUntil(f)
zu lassen, aber dies ist ein weiteres Problem.
let p = el.parentNode
Dies ist die zweite Zeile. Wieder das Problem mit den Namen, diesmal mit der Variablen. Wenn sich jemand den Code ansieht, wird er höchstwahrscheinlich verstehen, was p
. Dies ist der parentNode
el
Parameters. Was jedoch passiert, wenn wir uns p
ansehen, das an anderer Stelle verwendet wird, haben wir keinen Kontext mehr, der erklärt, was es ist .
while (p.parentNode && !f(p)) {
In der nächsten Zeile besteht das Hauptproblem darin, dass wir nicht verstehen, was !f(p)
bedeutet oder tut, weil „f“ alles bedeuten kann . Es wird angenommen, dass die Person, die den Code liest, verstehen sollte, dass !f(p)
eine Überprüfung des aktuellen Knotens ist, um eine bestimmte Bedingung zu erfüllen. Wenn es vorbei ist, wird der Zyklus unterbrochen.
p = p.parentNode
Hier ist alles klar.
return p
Es ist nicht ganz offensichtlich, was aufgrund eines ungültigen Variablennamens zurückgegeben wird.
Teil 2: Lassen Sie uns umgestalten

Screenshot einer guten Version von traverseUpUntil
Zuerst ändern wir die Namen der Parameter und ihre Reihenfolge: (el, f) =>
bis (condition, node) =>
.
Sie fragen sich vielleicht, warum ich anstelle von "Element (russisches Element )" Knoten "(russischer Knoten ) verwendet habe. Ich habe es aus folgenden Gründen verwendet:
- Wir schreiben Code in Form von Knoten, zum Beispiel
.parentNode
. Warum also nicht konsistent machen .parentNode
- "Knoten" ist kürzer als "Element" und die Bedeutung geht nicht verloren .
Dann gehen wir weiter zu den Variablennamen:
let parent = node
Es ist sehr wichtig , den Wert Ihrer Variablen in ihrem Namen vollständig anzugeben , daher ist "p" jetzt "Eltern" (russische Eltern ). Möglicherweise haben Sie auch bemerkt, dass wir jetzt nicht mehr mit dem übergeordneten node.parentNode
, sondern nur mit dem Knoten.
Wir gehen weiter:
do { parent = parent.parentNode } while (parent.parentNode && !condition(parent))
Anstelle der üblichen while
ich die do ... while
. Dies bedeutet, dass wir den übergeordneten Knoten jedes Mal abrufen müssen, bevor wir die Bedingungen überprüfen, und nicht umgekehrt. Die Verwendung der do ... while
hilft auch dabei, Code wie einfachen Text zu lesen.
Versuchen wir zu lesen: "Weisen Sie den übergeordneten Knoten des übergeordneten Knotens dem übergeordneten Knoten zu, solange der übergeordnete Knoten einen übergeordneten Knoten hat und die Bedingungsfunktion nicht true zurückgibt . " Schon viel klarer.
return parent
Oft bevorzugen Entwickler die Verwendung einer allgemeinen Variablen ret
(oder returnValue
), aber dies ist eine ziemlich schlechte Praxis . Wenn Sie Ihre Rückgabevariablen korrekt benennen, wird deutlich, was zurückgegeben wird. Manchmal können Funktionen jedoch lang und komplex sein, was zu großer Verwirrung führt. In diesem Fall würde ich vorschlagen, Ihre Funktion in mehrere Funktionen zu unterteilen . Wenn dies immer noch zu kompliziert ist, kann das Hinzufügen von Kommentaren möglicherweise hilfreich sein.
Teil 3: Code-Vereinfachung
Nachdem Sie den Code lesbar gemacht haben, ist es Zeit , den unnötigen Code zu entfernen . Ich bin sicher, dass einige von Ihnen bereits bemerkt haben, dass wir die parent
Variable überhaupt nicht benötigen.
const traverseUpUntil = (condition, node) => { do { node = node.parentNode } while (node.parentNode && !condition(node)) return node }
Ich habe einfach die erste Zeile entfernt und "Eltern" durch "Knoten" ersetzt. Also habe ich den unnötigen Schritt des Erstellens eines „Elternteils“ übersprungen und bin direkt in die Schleife gegangen.
Aber was ist mit dem Variablennamen?
Obwohl "Knoten" nicht die beste Beschreibung für diese Variable ist, ist sie zufriedenstellend. Aber lasst uns hier nicht aufhören, lasst es uns umbenennen. Was ist mit "currentNode"?
const traverseUpUntil = (condition, currentNode) => { do { currentNode = currentNode.parentNode } while (currentNode.parentNode && !condition(currentNode)) return currentNode }
Das ist besser! Wenn wir nun die Methode lesen, wissen wir, dass currentNode
immer den Knoten darstellt, in dem wir uns gerade befinden, anstatt ein "irgendeiner Art" Knoten zu sein.