7619 Stimmen

Wie funktionieren die JavaScript-Schließungen?

Wie würden Sie JavaScript-Schließungen jemandem erklären, der zwar die Konzepte kennt, aus denen sie bestehen (z. B. Funktionen, Variablen und Ähnliches), aber die Schließungen selbst nicht versteht?

Ich habe gesehen das Beispiel Schema auf Wikipedia angegeben, aber leider hat es nicht geholfen.

8voto

devlighted Punkte 201

Dies ist, wie ein Anfänger wickelte den Kopf um Closures wie eine Funktion innerhalb einer Funktion Körper auch bekannt als gewickelt ist Schließungen .

Definition aus dem Buch Speaking JavaScript "Eine Closure ist eine Funktion plus die Verbindung zu dem Bereich, in dem die Funktion erstellt wurde". Dr.Axel Rauschmayer

Wie könnte das also aussehen? Hier ist ein Beispiel

function newCounter() {
  var counter = 0;
   return function increment() {
    counter += 1;
   }
}

var counter1 = newCounter();
var counter2 = newCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3

neuerZähler schließt sich um Inkrement , Zähler kann referenziert und aufgerufen werden durch Inkrement .

Zähler1 y Zähler2 behalten ihren eigenen Wert im Auge.

Einfach, aber hoffentlich eine klare Perspektive dessen, was ein Abschluss um all diese großartigen und fortgeschrittenen Antworten herum ist.

8voto

Alexis Punkte 5421

Verschlüsse sind nicht schwer zu verstehen. Es kommt nur auf den Blickwinkel an.

Ich persönlich verwende sie gerne in Fällen des täglichen Lebens.

function createCar()
{
    var rawMaterial = [/* lots of object */];
    function transformation(rawMaterials)
    {
       /* lots of changement here */
       return transformedMaterial;
    }
    var transformedMaterial = transformation(rawMaterial);
    function assemblage(transformedMaterial)
    {
        /*Assemblage of parts*/
        return car;
    }
    return assemblage(transformedMaterial);
}

Wir müssen nur in bestimmten Fällen bestimmte Schritte durchlaufen. Was die Umwandlung von Materialien betrifft, so ist sie nur dann sinnvoll, wenn man die Teile hat.

8voto

Alireza Punkte 92209

Beginnen wir von hier aus, wie auf MDN definiert: Schließungen sind Funktionen, die sich auf unabhängige (freie) Variablen beziehen (Variablen, die lokal verwendet werden, aber in einem umschließenden Bereich definiert sind). Mit anderen Worten: Diese Funktionen "erinnern" sich an die Umgebung, in der sie erstellt wurden.

Lexikalisches Scoping
Bedenken Sie Folgendes:

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

init() erstellt eine lokale Variable namens name und eine Funktion namens displayName(). Die Funktion displayName() ist eine innere Funktion, die innerhalb von init() definiert wird und nur innerhalb des Körpers der Funktion init() verfügbar ist. Die Funktion displayName() hat keine eigenen lokalen Variablen. Da innere Funktionen jedoch Zugriff auf die Variablen äußerer Funktionen haben, kann displayName() auf den Variablennamen zugreifen, der in der übergeordneten Funktion init() deklariert ist.

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

Führen Sie den Code aus und stellen Sie fest, dass die alert()-Anweisung innerhalb der Funktion displayName() den Wert der Variablen name, die in der übergeordneten Funktion deklariert ist, erfolgreich anzeigt. Dies ist ein Beispiel für lexikalisches Scoping, das beschreibt, wie ein Parser Variablennamen auflöst, wenn Funktionen verschachtelt sind. Das Wort "lexikalisch" bezieht sich auf die Tatsache, dass das lexikalische Scoping den Ort verwendet, an dem eine Variable im Quellcode deklariert ist, um zu bestimmen, wo diese Variable verfügbar ist. Verschachtelte Funktionen haben Zugriff auf Variablen, die in ihrem äußeren Bereich deklariert sind.

Schließung
Betrachten Sie nun das folgende Beispiel:

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

Die Ausführung dieses Codes hat genau denselben Effekt wie das vorherige Beispiel der Funktion init(): Diesmal wird die Zeichenfolge "Mozilla" in einem JavaScript-Warnfeld angezeigt. Der Unterschied - und interessant - ist, dass die innere Funktion displayName() von der äußeren Funktion zurückgegeben wird, bevor sie ausgeführt wird.

Auf den ersten Blick mag es unintuitiv erscheinen, dass dieser Code noch funktioniert. In einigen Programmiersprachen existieren die lokalen Variablen innerhalb einer Funktion nur für die Dauer der Ausführung dieser Funktion. Sobald makeFunc() die Ausführung beendet hat, könnte man erwarten, dass die Variable name nicht mehr zugänglich ist. Da der Code jedoch weiterhin wie erwartet funktioniert, ist dies in JavaScript offensichtlich nicht der Fall.

Der Grund dafür ist, dass Funktionen in JavaScript Abschlüsse bilden. Eine Closure ist die Kombination aus einer Funktion und der lexikalischen Umgebung, in der diese Funktion deklariert wurde. Diese Umgebung besteht aus allen lokalen Variablen, die zu dem Zeitpunkt, als die Closure erstellt wurde, im Gültigkeitsbereich waren. In diesem Fall ist myFunc ein Verweis auf die Instanz der Funktion displayName, die bei der Ausführung von makeFunc erstellt wurde. Die Instanz von displayName verwaltet einen Verweis auf ihre lexikalische Umgebung, in der die Variable name existiert. Aus diesem Grund bleibt der Variablenname beim Aufruf von myFunc zur Verwendung verfügbar und "Mozilla" wird an alert übergeben.

