Ich versuche, meinen Kopf um Verschlüsse in Javascript zu wickeln.
Hier ist ein Beispiel aus einem Tutorial:
%Vor%Der Autor hat gesagt, dass dies eine effektive Methode ist, um Closure zu benutzen, um private Variablen zu erstellen, aber ich verstehe das nicht.
Könnte jemand die Vorteile einer solchen Kodierung aufklären?
Ein closure ist ein Paar von einer Funktion und der Umgebung , in der es definiert wurde (unter der Annahme, dass lexic scoping , welches JavaScript verwendet). So kann die Funktion einer Schließung auf Variablen in ihrer Umgebung zugreifen; Wenn keine andere Funktion Zugriff auf diese Umgebung hat, sind alle darin enthaltenen Variablen effektiv privat und nur über die Funktion closure zugänglich.
Das von Ihnen bereitgestellte Beispiel zeigt dies recht gut. Ich habe Inline-Kommentare hinzugefügt, um die Umgebungen zu erklären.
%Vor% Wenn var bobGreeter = greeter("Bob", 47);
ausgeführt wird, wird eine neue Schließung erstellt. Das heißt, Sie haben nun eine neue Funktionsinstanz zusammen mit der Umgebung, in der sie erstellt wurde. Daher hat Ihre neue Funktion einen Verweis auf die Variablen 'message' in dieser Umgebung, obwohl niemand sonst.
Zusätzliche Lektüre: SICP Ch 3.2 . Obwohl es sich auf Scheme konzentriert, sind die Ideen die gleichen. Wenn Sie dieses Kapitel gut verstehen, haben Sie eine gute Grundlage dafür, wie Umgebungen und lexikalisches Scoping funktionieren.
Mozilla hat auch eine Seite, die Verschlusssachen erklärt.
Der Zweck einer Schließung ist, dass die Variablen, die Sie innerhalb einer bestimmten Funktion verwenden, garantiert "geschlossen" sind, was bedeutet, dass sie nicht von externen Variablen abhängen - sie hängen nur davon ab und verwenden ihre Argumente. Dies macht Ihre Javascript-Methoden näher an eine reine Funktion , dh eine, die denselben Wert für die gleichen Argumente zurückgibt.
Ohne Verwendung von Verschlüssen sind Ihre Funktionen wie Schweizer Käse, sie werden Löcher haben. Ein Verschluss verstopft diese Löcher, so dass die Methode nicht von Variablen abhängt, die höher in der Umfangskette sind.
Nun, bis zu diesem Punkt bestand meine Antwort einfach darin, Ihren Code und Ihren Stil zu organisieren. Also nimm dieses einfache Beispiel. In der Zeile mit dem Kommentar rufe ich eine Funktion auf und der Wert der Variablen a
wird zur zukünftigen Verwendung erfasst.
Nun, warum sollten Sie das tun? Nun, hier ist ein praktisches Beispiel. Stellen Sie sich den folgenden Code vor, der jQuery zum Hinzufügen von 5 Links zum Dokument verwendet. Wenn Sie auf einen Link klicken, würden Sie erwarten, dass er alert
der mit dem Link verknüpften Nummer entspricht. Klicken Sie also auf den ersten Link, von dem Sie annehmen würden, dass er auf 0 aufmerksam macht, und so weiter. Aber, ist dies nicht der Fall, jeder Link wird alert
den Wert von 5. Dies liegt daran, dass die von mir definierte Funktion von der Variable i
abhängt, die außerhalb des Kontexts der Funktion geändert wird . Die Funktion, die ich in bind
übergebe, ist eine Schweizer Käse-Funktion.
Lasst uns das nun beheben, indem wir eine Schließung erstellen, so dass jeder Link alert
seine korrekte Nummer hat.
Ich denke nicht, dass dies ein gutes Beispiel für private Variablen ist, weil es keine echten Variablen gibt. Der schließende Teil ist, dass die Funktion greet
message
sehen kann (was nach außen nicht sichtbar ist, also privat), aber sie (oder jemand anders) ändert sie nicht, sie ist also eher eine Konstante.
Wie wäre es stattdessen mit dem folgenden Beispiel?
%Vor%Ein besseres Beispiel könnte
sein %Vor%Hier fügen Sie jedes Mal, wenn Sie die zurückgegebene Funktion aufrufen, 1 hinzu. Die Interna sind gekapselt.
Die zurückgegebene Funktion hat weiterhin Zugriff auf ihre übergeordneten Variablen (in diesem Fall start
und increment
).
Auf einer niedrigeren Ebene des Denkens denke ich, dass der Stapel der Funktion nicht zerstört wird, wenn er zurückkehrt.
Sobald Sie es verstanden haben, werden Sie sich fragen, warum Sie so lange gebraucht haben, um es zu verstehen. So habe ich mich jedenfalls gefühlt.
Ich denke, Funktionsumfang in Javascript kann ziemlich prägnant ausgedrückt werden.
Der Funktionskörper hat Zugriff auf alle Variablen, die in der lexikalischen Umgebung der Funktionsdeklaration sichtbar waren, sowie auf alle Variablen, die über den Aufruf der Funktion erstellt wurden - dh alle lokal deklarierten Variablen, die als Argumente übergeben oder anderweitig bereitgestellt wurden durch die Sprache (wie
this
oderarguments
).
Es heißt "closures", weil sie um freie Variablen "geschlossen" sind, und es gibt viel mehr Möglichkeiten, es zu verwenden, als nur den Status zu verbergen. Zum Beispiel werden in der funktionalen Programmierung, wo Schließungen herkommen, diese oft verwendet, um die Anzahl von Parametern zu reduzieren oder um eine Konstante für eine Funktion zu setzen. Angenommen, Sie benötigen die Funktion goodEnough()
, die prüft, ob ein Ergebnis besser ist als ein bestimmter Schwellenwert. Sie können die Funktion von 2 Variablen verwenden - Ergebnis und Schwelle. Aber Sie können auch Ihre konstante innere Funktion einschließen:
Mit Verschlüssen können Sie auch alle Tricks mit Funktionen wie ihrer Zusammensetzung verwenden:
%Vor%Weitere Informationen zum Design und zur Verwendung von Schließungen finden Sie unter SICP .
Dies ist eine grundlegende Funktionsanweisung in Javascript, die ein Array von [10,20,30]
zurückgibt %Vor%Dies wird 10, 20, 30 sequentiell drucken, während die Schleife iteriert, aber ein Array von [undefiniert, undefiniert, undefiniert] zurückgibt, der Hauptgrund ist, dass wir nicht den tatsächlichen Wert von i drücken, sondern nur drucken out, daher wird die JavaScript Engine bei jeder Iteration auf undefined setzen.
%Vor%Das ist etwas schwierig, was erwarten Sie von der Ausgabe, wird es [10,20,30] sein? Die Antworten sind nein, lasst uns sehen, wie das passiert. Zuerst wird ein globaler Ausführungskontext erstellt, wenn wir dn erstellen, außerdem haben wir die generatecash () Funktion. Jetzt sehen wir, dass die for-Schleife bei der Iteration drei anonyme Funktionsobjekte erzeugt. Es könnte verlockend sein zu denken, dass die console.log innerhalb der Push-Funktion ebenfalls ausgelöst wird, in Wirklichkeit aber nicht. Wir haben generateCash () aufgerufen, also erzeugt die Push-Funktion nur drei anonyme Funktionsobjekte, sie löst die Funktion nicht aus. Am Ende der Iteration wird der aktuelle lokale Kontext aus dem Ausführungsstapel ausgegeben und der Zustand von i: 40 und arr: [functionobj0 (), functionob1 (), functionobj2 ()].
verlassenWenn wir also mit der Ausführung der letzten drei Anweisungen beginnen, geben alle 40 aus, da es nicht möglich ist, den Wert von i aus dem aktuellen Gültigkeitsbereich zu erhalten, geht es die Gültigkeitsbereichskette hinauf und findet, dass der Wert von i gewesen ist Setzen Sie den Wert auf 40. Der Grund dafür, dass alle 40 ausgelöst werden, liegt darin, dass jede einzelne Komponente von dn im selben Ausführungskontext liegt und alle davon nicht in der Lage sind, den Wert von i in ihrem aktuellen Umfang zu finden finde ich als 40 und gebe es entsprechend aus
Tags und Links javascript closures