Heim > Artikel > Web-Frontend > Abschlüsse in Javascript verstehen
Inhalt lesen
Was ist ein Abschluss?
Eigenschaften von Verschlüssen
Funktion von Verschlüssen:
Codebeispiele für Verschlüsse
Hinweise
Zusammenfassung
Verschluss ist ein wichtiges Konzept in JavaScript und eine Technologie, die in der täglichen Arbeit häufig verwendet wird. Lassen Sie uns eine kleine Zusammenfassung davon geben
Was ist ein Verschluss?
Offizielle Aussage:
Ein Abschluss bezieht sich auf eine Funktion, die Zugriff auf eine Variable im Gültigkeitsbereich einer anderen Funktion hat. Eine übliche Methode zum Erstellen eines Abschlusses besteht darin, eine weitere Funktion innerhalb einer Funktion zu erstellen und über eine andere Funktion auf die lokalen Variablen dieser Funktion zuzugreifen
Das Folgende ist ein einfacher Abschluss:
function A(){ var text="hello world"; function B(){ console.log(text); } return B; } var c=A(); c(); // hello world
Gemäß der wörtlichen Bedeutung: Funktion B hat das Recht, auf die Variable (Text) im Bereich von Funktion A zuzugreifen und über eine andere Funktion C auf den lokalen Variablentext dieser Funktion zuzugreifen. Daher bildet Funktion B einen Abschluss. Man kann auch sagen, dass C ein Abschluss ist, da C tatsächlich die Funktion B ausführt.
Es ist zu beachten, dass es keine Reaktion gibt, wenn A(); direkt ausgeführt wird. Weil return B nicht ausgeführt wird, es sei denn, es ist return B();
Merkmale des Abschlusses
Der Abschluss hat drei Merkmale:
1. Funktion verschachtelte Funktion
2. Externe Parameter und Variablen können innerhalb der Funktion referenziert werden
3. Parameter und Variablen werden vom Garbage-Collection-Mechanismus nicht recycelt
Erklären Sie Punkt 3, warum die Parameter von Abschlüssen und Will Variablen werden nicht vom Garbage-Collection-Mechanismus recycelt?
Lassen Sie uns zunächst das Prinzip der Garbage Collection in Javascript verstehen:
(1) Wenn in Javascript nicht mehr auf ein Objekt verwiesen wird, ist das Objekt GC (Garbage Collection). ) Recycling;
(2) Wenn zwei Objekte aufeinander verweisen und nicht mehr von einer dritten Partei referenziert werden, werden auch die beiden Objekte, die aufeinander verweisen, recycelt.
Im obigen Beispielcode ist A die übergeordnete Funktion von B und B wird einer globalen Variablen C zugewiesen (der Lebenszyklus der globalen Variablen endet erst, wenn der Browser die Seite entlädt), was dazu führt B muss immer im Speicher sein, und die Existenz von B hängt von A ab, sodass A immer im Speicher ist und nach Abschluss des Aufrufs nicht vom Garbage-Collection-Mechanismus recycelt wird.
Die Rolle des Verschlusses:
Tatsächlich wird die Rolle des Verschlusses auch durch die Merkmale des Verschlusses bestimmt. Gemäß den oben genannten Verschlussmerkmalen ist die Rolle des Verschlusses wie folgt:
1. Sie können die Variablen innerhalb der Funktion lesen, anstatt globale Variablen zu definieren, um eine Umweltverschmutzung zu vermeiden
2. Behalten Sie die Werte dieser Variablen im Speicher.
Codebeispiele für Abschlüsse
Im Folgenden werden hauptsächlich einige gängige Abschlüsse vorgestellt und analysiert:
demo1 Akkumulation lokaler Variablen.
function countFn(){ var count=1; return function(){ //函数嵌套函数 count++; console.log(count); } } var y = countFn(); //外部函数赋给变量y; y(); //2 //y函数调用一次,结果为2,相当于countFn()() y(); //3 //y函数调用第二次,结果为3,因为上一次调用的count还保存在内存中,没有被销毁,所以实现了累加 y=null; //垃圾回收,释放内存 y(); // y is not a function
Da die erste Ausführung abgeschlossen ist, wird die Variablenanzahl weiterhin im Speicher gespeichert, sodass sie nicht recycelt wird, sodass der letzte Wert während der zweiten aktualisiert werden kann Ausführung. Einfach zusammenzählen. Wenn y=null eingeführt wird, wird die Referenz zerstört und der Speicher freigegeben
demo2 verwendet Abschlüsse in der Schleife
Der Code lautet wie folgt (drei Codebeispiele unten): Unser Zweck ist es Verwenden Sie Abschlüsse in jeder Schleife. Rufen Sie die Schleifensequenznummer auf:
demo2-1
for (var i = 0; i < 10; i++) { var a = function(){ console.log(i) } a() //依次为0--9 }
Das Ergebnis dieses Beispiels ist unbestreitbar, wir haben 0-9 ausgedruckt
Jede Schicht anonymer Funktionen und Variablen bildet einen Abschluss, aber in der Schleife gibt es kein Problem, da die Funktion sofort im Schleifenkörper ausgeführt wird
demo2-2
Aber in setTimeout ist das anders
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); //10次10 }, 1000); }
Was wir erwarten, ist, 0-10 nacheinander auszudrucken, aber die tatsächliche Situation ist, 10 10 Mal auszudrucken. Auch wenn die setTimeout-Zeit auf 0 geändert wird, werden 10 10 Sekunden gedruckt. Warum ist das so?
Dies liegt am Mechanismus von setTimeout. setTimeout startet die Zeitmessung, wenn die Aufgabenwarteschlange endet. Hier ist die Aufgabenwarteschlange eine eigene Schleife.
setTimeout beginnt nicht mit der Zeitmessung, bis die Schleife endet, also ist das i in setTimeout auf jeden Fall das i der letzten Schleife. In diesem Code ist das letzte i 10, also werden 10 10s gedruckt
Aus diesem Grund nimmt der Rückruf von setTimeout nicht bei jeder Schleife den Wert an, sondern den letzten Wert
demo2-3
Lösen Sie das Problem, dass das obige setTimeout die Schleife nicht nacheinander ausdrucken kann
for(var i=0;i<10;i++){ var a=function(e){ return function(){ console.log(e); //依次输入0--9 } } setTimeout(a(i),0); }
Da der erste Parameter von setTimeout eine Funktion erfordert, wird er zurückgegeben one Die Funktion gibt es aus, und bei der Rückgabe wird i als Parameter übergeben und i über den formalen Parameter e zwischengespeichert. Mit anderen Worten, die e-Variable entspricht einer Kopie von i und wird in die zurückgegebene Funktion übernommen .
Wenn setTimeout ausgeführt wird, hat es einen Verweis auf e und dieser Wert wird durch die Schleife nicht geändert.
kann auch wie folgt geschrieben werden, ähnlich wie oben:
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); //依次打印出0-9 }, 0); })(i); }
demo3 Ereignisse in der Schleife hinzufügen
Sehen Sie sich a an typische Demo unten.
Wir hoffen, dass jedes Mal, wenn wir auf li klicken, der Indexwert von li benachrichtigt wird, daher verwenden wir den folgenden Code:
<ul id="test"> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes = document.getElementsByTagName("li"); for(i = 0,len=nodes.length;i<len;i++){ nodes[i].onclick = function(){ alert(i); //值全是4 }; }
Es schlägt fehl, egal auf welches li geklickt wird, alle sind alarm(4), das heißt, sie sind alle Indexwerte, nachdem die Alarmschleife endet. Warum ist das so?
Das liegt daran, dass Ereignisse an verschiedene Elemente in der Schleife gebunden sind. Wenn in der Ereignis-Callback-Funktion eine Variable aufgerufen wird, die sich auf die Schleife bezieht, nimmt diese Variable den letzten Wert der Schleife an.
由于绑定的回调函数是一个匿名函数,所以上面的代码中, 这个匿名函数是一个闭包,携带的作用域为外层作用域(也就是for里面的作用域),当事件触发的时候,作用域中的变量已经随着循环走到最后了。
还有一点就是,事件是需要触发的,而绝大多数情况下,触发的时候循环已经结束了,所以循环相关的变量就是最后一次的取值。
要实现点击li,alert出li的索引值,需要将上面的代码进行以下的修改:
<ul id="test"> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes=document.getElementsByTagName("li"); for(var i=0;i<nodes.length;i++){ (function(e){ nodes[i].onclick=function(){ alert(e); }; })(i) }
解决思路: 增加若干个对应的闭包域空间(这里采用的是匿名函数),专门用来存储原先需要引用的内容(下标)。
当立即执行函数执行的时候,e 值不会被销毁,因为它的里面有个匿名函数(也可以说是因为闭包的存在,所以变量不会被销毁)。执行后,e 值 与全局变量 i 的联系就切断了,
也就是说,执行的时候,传进的 i 是多少,立即执行函数的 e 就是多少,但是 e 值不会消失,因为匿名函数的存在。
也可以用下面的解法,原理是一样的:
<ul id="test"> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes=document.getElementsByTagName('li'); for(var i = 0; i<nodes.length;i++){ (function(){ var temp = i; nodes[i].onclick = function () { alert(temp); } })(); }
注意事项
1、造成内存泄露
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,所以只有在绝对必要时再考虑使用闭包。
2、在闭包中使用this也可能会导致一些问题。
其实我们的目的是想alert出object里面的name
var name="The Window"; var object={ name:"My Object", getNameFunc:function(){ return function(){ return this.name; } } } alert(object.getNameFunc()()); // The Window
因为在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。
每个函数在被调用时,都会自动取的两个特殊变量:this和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止。也就是说,里面的return function只会搜索
到全局的this就停止继续搜索了。因为它永远不可能直接访问外部函数中的这两个变量。
稍作修改,把外部作用域中的this对象保存在一个闭包能够访问的变量里。这样就可以让闭包访问该对象了。
var name="The Window"; var object={ name:"My Object", getNameFunc:function(){ var that=this; return function(){ return that.name; } } } alert(object.getNameFunc()()); // My Object
我们把this对象赋值给了that变量。定义了闭包之后闭包也可以访问这个变量。因此,即使在函数返回之后,that也仍引用这object,所以调用object.getNameFunc()()就返回 “My Object”了。
总结
当在函数内部定义了其他函数,就创建了闭包。闭包有权访问包含函数内部的所有变量。
闭包的作用域包含着它自己的作用域、包含函数的作用域和全局作用域。
当函数返回一个闭包时,这个函数的作用域会一直在内存中保存到闭包不存在为止。
使用闭包必须维护额外的作用域,所有过度使用它们可能会占用大量的内存
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持PHP中文网!
更多理解javascript中的闭包相关文章请关注PHP中文网!