Was ist eine praktische Verwendung für einen Abschluss in JavaScript?


279

Ich versuche mein Bestes, um mich mit JavaScript-Verschlüssen zu beschäftigen.

Ich erhalte das durch die Rückgabe einer inneren Funktion, die Zugriff auf jede Variable hat, die in ihrem unmittelbaren übergeordneten Element definiert ist.

Wo wäre das für mich nützlich? Vielleicht habe ich es noch nicht ganz verstanden. Die meisten Beispiele, die ich online gesehen habe, enthalten keinen realen Code, sondern nur vage Beispiele.

Kann mir jemand eine reale Verwendung eines Verschlusses zeigen?

Ist das zum Beispiel?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
+1 für dein Bestes :-) Verschlüsse können anfangs sehr entmutigend wirken, ich weiß, dass sie für mich waren. Sobald Sie den Dreh raus haben, werden Sie sofort ein viel besserer Programmierer.
Andy E

7
Ich habe gerade einen Blog-Beitrag über Schließungen in JavaScript geschrieben, in dem Sie möglicherweise helfpul finden.
Skilldrick

@ Skilldrick. link is dead ... und auch ich fand dieses praktische Beispiel sehr hilfreich. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Antworten:


239

Ich habe Verschlüsse verwendet, um Dinge zu tun wie:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

Wie Sie dort sehen können, abefindet sich jetzt ein Objekt mit einer Methode publicfunction( a.publicfunction()), die aufruft privatefunctionund nur innerhalb des Abschlusses existiert. Sie können NICHTprivatefunction direkt (dh a.privatefunction()) anrufen , nur publicfunction().

Es ist ein minimales Beispiel, aber vielleicht können Sie sehen, wie es verwendet wird? Wir haben dies verwendet, um öffentliche / private Methoden durchzusetzen.


26
Ah, wenn dies ein Verschluss ist, dann habe ich Verschlüsse verwendet, ohne es zu wissen! Ich füge oft Funktionen wie diese in eine andere ein und mache dann alle benötigten öffentlich sichtbar, indem ich ein Objektliteral wie in Ihrem Beispiel zurückgebe.
alex

1
Ja, wie Sie sehen, behalten Sie den Kontext der Funktion bei, da das zurückgegebene Objekt auf Variablen (und Funktionen) verweist. Sie haben sie also benutzt, Sie haben es einfach nicht gewusst.
Francisco Soto

9
Technisch gesehen ist jede Funktion, die Sie in Javascript in einem Browser ausführen, ein Abschluss, da das Fensterobjekt daran gebunden ist.
Adam Gent

9
Ich weiß, dass dies eine alte Frage ist, aber für mich liefert dies immer noch keine angemessene Antwort. Warum nicht einfach die Funktion direkt aufrufen? Warum brauchen Sie eine private Veranstaltung?
Qodeninja

5
Denn obwohl das Beispiel nur eine Funktion hat, kann es auch Variablen enthalten, auf die von außen nicht zugegriffen werden kann. Sprich: var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 usw.
Francisco Soto

209

Angenommen, Sie möchten zählen, wie oft der Benutzer auf einer Webseite auf eine Schaltfläche geklickt hat .
Dazu lösen Sie bei onclickEreignis der Schaltfläche eine Funktion aus, um die Anzahl der Variablen zu aktualisieren

<button onclick="updateClickCount()">click me</button>  

Jetzt könnte es viele Ansätze geben wie:

1) Sie können eine globale Variable und eine Funktion verwenden, um den Zähler zu erhöhen :

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

Die Gefahr besteht jedoch darin, dass jedes Skript auf der Seite den Zähler ändern kann, ohne ihn aufzurufenupdateClickCount() .


2) Nun könnten Sie daran denken, die Variable innerhalb der Funktion zu deklarieren:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

Aber hey! Bei jedem updateClickCount()Aufruf der Funktion wird der Zähler erneut auf 1 gesetzt.


