Definieren Sie eine Funktion innerhalb einer anderen Funktion in JavaScript

Lesezeit: 7 Minuten

Benutzer-Avatar
Tamakiquadrat

function foo(a) {
    if (/* Some condition */) {
        // perform task 1
        // perform task 3
    }
    else {
        // perform task 2
        // perform task 3
    }
}

Ich habe eine Funktion, deren Struktur der obigen ähnelt. Ich möchte abstrahieren Aufgabe 3 in eine Funktion, bar()aber ich möchte den Zugriff auf diese Funktion auf den Geltungsbereich von beschränken foo(a).

Ist es richtig, Folgendes zu ändern, um das zu erreichen, was ich will?

function foo(a) {
    function bar() {
        // Perform task 3
    }

    if (/* Some condition */) {
        // Perform task 1
        bar();
    }
    else {
        // Perform task 2
        bar();
    }
}

Wenn das obige richtig ist, tut es bar() werden jedes Mal neu definiert foo(a) wird angerufen? (Ich mache mir hier Sorgen um die Verschwendung von CPU-Ressourcen.)

  • Testen Sie selbst, ob es sich lohnt: jsperf.com Ich stelle mir vor, es hängt von task3 ab.

    – Tom Byrer

    18. April 2012 um 7:08 Uhr

  • @tomByer – +1 für den Vorschlag des Tools

    – Tamakiquadrat

    18. April 2012 um 7:11 Uhr

Benutzer-Avatar
TJ Crowder