Hier ist ein etwas interessanteres Beispiel - eine makeAdder-Funktion:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

In diesem Beispiel haben wir eine Funktion makeAdder(x) definiert, die ein einzelnes Argument, x, annimmt und eine neue Funktion zurückgibt. Die Funktion, die sie zurückgibt, nimmt ein einzelnes Argument, y, und gibt die Summe von x und y zurück.

Im Wesentlichen ist makeAdder eine Funktionsfabrik - sie erzeugt Funktionen, die einen bestimmten Wert zu ihrem Argument hinzufügen können. Im obigen Beispiel verwenden wir unsere Funktionsfabrik, um zwei neue Funktionen zu erstellen - eine, die zu ihrem Argument 5 addiert, und eine, die 10 addiert.

add5 und add10 sind beides Verschlüsse. Sie haben die gleiche Funktionskörperdefinition, speichern aber unterschiedliche lexikalische Umgebungen. In der lexikalischen Umgebung von add5 ist x gleich 5, während in der lexikalischen Umgebung von add10 x gleich 10 ist.

Praktische Verschlüsse

Closures sind nützlich, weil sie es ermöglichen, einige Daten (die lexikalische Umgebung) mit einer Funktion zu verknüpfen, die auf diesen Daten operiert. Dies weist offensichtliche Parallelen zur objektorientierten Programmierung auf, bei der Objekte die Möglichkeit bieten, einige Daten (die Eigenschaften des Objekts) mit einer oder mehreren Methoden zu verknüpfen.

Folglich können Sie eine Schließung überall dort verwenden, wo Sie normalerweise ein Objekt mit nur einer einzigen Methode verwenden würden.

Situationen, in denen man dies tun möchte, sind im Web besonders häufig. Ein Großteil des Codes, den wir in Front-End-JavaScript schreiben, ist ereignisbasiert - wir definieren ein bestimmtes Verhalten und hängen es dann an ein Ereignis an, das vom Benutzer ausgelöst wird (z. B. ein Klick oder ein Tastendruck). Unser Code wird im Allgemeinen als Callback angehängt: eine einzelne Funktion, die als Reaktion auf das Ereignis ausgeführt wird.

Nehmen wir zum Beispiel an, wir möchten einer Seite einige Schaltflächen hinzufügen, mit denen die Textgröße angepasst werden kann. Eine Möglichkeit, dies zu tun, besteht darin, die Schriftgröße des body-Elements in Pixeln anzugeben und dann die Größe der anderen Elemente auf der Seite (z. B. Überschriften) mit der relativen Einheit em festzulegen:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

Unsere interaktiven Schaltflächen für die Textgröße können die Eigenschaft "font-size" des body-Elements ändern, und die Anpassungen werden dank der relativen Einheiten auch von anderen Elementen auf der Seite übernommen. Hier ist das JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12, size14 und size16 sind nun Funktionen, die die Größe des Textes auf 12, 14 bzw. 16 Pixel ändern. Wir können sie wie folgt an Schaltflächen (in diesem Fall Links) anhängen:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

Weitere Informationen über Schließungen finden Sie auf der Link auf MDN

7voto

NinjaBeetle Punkte 85

Es war einmal ein Höhlenmensch

function caveman {

der einen ganz besonderen Stein hatte,

var rock = "diamond";

Man konnte den Stein nicht selbst holen, weil er sich in der privaten Höhle des Höhlenmenschen befand. Nur der Höhlenmensch wusste, wie man den Felsen findet und holt.

return {
    getRock: function() {
        return rock;
    }
};
}

Zum Glück war er ein freundlicher Höhlenmensch, und wenn man bereit war, auf seine Rückkehr zu warten, würde er sie gerne für einen holen.

var friend = caveman();
var rock = friend.getRock();

Ziemlich schlauer Höhlenmensch.

7voto

Pao Im Punkte 317

Closures in JavaScript sind mit dem Konzept der Scopes verbunden.

Vor es6 gibt es keinen Bereich auf Blockebene, sondern nur einen Bereich auf Funktionsebene in JS.

Das bedeutet, dass wir, wann immer wir einen Anwendungsbereich auf Blockebene benötigen, diesen in eine Funktion verpacken müssen.

Sehen Sie sich dieses einfache und interessante Beispiel an, wie Closure dieses Problem in ES5 löst

// let say we can only use a traditional for loop, not the forEach

for (var i = 0; i < 10; i++) {

    setTimeout(function() {
        console.log('without closure the visited index - '+ i)
    })
}

// this will print 10 times 'visited index - 10', which is not correct

/**
Expected output is 

visited index - 0
visited index - 1
.
.
.
visited index - 9

**/

// we can solve it by using closure concept 
   //by using an IIFE (Immediately Invoked Function Expression)

// --- updated code ---

for (var i = 0; i < 10; i++) {
    (function (i) {
      setTimeout(function() {
        console.log('with closure the visited index - '+ i)
      })
    })(i);
}

NB: Dieses Problem kann leicht durch die Verwendung von es6 gelöst werden. let 代わりに var , da let lexikalischen Spielraum schafft.


In einfachen Wort, Closure in JS ist nichts anderes als den Zugriff auf Funktion Umfang.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X