3) Denken Sie über verschachtelte Funktionen nach ?

Verschachtelte Funktionen haben Zugriff auf den Bereich "über" ihnen.
In diesem Beispiel hat die innere Funktion updateClickCount()Zugriff auf die Zählervariable in der übergeordneten FunktioncountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

Dies hätte das Gegendilemma lösen können, wenn Sie die updateClickCount()Funktion von außen erreichen könnten und auch einen Weg finden müssten, counter = 0nur einmal und nicht jedes Mal auszuführen.


4) Schließung zur Rettung! (selbstaufrufende Funktion) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

Die selbstaufrufende Funktion wird nur einmal ausgeführt. Es setzt den counterWert auf Null (0) und gibt einen Funktionsausdruck zurück.

Dieser Weg updateClickCountwird zur Funktion. Der "wunderbare" Teil ist, dass es auf den Zähler im übergeordneten Bereich zugreifen kann.

Dies wird als JavaScript-Schließung bezeichnet . Es ermöglicht einer Funktion, " private " Variablen zu haben.

Das counterist durch den Umfang der anonymen Funktion geschützt und kann nur mit der Funktion add geändert werden!

Lebhafteres Beispiel für Closure:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


Referenz: https://www.w3schools.com/js/js_function_closures.asp


49
Dies ist die erste Antwort , die ich sagen : „Oh, das ist , warum ich Verschlüsse verwenden würde!“
Devil's Advocate

6

15
Ich habe gerade die w3schools-Seite über Schließungen gelesen und bin dann hierher gekommen, um weitere Informationen zu erhalten. Dies ist das gleiche wie die w3schools Seite: w3schools.com/js/js_function_closures.asp
tyelford

1
@ JerryGoyal könntest du es mit 2 separaten Schaltflächen zum Laufen bringen? Ich kann nicht herausfinden, wie ohne auf 2 Variablen (Kopien der Funktion) zurückzugreifen, was einige der Hauptvorteile / Bequemlichkeit zu entfernen scheint.
Tyler Collier

2
Gute Antwort. Beachten Sie aber , dass eine Schließung nicht braucht eine selbst Aufruf Funktion zu sein, aber es kann sein. Wenn ein Verschluß ist selbst rufenden (dh unmittelbar durch Zugabe von () nach der Funktion genannt wird ), bedeutet dies , ist der Rückgabewert wird sofort berechnet, anstatt die Funktion zurückgeführt wird und der Rückgabewert berechnet wird später einmal die Funktion aufgerufen wird. Ein Abschluss kann tatsächlich eine beliebige Funktion innerhalb einer anderen Funktion sein, und sein Hauptmerkmal besteht darin, dass er Zugriff auf den Bereich der übergeordneten Funktion einschließlich ihrer Variablen und Methoden hat.
Chris Halcrow

69

Das Beispiel, das Sie geben, ist ausgezeichnet. Verschlüsse sind ein Abstraktionsmechanismus, mit dem Sie Bedenken sehr sauber trennen können. In Ihrem Beispiel wird die Instrumentierung (Zählen von Aufrufen) von der Semantik (eine API zur Fehlerberichterstattung) getrennt. Andere Verwendungen umfassen:

  1. Parametrisiertes Verhalten an einen Algorithmus übergeben (klassische Programmierung höherer Ordnung):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. Simulation der objektorientierten Programmierung:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. Implementierung einer exotischen Flusskontrolle wie der Ereignisbehandlung von jQuery und AJAX-APIs.


3
( int?) Als ich das letzte Mal nachgesehen habe, war JavaScript eine Sprache vom Typ Ente. Vielleicht haben Sie an Java gedacht?
Hallo71

1
@ Hello71: Ich habe an JavaScript gedacht, aber alte Gewohnheiten sterben schwer. Guter Fang.
Marcelo Cantos

2
@MarceloCantos Es sieht so aus, als hätten Sie ein Komma in der Implementierung des Zählers vergessen. Ich habe Ihren Beitrag bearbeitet, um ihn zu korrigieren. Hoffe das ist ok :)
Natan Streppel

2
@Streppel: Guter Fang! Ich freue mich sehr, dass Sie meinen Code verbessern. :-)
Marcelo Cantos

versuchen # 1 zu verstehen ... Wie würden Sie Proximity_Sort nennen?
Dave2081

26

Ich weiß, dass ich diese Frage sehr spät beantworte, aber es könnte jedem helfen, der 2018 noch nach der Antwort sucht.

Javascript-Verschlüsse können verwendet werden, um Drossel- und Entprellungsfunktionen in Ihrer Anwendung zu implementieren .

Drosselung :

Durch die Drosselung wird begrenzt, wie oft eine Funktion im Laufe der Zeit maximal aufgerufen werden kann. Wie in "Führen Sie diese Funktion höchstens einmal alle 100 Millisekunden aus."

Code:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Entprellen :

Durch das Entprellen wird eine Funktion begrenzt, die erst nach einer bestimmten Zeit wieder aufgerufen wird, ohne dass sie aufgerufen wird. Wie in "Führen Sie diese Funktion nur aus, wenn 100 Millisekunden vergangen sind, ohne dass sie aufgerufen wurde."

Code:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

Wie Sie sehen können, haben Schließungen bei der Implementierung von zwei schönen Funktionen geholfen, die jede Web-App haben sollte, um eine reibungslose Benutzeroberfläche zu bieten.

Ich hoffe es wird jemandem helfen.


18

Ja, das ist ein gutes Beispiel für einen nützlichen Verschluss. Der Aufruf von warnUser erstellt die calledCountVariable in ihrem Bereich und gibt eine anonyme Funktion zurück, die in der warnForTamperVariablen gespeichert ist. Da es immer noch einen Abschluss gibt, der die Variable " warnForTamper()CalledCount" verwendet, wird sie beim Beenden der Funktion nicht gelöscht. Daher erhöht jeder Aufruf von die Variable "Scoped" den Gültigkeitsbereich und alarmiert den Wert.

Das häufigste Problem, das ich bei StackOverflow sehe, ist, dass jemand die Verwendung einer Variablen "verzögern" möchte, die bei jeder Schleife erhöht wird. Da die Variable jedoch einen Gültigkeitsbereich hat, erfolgt jeder Verweis auf die Variable nach dem Ende der Schleife, was zu dem führt Endzustand der Variablen:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

Dies würde dazu führen, dass jede Warnung den gleichen Wert anzeigt, den Wert i, auf den sie erhöht wurde, als die Schleife endete. Die Lösung besteht darin, einen neuen Abschluss zu erstellen, einen separaten Bereich für die Variable. Dies kann mithilfe einer sofort ausgeführten anonymen Funktion erfolgen, die die Variable empfängt und ihren Status als Argument speichert:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

Interessant -1, ich denke, dies ist nicht "eine praktische Verwendung für einen Abschluss in Javascript"?
Andy E

1
Ich fand eine Verwendung beim Lesen, also habe ich vor der Abwertung eine +1 vergeben.
alex

1
@alex: danke, ich habe die positive Bewertung bemerkt. Ich habe mich hier bei SO fast an anonyme Abstimmungen gewöhnt. Es ärgert mich nur, weil ich wirklich gerne wissen möchte, ob ich etwas Ungenaues oder Falsches gesagt habe, und sie lassen Sie denken, dass Sie gerade von einem anderen Antwortenden herabgestimmt wurden, der eine bessere Sichtbarkeit für seine eigene Antwort wünscht. Zum Glück bin ich nicht der rachsüchtige Typ ;-)
Andy E

1
Ich denke, dies ist eher eine Problemumgehung für JavaScripts defekten Blockbereich. Sie sollten nur var j = i hinzufügen können; vor dem ersten setTimeout und erhalten Sie die Warnung, dieses j zu verwenden. Eine andere Möglichkeit besteht darin, 'with' wie folgt zu verwenden: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Wert von Ich war "+ i +", als dieser Timer eingestellt wurde ")}, 100);}}
Davidbuttar