Ja, was du da hast, ist richtig. Einige Notizen:

  • bar wird bei jedem Aufruf der Funktion erstellt fooaber:
    • Bei modernen Browsern ist dies ein sehr schneller Vorgang. (Einige Engines kompilieren möglicherweise nur die Code dafür einmal verwenden und diesen Code dann jedes Mal mit einem anderen Kontext wiederverwenden; Googles V8-Motor [in Chrome and elsewhere] tut das in den meisten Fällen.)
    • Und je nachdem was bar tut, können einige Engines feststellen, dass sie es “inline” können, wodurch der Funktionsaufruf vollständig eliminiert wird. V8 tut dies, und ich bin sicher, dass es nicht der einzige Motor ist, der dies tut. Natürlich können sie dies nur tun, wenn es das Verhalten des Codes nicht ändert.
  • Die Leistungsauswirkung, falls vorhanden, zu haben bar jedes Mal erstellt wird, variiert stark zwischen den JavaScript-Engines. Wenn bar trivial ist, wird es von nicht nachweisbar bis ziemlich klein variieren. Wenn Sie nicht anrufen foo tausende Male hintereinander (zum Beispiel von a mousemove Handler), würde ich mir keine Sorgen machen. Selbst wenn Sie es sind, würde ich mir nur Sorgen machen, wenn ich ein Problem bei langsameren Motoren sehe. Hier ist ein Testfall mit DOM-Vorgängenwas darauf hindeutet, dass es einen Einfluss gibt, aber einen trivialen (wahrscheinlich durch das DOM-Zeug ausgewaschen). Hier ist ein Testfall, der reine Berechnungen durchführt was eine viel höhere Wirkung zeigt, aber ehrlich gesagt sprechen wir sogar von einem Unterschied MikroSekunden, weil sogar eine 92% ige Steigerung auf etwas, das dauert MikroSekunden passieren, ist immer noch sehr, sehr schnell. Bis/es sei denn, Sie haben eine Auswirkung in der realen Welt gesehen, ist dies kein Grund zur Sorge.
  • bar ist nur innerhalb der Funktion zugänglich und hat Zugriff auf alle Variablen und Argumente für diesen Aufruf der Funktion. Dies macht dies zu einem sehr praktischen Muster.
  • Beachten Sie das, weil Sie eine Funktion verwendet haben Erklärungspielt es keine Rolle, wo Sie die Deklaration platzieren (oben, unten oder in der Mitte – solange sie sich auf der obersten Ebene der Funktion befindet und nicht innerhalb einer Flusssteuerungsanweisung, was ein Syntaxfehler ist), wird sie vor der definiert Die erste Zeile des schrittweisen Codes wird ausgeführt.

  • Thx für deine Antwort. Wollen Sie damit sagen, dass dies ein vernachlässigbarer Leistungseinbruch ist? (da eine Kopie von bar wird bei jedem Aufruf von erstellt foo)

    – Tamakiquadrat

    18. April 2012 um 7:06 Uhr

  • @ahmoo: Bei der JavaScript-Leistung lautet die Antwort fast immer: Es kommt darauf an. 🙂 Es hängt davon ab, mit welcher Engine es läuft und wie oft Sie anrufen foo. Wenn Sie nicht anrufen foo tausende Male hintereinander (zum Beispiel nicht in einem mousemove Handler), dann würde ich mir überhaupt keine Sorgen machen. Und beachten Sie, dass einige Engines (z. B. V8) den Code trotzdem einbetten und den Funktionsaufruf vollständig eliminieren, vorausgesetzt, dies ändert das Geschehen nicht auf eine Weise, die extern erkannt werden kann.

    – TJ Crowder

    18. April 2012 um 7:30 Uhr


  • @TJCrowder: kannst du die Antwort von robrich kommentieren? verhindert diese Lösung die Wiederherstellung von bar() bei jedem Anruf? würde auch verwenden foo.prototype.bar um die Funktion Hilfe zu definieren?

    – rkw

    18. April 2012 um 7:41 Uhr

  • @rkw: Das einmalige Erstellen der Funktion, wie es die Antwort von Robrich tut, ist eine nützliche Methode, um die Kosten für die Erstellung bei jedem Aufruf zu vermeiden. Sie verlieren die Tatsache, dass bar hat Zugriff auf die Variablen und Argumente für den Aufruf von foo (alles, woran Sie arbeiten möchten, müssen Sie bestehen), was die Dinge ein wenig verkomplizieren kann, aber in einer leistungskritischen Situation, in der Sie ein tatsächliches Problem gesehen haben, können Sie so umgestalten, um zu sehen, ob es das löst Problem. Nein, verwenden foo.prototype würde nicht wirklich helfen (zum einen bar wäre nicht mehr privat).

    – TJ Crowder

    18. April 2012 um 7:53 Uhr

  • @ahmoo: Testfall hinzugefügt. Interessanterweise erhalte ich ein anderes Ergebnis aus Guffas Testfall, ich denke, seine Funktion kann nur ein bisschen sein zu einfach. Aber ich glaube immer noch nicht, dass Leistung ein Problem sein muss.

    – TJ Crowder

    18. April 2012 um 7:59 Uhr


Dafür gibt es Schließungen.

var foo = (function () {
  function bar() {
    // perform task 3
  };

  function innerfoo (a) { 
    if (/* some cond */ ) {
      // perform task 1
      bar();
    }
    else {
      // perform task 2
      bar();
    }
  }
  return innerfoo;
})();

Innerfoo (ein Closure) enthält einen Verweis auf bar, und nur ein Verweis auf innerfoo wird von einer anonymen Funktion zurückgegeben, die nur einmal aufgerufen wird, um den Closure zu erstellen.

Die Bar ist auf diese Weise nicht von außen zugänglich.

  • Interessant. Ich habe nur begrenzten Kontakt mit Javascript, daher ist das Schließen etwas Neues für mich. Sie haben jedoch einen Ausgangspunkt für mich markiert, um Abschluss zu studieren. Vielen Dank.

    – Tamakiquadrat

    18. April 2012 um 7:39 Uhr

  • Wie oft verwenden Sie die Schließung, um mit Variablen-/Funktionsbereichen umzugehen? Wenn Sie beispielsweise 2 Funktionen haben, die auf dieselben 3 Variablen zugreifen müssen, würden Sie die 3 Variablen zusammen mit den 2 Funktionen in einer Schließung deklarieren und dann die 2 Funktionen zurückgeben?

    – doppeltOrt

    27. Juni 2018 um 13:11 Uhr

