Sprechen wir über den Ursprung der Schließung
Funktion a () {var i = 0; Funktion b () {console.log (i);} return b;} var c = a (); c ();Wenn eine anonyme Funktion in einer Funktion eine eigene Variable verwendet und die anonyme Funktion zurückgegeben wird, wird im Allgemeinen ein Verschluss erstellt, z. B. den obigen Code.
Zu diesem Zeitpunkt werde ich auch dann existieren und nicht verschwinden. Wenn a definiert ist, setzt der JS -Interpreter die Funktionsumfangskette von A auf die Umgebung, in der sie definiert ist. Wenn a ausgeführt wird, wird A in die entsprechende Ausführungsumgebung eingeben. Erst wenn die Ausführungsumgebung erstellt wurde, wird es ein Bereich des Bereichsbereichs geben, das dann ein aktives Objekt erstellt und anschließend auf die Oberseite der Bereichskette festgelegt wird.
Jetzt hat die Scope -Kette von A das aktive Objekt von A und Fenster
Fügen Sie dann dem aktiven Objekt Argumenteattribut hinzu
Zu diesem Zeitpunkt wird der Hinweis auf die Rückgabefunktion B von A an c gegeben. Die Scope -Kette von B enthält die Referenz für aktive Objekte von A, sodass C auf das aktive Objekt von a zugreifen kann. Zu diesem Zeitpunkt wird nach der Rückkehr kein GC sein
Das obige ist eine kurze Einführung in Schließungen. Wenn Sie zu viel sagen, ist es einfach, sich fortzubewegen. Lassen Sie es hier kurz beenden und dann in die eigentliche Szene eingeben, um sie zu erklären.
Tatsächliche Szene
Kollegen Zweifel
Ein Kollege von mir bat mich, einen Code zu lesen:
var user = function (opts) {var scope = this; für (var k in opts) {scope ['get' + k] = function () {return opts [k];}; scope ['set' + k] = function (v) {return opts [k] = v;};}}}};Der Code ist sehr einfach, und ich hoffe, eine GET/SET -Methode für das eingehende Objekt zu generieren, aber er hat hier auf ein Schließungsproblem gestoßen:
Der Grund für dieses Problem ist, dass das K, das im Renditewert intern verwendet wird, immer "Alter" ist. Dieser K ist auf das aktive Objekt zurückzuführen, das von der GETXXX -Funktion gemeinsam genutzt wird. Daher ist es relativ einfach, es hier zu ändern.
var user = function (opts) {var scope = this; für (var k in opts) {(function (k) {scope ['get' + k] = function () {return opts [k];}; Scope ['set' + k] = function (v) {return opts [k] = v; 11});Erstellen Sie eine sofortige Ausführungsfunktion in der für Schleife und geben Sie k ein. Zu diesem Zeitpunkt teilt die GETXXX -Funktion das "K" jeder anonymen Funktion.
Generieren Sie eine eindeutige ID
Die Erzeugung einer eindeutigen ID ist auch eine klassische Möglichkeit, Schließungen zu verwenden.
Funktion getuUid () {var id = 0; return function () {return ++ id;}} var uUid = getuUid ();Dieser Code ist eigentlich sehr aussagekräftig. Wir führen uUid () ständig im Browser aus und erhalten tatsächlich unterschiedliche Werte, aber wenn wir nur GetuUid () () verwenden, wird der Wert jedes Mal immer noch der gleiche sein.
Der Grund für dieses Problem ist, dass wir das Ergebnis nach der Ausführung von Getuuid an UUID zuweisen. Zu diesem Zeitpunkt speichert UUID den Hinweis auf die anonyme Funktion, und die anonyme Funktion speichert das aktive Objekt von GetuUid, sodass die ID nicht zerstört wurde.
Wenn Sie es direkt aufrufen, wird das aktive Objekt jedes Mal regeneriert, sodass die ID nicht gespeichert werden kann.
Ein interessantes Stück Code
Util.tryurl = function (url) {var iframe = document.createelement ('iframe'); iframe.height = 1; iframe.width = 1; iframe.frameborder = 0; iframe.style.position = 'absolute'; '-9999px'; document.body.AppendChild (iframe); utry.ryurl = function (url) {iframe.src = url;}; u.ryurl (url);};Dieser Code ist sehr interessant. Wenn wir es zum ersten Mal nennen, erstellen wir ein Iframe -Objekt, und das Iframe -Objekt existiert beim zweiten Mal. Wir werden den Code hier vereinfachen.
var getuUid = function () {var i = 0; getuUid = function () {return i ++;}; return getuUid ();};Nach dieser Anpassung gibt es tatsächlich keine Rückkehrfunktion, aber wir bilden immer noch einen Verschluss
Ereignisdelegation und Schließung
Wir alle wissen, dass JQuery's on Event -Delegation verwendet, aber es erfordert immer noch große Anstrengungen, um wirklich zu verstehen, was Ereignisdelegation ist. Versuchen wir es also hier
Schließungen sind der Eckpfeiler der Implementierung der Ereignisdelegation. Schließlich beenden wir die heutige Schließstudie mit Event -Delegation.
Fügen Sie unserer Seite der folgenden DOM -Struktur hinzu
<input id = "input" value = "input" type = "button"/> <div id = "div"> Ich bin div </div> <span id = "span"> Ich bin span </span> <div id = "Wraper"> <Eingabe id = "Inner" value = "i am innere" type "type"/> </div>
Wenn wir Zepto verwenden, verwenden wir die folgende Methode, um Ereignisse zu binden
$ .on ('Click', 'Selector', Fn)Wir haben hier kein Zepto, implementieren Sie es einfach einfach alleine
Das Prinzip der Ereignisdelegation
Zunächst ist der Eckpfeiler der Implementierung von Event -Delegierten Ereignisblasen. Jeder Klick auf die Seite sprudelt schließlich zu seinem übergeordneten Element, sodass wir alle Ereignisse im Dokument fangen können.
Nachdem wir dieses Problem kennen, können wir selbst eine einfache Delegate -Ereignis -Bindungsmethode implementieren:
Funktionsdelegate (Selector, Typ, fn) {document.adDeVentListener (Typ, fn, false);} delegate ('#input', 'click', function () {console.log ('ttt');});Dieser Code ist die einfachste Implementierung. Zunächst werden wir das Klickereignis ausführen, egal wo wir auf die Seite klicken. Natürlich ist dies offensichtlich nicht die Situation, die wir sehen möchten. Daher führen wir die Verarbeitung durch, um das Ereignis auszulösen, das er beim Klicken haben sollte.
Hier sind ein paar ernsthaftere Fragen:
① Woher weiß ich, welches Element ich jetzt an das Dokument gebunden ist, da ich jetzt klicke?
② Auch wenn ich das aktuelle Klickelement basierend auf E.Target erhalten kann, woher weiß ich, welches Element das Ereignis hat?
③ Auch wenn ich bestimmen kann, welches Element des aktuellen Klicks das Ereignis basierend auf dem Selektor ausführen muss, wie kann ich herausfinden, welches Ereignis es ist?
Wenn die oben genannten Probleme gelöst werden können, ist unser späterer Prozess relativ einfach
Bestimmen Sie, ob das Klickelement das Ereignis auslöst
Wenn wir klicken, können wir zuerst E.Target verwenden, um das aktuelle Klickelement zu erhalten, und dann nach dem Selektor nach seiner übergeordneten DOM zu suchen. Wenn es gefunden wird, sollte das Ereignis ausgelöst werden.
Da diese erst bei Auslöser entschieden werden können, müssen wir die FN -Rückruffunktion umschreiben, sodass nach einer einfachen Operation:
var arr = []; var Slice = arr.lice; var extend = function (src, obj) {var o = {}; für (var k in src) {o [k] = src [k];} für (var k) {o [k] = obj [k]; = Funktion (e) {// Element, die durch den Selektor var selectorel = document.querySelector (Selektor) gefunden wurde; // Das aktuelle Klick -Element var el = e.target; // Bestimmen Sie, ob das vom Selektor gefundene Element das aktuelle Klickelement enthält. If it is included, the event should be triggered/*************************** Note that this is just a simple implementation, and there will be many judgments in actual applications**********************/if (selectorEl.contains(el)) {var evt = extend(e, { currentTarget: selectorEl });evt = [evt] .concat (Slice.call (Argumente, 1)); Callback.Apply (selectorel, evt); varSo können wir den Anruf erweitern:
delegate ('#input', 'click', function () {console.log ('input');}); delegate ('#div', 'click', function () {console.log ('div');}); Delegate ('#Wrapper', 'klick', function () {console.log ('click'; {console.log ('span');}); delegate ('#inner', 'click', function () {console.log ('inner');});Lassen Sie uns kurz das gesamte Programm analysieren
① Wir rufen Delegierte an, um dem Körper Ereignisse hinzuzufügen
② Bei der Bindung haben wir die Incallbacks darin umgeschrieben.
③ Beim Klicken (die Anzahl, mit der das Bindungsereignis tatsächlich ein Klick auslöst) wird das aktuelle Element erhalten, um zu überprüfen, ob das von seinem Selektor durchsuchte Element es enthält. Wenn es es enthält, wird das Ereignis ausgelöst.
④ Da jedes Mal, wenn Sie sich hier registrieren, eine Schließung gebildet wird, wird der eingehende Rückruf beibehalten, sodass Sie jedes Mal, wenn Sie ihn anrufen, Ihre eigene Rückruffunktion finden können (es ist sehr hilfreich, um die Schließung hier zu verstehen).
⑤ Schreiben Sie schließlich das aktuelle Target des Ereignisgriffs neu, so
PS: Ich habe Probleme mit der Implementierung hier, wie der Verarbeitung des Ereignisses, aber als Demo werde ich nicht darauf achten. Interessierte Freunde können sich die Zepto -Implementierung selbst ansehen.
Fragen der Ereignisdelegation
Die Ereignisdelegation kann die Effizienz verbessern, aber eine nervigere Sache ist, dass es nutzlos ist, Blasen zu stoppen.
Nehmen Sie den obigen Code als Beispiel. Es gibt ein inneres Element und ein Wrapper -Element. Sie wickeln sich gegenseitig die Beziehung.
Seine Ausführungsreihenfolge ist jedoch nicht die Reihenfolge der Ereignisblasen innen und außen, da alle Ereignisse an das Dokument gebunden sind
Hier ist eine Frage: Wie man "Blasen stoppen"
Abschluss der Ausführung im Inneren
E.StopimmediatePropagation ()
Es kann den Zweck erreichen, aber es erfordert immer noch, dass das innere Element vorher registriert werden muss
Darüber hinaus ist nur ein Ereignis an solche verschachtelten Elemente gebunden, und E.Target entscheidet, welches Ereignis ausführen soll. Für Einzelheiten denken Sie bitte selbst.
Die oben genannten Probleme können bei der Verwendung des Rückgrats tatsächlich auftreten