1
@AndyE Lustig ist möglicherweise nicht das richtige Wort. Mir ist gerade aufgefallen, dass Menschen häufig selbstaufrufende Funktionen verwenden, um Schließungen zu erklären, wie viele Antworten auf dieser Seite. Die Rückruffunktion im setTimeout ist aber auch ein Abschluss; Dies kann als "praktische Verwendung" angesehen werden, da Sie über den Rückruf auf einige andere lokale Variablen zugreifen können. Als ich etwas über Schließungen lernte, war es für mich nützlich zu erkennen, dass Schließungen überall sind, nicht nur in Arcade-JavaScript-Mustern.
Antoine

14

Insbesondere in der JavaScript-Sprache (oder einer beliebigen ECMAScript-Sprache) sind Schließungen hilfreich, um die Implementierung von Funktionen zu verbergen und gleichzeitig die Benutzeroberfläche anzuzeigen.

Stellen Sie sich beispielsweise vor, Sie schreiben eine Klasse von Datumsdienstprogrammmethoden und möchten Benutzern ermöglichen, Wochentagsnamen nach Index zu suchen, aber nicht, dass sie das unter der Haube verwendete Namensarray ändern können.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Beachten Sie, dass das daysArray einfach als Eigenschaft des dateUtilObjekts gespeichert werden kann, dann aber für Benutzer des Skripts sichtbar ist und sie es sogar ändern können, wenn sie dies möchten, ohne dass Sie Ihren Quellcode benötigen. Da es jedoch von der anonymen Funktion eingeschlossen ist, die die Datumssuchfunktion zurückgibt, ist es nur für die Suchfunktion zugänglich, sodass es jetzt manipulationssicher ist.


2
Das mag dumm klingen, aber konnten sie nicht einfach die JavaScript-Datei selbst öffnen und Ihre Implementierung sehen?
Itsmichaelwang

1
@Zapurdead: ja, könnten sie natürlich sehen , die Umsetzung aber sie können es nicht ändern , die Umsetzung (versehentlich oder absichtlich) , ohne direkt den Quellcode zu verändern. Ich nehme an, Sie könnten es mit geschützten Mitgliedern in Java vergleichen.
Maerics

6

Es gibt einen Abschnitt über praktische Abschlüsse im Mozilla Developer Network .


Wenn ich das durchschaue, sehe ich nicht, wie "praktisch" es ist, als ob ich den ganzen return function ()...Code entferne, der immer noch gut funktioniert. Die Schließung ist nicht notwendig
ein Ohrwurm

@James_Parsons Sie konnten sie dann nicht den Ereignishandlern zuweisen, wie sie es damals im Beispiel getan haben.
Alex

5

Eine weitere häufige Verwendung für Schließungen besteht darin, thiseine Methode an ein bestimmtes Objekt zu binden , damit sie an anderer Stelle aufgerufen werden kann (z. B. als Ereignishandler).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Immer wenn ein Mausbewegungsereignis ausgelöst wird, watcher.follow(evt)wird aufgerufen.

Verschlüsse sind auch ein wesentlicher Bestandteil von Funktionen höherer Ordnung, was das sehr häufige Muster des Umschreibens mehrerer ähnlicher Funktionen als einzelne Funktion höherer Ordnung durch Parametrisierung der unterschiedlichen Teile ermöglicht. Als abstraktes Beispiel

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

wird

fooer = function (x) {
    return function (...) {A x B}
}

Dabei sind A und B keine syntaktischen Einheiten, sondern Quellcode-Zeichenfolgen (keine Zeichenfolgenliterale).

Ein konkretes Beispiel finden Sie unter " Optimieren meines Javascript mit einer Funktion ".


5