Benutzer-Avatar
robrich

var foo = (function () {
    var bar = function () {
        // perform task 3
    }
    return function (a) {

        if (/*some condition*/) {
            // perform task 1
            bar();
        }
        else {
            // perform task 2
            bar();
        }
    };
}());

Der Verschluss behält den Umfang bei bar() enthalten, die Rückgabe der neuen Funktion aus der selbstausführenden anonymen Funktion setzt einen sichtbareren Bereich um foo(). Die anonyme selbstausführende Funktion wird genau einmal ausgeführt, daher gibt es nur eine bar() Instanz und jede Ausführung von foo() werde es verwenden.

  • Interessant. Dann muss ich mal nach Verschluss suchen. Vielen Dank.

    – Tamakiquadrat

    18. April 2012 um 7:39 Uhr

  • Wie oft verwenden Sie die Schließung, um mit Variablen-/Funktionsbereichen umzugehen? Wenn Sie beispielsweise 2 Funktionen haben, die auf dieselben 3 Variablen zugreifen müssen, würden Sie die 3 Variablen zusammen mit den 2 Funktionen in einer Schließung deklarieren und dann die 2 Funktionen zurückgeben?

    – doppeltOrt

    27. Juni 2018 um 13:14 Uhr

  • Wie oft verwende ich Verschlüsse? Die ganze Zeit. Wenn ich 3 Variablen hätte, die von 2 Funktionen benötigt werden: 1. (Am besten) Übergeben Sie die 3 Variablen an beide Funktionen – die Funktionen können nur einmal definiert werden. 2. (gut) Erstellen Sie die 2 Funktionen, bei denen die Variablen außerhalb des Geltungsbereichs von beiden liegen. Dies ist eine Schließung. (Im Grunde die Antwort hier.) Leider werden die Funktionen für jede neue Verwendung der Variablen neu definiert. 3. (schlecht) Verwenden Sie keine Funktionen, sondern haben Sie nur eine große lange Methode, die beide Dinge erledigt.

    – robrich

    28. Juni 2018 um 16:37 Uhr

  • Du sagtest die Funktionen werden für jede neue Verwendung der Variablen neu definiert. Können Sie das bitte erklären? @robrich

    – Budi Salah

    25. Dezember 2021 um 19:42 Uhr

  • @BudiSalah, während du das ausführst foo -Funktion erstellt sie die bar-Funktion und die anonyme Funktion, die sie zurückgibt. Rufen Sie es ein zweites Mal auf, und Sie erhalten zwei neue Funktionen erstellt. Nennen Sie es ein Drittel, und die Funktionen werden wieder neu erstellt.

    – robrich

    29. Dezember 2021 um 4:54 Uhr

Ja, das funktioniert gut.

Die innere Funktion wird nicht jedes Mal neu erstellt, wenn Sie die äußere Funktion eingeben, aber sie wird neu zugewiesen.

Wenn Sie diesen Code testen:

function test() {

    function demo() { alert('1'); }

    demo();
    demo = function() { alert('2'); };
    demo();

}

test();
test();

wird sich zeigen 1, 2, 1, 2nicht 1, 2, 2, 2.

Ich habe ein jsperf erstellt, um verschachtelte vs. nicht verschachtelte und Funktionsausdrücke vs. Funktionsdeklarationen zu testen, und ich war überrascht zu sehen, dass die verschachtelten Testfälle 20-mal schneller ausgeführt wurden als die nicht verschachtelten. (Ich habe entweder das Gegenteil oder vernachlässigbare Unterschiede erwartet).

https://jsperf.com/nested-functions-vs-not-nested-2/1

Dies ist auf Chrome 76, macOS.

1187300cookie-checkDefinieren Sie eine Funktion innerhalb einer anderen Funktion in JavaScript

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy