Soll ich Funktionen, die nur in einer anderen Funktion verwendet werden, innerhalb dieser Funktion platzieren?

30

Insbesondere schreibe ich in JavaScript.

Angenommen, meine primäre Funktion ist Funktion A. Wenn Funktion A mehrere Aufrufe von Funktion B ausführt, Funktion B jedoch nirgendwo anders verwendet wird, sollte ich Funktion B dann einfach in Funktion A einfügen?

Ist das eine gute Übung? Oder sollte ich die Funktion B trotzdem in den gleichen Bereich wie die Funktion A versetzen?

Gary
quelle

Antworten:

32

Ich bin im Allgemeinen für verschachtelte Funktionen, insbesondere in JavaScript.

  • In JavaScript können Sie die Sichtbarkeit einer Funktion nur einschränken, indem Sie sie in eine andere Funktion einbetten.
  • Die Hilfsfunktion ist ein privates Implementierungsdetail. Wenn Sie den gleichen Bereich festlegen, bedeutet dies, dass Sie die privaten Funktionen einer Klasse öffentlich machen.
  • Wenn sich herausstellt, dass es von allgemeinerem Nutzen ist, ist es einfach ausziehen, da Sie sicher sein können, dass der Helfer derzeit nur von dieser einen Funktion verwendet wird. Mit anderen Worten, es macht Ihren Code zusammenhängender.
  • Sie können häufig Parameter entfernen, wodurch die Funktionssignatur und -implementierung weniger ausführlich wird. Dies ist nicht dasselbe, wie Variablen global zu machen. Es ist eher so, als würde man ein Klassenmitglied in einer privaten Methode verwenden.
  • Sie können den Hilfsfunktionen bessere, einfachere Namen geben, ohne sich um Konflikte sorgen zu müssen.
  • Die Hilfsfunktion ist leichter zu finden. Ja, es gibt Tools, die Ihnen helfen können. Es ist immer noch einfacher, die Augen ein wenig nach oben zu bewegen.
  • Code bildet natürlich eine Art Abstraktionsbaum: einige allgemeine Funktionen an der Wurzel, die in mehrere Implementierungsdetails verzweigen. Wenn Sie einen Editor mit faltender / zusammenklappender Funktion verwenden, erstellt die Verschachtelung eine Hierarchie eng verwandter Funktionen auf derselben Abstraktionsebene. Das macht es sehr einfach, den Code auf der gewünschten Ebene zu studieren und die Details zu verbergen.

Ich denke, viel Widerstand kommt von der Tatsache, dass die meisten Programmierer entweder in einer C / C ++ / Java-Tradition erzogen wurden oder von jemand anderem unterrichtet wurden, der es war. Verschachtelte Funktionen sehen nicht natürlich aus, weil wir ihnen beim Programmieren nicht viel ausgesetzt waren. Das heißt nicht, dass sie nicht nützlich sind.

Karl Bielefeldt
quelle
14

Sie sollten es aus mehreren Gründen global anwenden.

  • Durch das Verschachteln einer Hilfsfunktion in den Aufrufer wird die Länge des Aufrufers erhöht. Die Funktionslänge ist fast immer ein negativer Indikator. kurze funktionen sind einfacher zu verstehen, auswendig zu lernen, zu debuggen und zu warten.

  • Wenn die Hilfsfunktion einen vernünftigen Namen hat, reicht es aus, diesen Namen zu lesen, ohne dass die Definition in der Nähe angezeigt werden muss. Wenn Sie die Hilfedefinition sehen müssen, um die Aufruferfunktion zu verstehen, tut dieser Aufrufer zu viel oder arbeitet gleichzeitig an zu vielen Abstraktionsebenen.

  • Wenn der Helfer global verfügbar ist, können andere Funktionen ihn aufrufen, wenn er sich als allgemein nützlich erweist. Wenn der Helfer nicht verfügbar ist, sind Sie versucht, ihn auszuschneiden und einzufügen oder ihn zu vergessen und ihn schlecht zu implementieren oder eine andere Funktion länger zu machen, als sie sein muss.

  • Das Verschachteln der Hilfefunktion erhöht die Versuchung, Variablen aus dem Gültigkeitsbereich des Aufrufers ohne Deklaration zu verwenden, so dass unklar wird, welche Ein- und Ausgaben der Helfer hat. Wenn eine Funktion nicht eindeutig angibt, mit welchen Daten sie arbeitet und welche Auswirkungen sie hat, ist dies in der Regel ein Zeichen für unklare Verantwortlichkeiten. Wenn Sie den Helfer als eigenständige Funktion deklarieren, müssen Sie genau wissen, was er tatsächlich tut.

Bearbeiten

Das stellte sich als kontroverse Frage heraus, als ich dachte. Zu klären:

In JavaScript erfüllen Funktionen, die sich über große Dateien erstrecken, häufig die Rolle von Klassen, da die Sprache keinen anderen Mechanismus zur Einschränkung des Gültigkeitsbereichs bietet. Auf jeden Fall sollten Hilfsfunktionen in solche Quasi-Klassen gehen, nicht außerhalb von ihnen.