Hier habe ich einen Gruß, den ich mehrmals sagen möchte. Wenn ich einen Abschluss erstelle, kann ich diese Funktion einfach aufrufen, um die Begrüßung aufzuzeichnen. Wenn ich den Verschluss nicht erstelle, muss ich meinen Namen jedes Mal weitergeben.

Ohne Schließung ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

Mit einer Schließung ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
Ich bin nicht sicher, aber immer noch ohne Abschluss, den Sie als var grretBilly = Gruß ("Billy", "Bob") bezeichnen können. und rufe grretBilly () auf; Es würde immer noch das gleiche tun? Obwohl Sie einen Abschluss erstellen oder nicht, ist dies ein anderes Problem, aber die Namensübergabe jedes Mal ist hier kein Problem.
user2906608

4

Wenn Sie mit dem Konzept vertraut sind, eine Klasse im objektorientierten Sinne zu instanziieren (dh ein Objekt dieser Klasse zu erstellen), sind Sie dem Verständnis von Abschlüssen nahe.

Stellen Sie sich das so vor: Wenn Sie zwei Personenobjekte instanziieren, wissen Sie, dass die Klassenmitgliedsvariable "Name" nicht von Instanzen gemeinsam genutzt wird. Jedes Objekt hat seine eigene 'Kopie'. Wenn Sie einen Abschluss erstellen, wird die freie Variable (in Ihrem obigen Beispiel 'CalledCount') an die 'Instanz' der Funktion gebunden.

Ich denke, Ihr konzeptioneller Sprung wird leicht durch die Tatsache behindert, dass jede Funktion / Schließung, die von der Funktion warnUser zurückgegeben wird (abgesehen davon, dass es sich um eine Funktion höherer Ordnung handelt ), "CalledCount" mit demselben Anfangswert (0) bindet, während dies häufig beim Erstellen von Schließungen der Fall ist Es ist sinnvoller, verschiedene Initialisierer an die Funktion höherer Ordnung zu übergeben, ähnlich wie verschiedene Werte an den Konstruktor einer Klasse übergeben werden.

Angenommen, 'CalledCount' erreicht einen bestimmten Wert, und Sie möchten die Sitzung des Benutzers beenden. Je nachdem, ob die Anfrage vom lokalen Netzwerk oder vom großen schlechten Internet eingeht, möchten Sie möglicherweise unterschiedliche Werte dafür (ja, es ist ein erfundenes Beispiel). Um dies zu erreichen, können Sie verschiedene Anfangswerte für aufgerufene Anzahl an warnUser übergeben (dh -3 oder 0?).

Ein Teil des Problems mit der Literatur ist die Nomenklatur, mit der sie beschrieben werden ("lexikalischer Umfang", "freie Variablen"). Lass dich nicht täuschen, Verschlüsse sind einfacher als es scheint ... auf den ersten Blick ;-)


3

Hier habe ich ein einfaches Beispiel für ein Schließungskonzept, das wir auf unserer E-Commerce-Website oder in vielen anderen verwenden können. Ich füge meinen jsfiddle-Link zum Beispiel hinzu. Es enthält eine kleine Produktliste mit 3 Artikeln und einen Warenkorb.

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

Verwendung von Verschlüssen:

Verschlüsse sind eine der leistungsstärksten Funktionen von JavaScript. JavaScript ermöglicht das Verschachteln von Funktionen und gewährt der inneren Funktion vollen Zugriff auf alle Variablen und Funktionen, die in der äußeren Funktion definiert sind (und auf alle anderen Variablen und Funktionen, auf die die äußere Funktion Zugriff hat). Die äußere Funktion hat jedoch keinen Zugriff auf die in der inneren Funktion definierten Variablen und Funktionen. Dies bietet eine Art Sicherheit für die Variablen der inneren Funktion. Da die innere Funktion Zugriff auf den Umfang der äußeren Funktion hat, leben die in der äußeren Funktion definierten Variablen und Funktionen länger als die äußere Funktion selbst, wenn es der inneren Funktion gelingt, über das Leben der äußeren Funktion hinaus zu überleben.

Beispiel:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

Im obigen Code ist die Namensvariable der äußeren Funktion für die inneren Funktionen zugänglich, und es gibt keine andere Möglichkeit, auf die inneren Variablen zuzugreifen, als über die inneren Funktionen. Die inneren Variablen der inneren Funktion dienen als sichere Speicher für die inneren Funktionen. Sie enthalten "persistente" und dennoch sichere Daten, mit denen die inneren Funktionen arbeiten können. Die Funktionen müssen nicht einmal einer Variablen zugewiesen sein oder einen Namen haben. Lesen Sie hier für Details


2

Ich mag Mozillas Funktionsfactory- Beispiel .

function makeAdder(x) {

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

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
Dies ist die Art von Beispiel, die den Leuten meiner Meinung nach nicht hilft, Verschlüsse zu verstehen oder wofür sie gut sind. Wie oft haben Sie jemals einen Abschluss geschrieben, um eine Funktion zum Hinzufügen von Zahlen zurückzugeben, außer als Beispiel?
Mohamad

2

Das JavaScript-Modulmuster verwendet Verschlüsse. Sein schönes Muster ermöglicht es Ihnen, etwas Ähnliches wie "öffentliche" und "private" Vars zu haben.

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

Dieser Thread hat mir sehr geholfen, besser zu verstehen, wie Verschlüsse funktionieren. Ich habe seitdem selbst experimentiert und mir diesen ziemlich einfachen Code ausgedacht, der einigen anderen Leuten helfen kann, zu sehen, wie Abschlüsse auf praktische Weise verwendet werden können und wie der Abschluss auf verschiedenen Ebenen verwendet wird, um statische und ähnliche Variablen beizubehalten / oder globale Variablen, ohne dass das Risiko besteht, dass sie überschrieben oder mit globalen Variablen verwechselt werden. Auf diese Weise werden die Schaltflächenklicks sowohl auf lokaler Ebene für jede einzelne Schaltfläche als auch auf globaler Ebene verfolgt, wobei jeder Schaltflächenklick gezählt wird, was zu einer einzelnen Zahl beiträgt. Hinweis: Ich habe dazu keine globalen Variablen verwendet. Dies ist der eigentliche Punkt der Übung. Ich habe einen Handler, der auf jede Schaltfläche angewendet werden kann, die auch global zu etwas beiträgt.

Bitte Experten, lassen Sie mich wissen, wenn ich hier schlechte Praktiken begangen habe! Ich lerne dieses Zeug immer noch selbst.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

Referenz: Praktische Verwendung von Verschlüssen

In der Praxis können Verschlüsse elegante Designs erstellen, die die Anpassung verschiedener Berechnungen, verzögerter Anrufe, Rückrufe, die Erstellung eines gekapselten Bereichs usw. ermöglichen.

Ein Beispiel für die Sortiermethode von Arrays, die die Sortierbedingungsfunktion als Argument akzeptiert:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

Zuordnen von Funktionalen als Zuordnungsmethode von Arrays, die ein neues Array anhand der Bedingung des Funktionsarguments abbildet:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

Oft ist es zweckmäßig, Suchfunktionen mit funktionalen Argumenten zu implementieren, die nahezu unbegrenzte Suchbedingungen definieren:

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

Wir können auch feststellen, dass das Anwenden von Funktionalen beispielsweise eine forEach-Methode ist, die eine Funktion auf ein Array von Elementen anwendet:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

Eine Funktion wird auf Argumente angewendet (auf eine Liste von Argumenten - in apply und auf positionierte Argumente - in call):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Zurückgestellte Anrufe:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

Rückruffunktionen:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Erstellung eines gekapselten Bereichs zum Ausblenden von Hilfsobjekten:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

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 Rückruf angehängt: eine einzelne Funktion, die als Reaktion auf das Ereignis ausgeführt wird. Größe 12, Größe 14 und Größe 16 sind jetzt Funktionen, mit denen der Textkörper auf 12, 14 bzw. 16 Pixel verkleinert wird. Wir können sie wie folgt an Schaltflächen (in diesem Fall Links) anhängen:

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;

Geige


Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
Donald Duck

1
Dieses Beispiel könnte meines Erachtens ohne Abschluss über eine Standardfunktion implementiert werden. Ich versuche ein Beispiel für etwas zu finden, das ohne Abschluss NICHT implementiert werden KÖNNTE
Zach Smith

0

Verschlüsse sind eine nützliche Methode zum Erstellen , eine Sequenz, die bei Bedarf erhöht wird:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

Die Unterschiede sind wie folgt zusammengefasst:

Anonyme Funktionen Definierte Funktionen

Kann nicht als Methode verwendet werden Kann als Methode eines Objekts verwendet werden

Existiert nur in dem Bereich, in dem es definiert ist. Existiert in dem Objekt, in dem es definiert ist

Kann nur in dem Bereich aufgerufen werden, in dem es definiert ist. Kann an jeder Stelle im Code aufgerufen werden

Kann einem neuen Wert zugewiesen oder gelöscht werden. Kann nicht gelöscht oder geändert werden

Verweise


0

Im angegebenen Beispiel ist der Wert der beiliegenden Variablen 'counter' geschützt und kann nur mit den angegebenen Funktionen (Inkrementieren, Dekrementieren) geändert werden. weil es in einem Verschluss ist,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

Erklären der praktischen Verwendung für einen Abschluss in JavaScript ---

Wenn wir eine Funktion innerhalb einer anderen Funktion erstellen, erstellen wir einen Abschluss. Verschlüsse sind leistungsstark, da sie die Daten ihrer äußeren Funktionen lesen und bearbeiten können. Immer wenn eine Funktion aufgerufen wird, wird für diesen Aufruf ein neuer Bereich erstellt. Die in der Funktion deklarierte lokale Variable gehört zu diesem Bereich und kann nur von dieser Funktion aus aufgerufen werden. Wenn die Funktion die Ausführung beendet hat, wird der Bereich normalerweise zerstört.

Ein einfaches Beispiel für eine solche Funktion ist:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

Im obigen Beispiel deklariert die Funktion buildName () eine Begrüßung einer lokalen Variablen und gibt sie zurück. Jeder Funktionsaufruf erstellt einen neuen Bereich mit einer neuen lokalen Variablen. Nachdem die Funktion ausgeführt wurde, können wir nicht mehr auf diesen Bereich verweisen, sodass der Müll gesammelt wird.

Aber wie wäre es, wenn wir einen Link zu diesem Bereich haben?

Schauen wir uns die nächste Funktion an:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

Die Funktion sayName () aus diesem Beispiel ist ein Abschluss. Die Funktion sayName () verfügt über einen eigenen lokalen Bereich (mit variabler Begrüßung) und hat auch Zugriff auf den Bereich der äußeren (umschließenden) Funktion. In diesem Fall die Variable Begrüßung von buildName ().

Nachdem die Ausführung von buildName abgeschlossen ist, wird der Bereich in diesem Fall nicht zerstört. Die Funktion sayMyName () hat weiterhin Zugriff darauf, sodass kein Müll gesammelt wird. Es gibt jedoch keine andere Möglichkeit, auf Daten aus dem äußeren Bereich zuzugreifen, als das Schließen. Der Abschluss dient als Tor zwischen dem globalen Kontext und dem äußeren Bereich.


0

Ich versuche, Clousures zu lernen, und ich denke, ein Beispiel, das ich erstellt habe, ist ein praktischer Anwendungsfall. Sie können Snippet ausführen und das Ergebnis in der Konsole anzeigen. Wir haben zwei separate Benutzer, die separate Daten haben. Jeder von ihnen kann den aktuellen Status anzeigen und aktualisieren.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.