Und der Punkt , um einfacher Wiederverwendung setzt voraus , dass , wenn ein Unterprogramm nicht weiter verbreitet wird, sind Sie bereit , sie ganz von Ort zu bewegen und legt es an entsprechender Stelle, zB einer String Utility - Bibliothek oder in der globalen Konfigurationsregister. Wenn Sie Ihren Code nicht so bestellen möchten, können Sie das Unterprogramm genauso gut verschachteln, wie Sie es mit einem normalen Methodenobjekt in einer "blockartigeren" Sprache tun würden .

Kilian Foth
quelle
Danke, alle sehr guten Punkte. Wie soll ich meine Funktionen generell bestellen? Zum Beispiel, im Moment, bei kleinen Programmen bestelle ich sie nur alphabetisch. Aber sollte ich Helfer- und Anruferfunktionen zusammenfassen? Ich denke, ich sollte meine Funktionen zumindest anhand der Teile der Anwendung aufschlüsseln, für die sie beispielsweise gelten.
Gary
Ich habe mich lange nicht mehr darum gekümmert, Methoden zu definieren. Wenn Sie mit einem gewissen Grad an Professionalität programmieren, sollten Sie über eine Umgebung verfügen, in der Sie mit wenigen Tastenanschlägen zu jeder Definition springen können. Daher sind kurze Methoden nützlich, aber kurze oder sogar geordnete Klassendateien bringen wenig Wert. (Natürlich musste JavaScript alle Definitionen über ihre Verwendungszwecke setzen, oder das Ergebnis wäre technisch undefiniertes Verhalten. Ich kann mich nicht erinnern, ob dies immer noch der Fall ist oder nicht.)
Kilian Foth,
2
Das ist eine großartige Antwort. Das Problem der kaputten Kapselung wird von vielen bei der Verwendung innerer Funktionen nicht berücksichtigt.
sbichenko
2
Globals sind ein Codegeruch. Sie verursachen unnötige Kopplungen, die ein Refactoring verhindern, sie verursachen Namenskonflikte, sie legen Implementierungsdetails offen usw. Dies ist besonders schlecht in Javascript, da alle Variablen veränderbar sind, Code normalerweise direkt von Drittanbietern geladen wird, es gibt (noch) keine ein allgemein verfügbares Modulsystem oder sogar eine allgemein verfügbare Implementierung von "let". In solch einer feindlichen Umgebung ist die einzige Verteidigungsstrategie, die wir haben, die Kapselung in One-Shot-Funktionen.
Warbo
1
"Die Rolle der Klassen erfüllen" versucht, JS so zu schreiben, als wäre es etwas anderes. Was ist "die Rolle der Klassen"? Typprüfung? Modularität? Inszenierte Zusammenstellung? Erbe? Die Frage beinhaltet Scoping, also nehme ich an, Sie meinen die groben Scoping-Regeln von Klassen. Das ist nur das A und O : Wie sollte der Umfang des Unterrichts sein? PHP macht sie global, was keine Kapselung bietet. Python umfasst Klassen lexikalisch, aber wenn Sie sich in einem Block mit lexikalischem Umfang befinden, warum sollten Sie dann alles in einen anderen Block mit Klassenumfang einschließen? JS hat keine solche Redundanz: Verwenden Sie nur so viel lexikalischen Umfang, wie Sie benötigen.
Warbo
8

Ich werde einen dritten Weg vorschlagen, um beide Funktionen innerhalb eines Abschlusses zu platzieren. Es würde so aussehen:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

Wir erstellen den Abschluss, indem wir die Deklaration beider Funktionen in ein IIFE einwickeln . Der Rückgabewert des IIFE ist die öffentliche Funktion, die in einer Variablen des Namens für die Funktion gespeichert ist. Die öffentliche Funktion kann genauso aufgerufen werden, als ob sie als globale Funktion deklariert wäre, d functionA(). H. Beachten Sie, dass der Rückgabewert die Funktion ist , kein Aufruf der Funktion, also keine Parens am Ende.

Durch das Zusammenfassen der beiden Funktionen wie functionBfolgt , ist nun völlig privat und kann nicht außerhalb des Verschlusses zugegriffen werden, sondern ist nur für sichtbar functionA. Es überfrachtet nicht den globalen Namespace und die Definition von functionA.

cbojar
quelle
0

Durch das Definieren von Funktion B in Funktion A erhält Funktion B Zugriff auf die internen Variablen von A.

Eine in einer Funktion A definierte Funktion B vermittelt also den Eindruck (oder zumindest die Möglichkeit), dass B die lokalen Variablen von A verwendet oder ändert. Bei der Definition von B außerhalb von A wird deutlich, dass B dies nicht tut.

Aus Gründen der Klarheit des Codes würde ich B in A nur dann definieren, wenn B auf lokale Variablen von A zugreifen muss. Wenn B einen eindeutigen Zweck hat, der von A unabhängig ist, würde ich ihn definitiv außerhalb von A definieren.

Florian F
quelle
-1

Sie sollten es nicht verschachteln, da sonst die verschachtelte Funktion jedes Mal neu erstellt wird, wenn Sie die äußere Funktion aufrufen.

Domenic
quelle
-3

Da die Funktion B an keiner anderen Stelle verwendet wird, können Sie sie auch zu einer Insider-Funktion innerhalb der Funktion A machen, da dies der einzige Grund ist, warum sie existiert.

Jack_9
quelle
1
Dies scheint nichts Wesentliches über die in den vorherigen drei Antworten gemachten und erklärten Punkte hinzuzufügen
Mücke