1. Was ist Schließung?
Die offizielle Erklärung lautet: Ein Abschluss ist ein Ausdruck (normalerweise eine Funktion), der viele Variablen und eine an diese Variablen gebundene Umgebung hat, sodass diese Variablen auch Teil des Ausdrucks sind
Ich glaube, dass nur wenige Menschen diesen Satz direkt verstehen können, weil seine Beschreibung zu akademisch ist. Tatsächlich bedeutet dieser Satz für Laien: Alle Funktionen in JavaScript sind Abschlüsse. Aber im Allgemeinen ist der durch verschachtelte Funktionen generierte Abschluss leistungsfähiger und wird meistens als „Abschluss“ bezeichnet. Schauen Sie sich den folgenden Code an:
function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(); c();
Dieser Code weist zwei Merkmale auf:
1. Funktion b ist in Funktion a verschachtelt;
2. Funktion a gibt Funktion b zurück.Die Referenzbeziehung ist wie in der Abbildung dargestellt:
Auf diese Weise zeigt die Variable c nach der Ausführung von var c=a() tatsächlich auf die Funktion b. Nach der erneuten Ausführung von c() wird ein Fenster angezeigt, in dem der Wert von i angezeigt wird (das erste Mal ist 1). Dieser Code erstellt tatsächlich einen Abschluss. Weil sich die Variable c außerhalb der Funktion a auf die Funktion b innerhalb der Funktion a bezieht, also:
Wenn die interne Funktion b der Funktion a von einer Variablen außerhalb der Funktion a referenziert wird, wird ein Abschluss erstellt.
Lassen Sie uns gründlicher vorgehen. Der sogenannte „Abschluss“ besteht darin, eine andere Funktion im Konstruktorkörper als Methodenfunktion des Zielobjekts zu definieren, und die Methodenfunktion dieses Objekts verweist wiederum auf die temporäre Variable im äußeren Funktionskörper. Dadurch können die vom ursprünglichen Konstruktorkörper verwendeten temporären Variablenwerte indirekt beibehalten werden, solange das Zielobjekt seine Methoden während seiner Lebensdauer immer beibehalten kann. Obwohl der anfängliche Konstruktoraufruf beendet wurde und der Name der temporären Variablen verschwunden ist, kann der Wert der Variablen immer innerhalb der Methode des Zielobjekts referenziert werden, und auf den Wert kann nur über diese Methode zugegriffen werden. Selbst wenn derselbe Konstruktor erneut aufgerufen wird, werden nur neue Objekte und Methoden generiert und die neuen temporären Variablen entsprechen nur neuen Werten, die vom letzten Aufruf unabhängig sind.
2. Welche Funktion hat der Abschluss?
Kurz gesagt besteht die Funktion des Abschlusses darin, dass der Abschluss nach der Ausführung und Rückgabe von a verhindert, dass der Garbage-Collection-Mechanismus GC von Javascript die von a belegten Ressourcen zurückfordert, da die Ausführung der internen Funktion b von a von a abhängen muss. Variablen in . Dies ist eine sehr einfache Beschreibung der Rolle von Abschlüssen. Sie ist weder professionell noch streng, aber die allgemeine Bedeutung ist, dass das Verständnis von Abschlüssen einen schrittweisen Prozess erfordert.Im obigen Beispiel wird i in a immer existieren, nachdem die Funktion a zurückgegeben wird. Auf diese Weise ist i jedes Mal, wenn c() ausgeführt wird, der Wert von i, der nach dem Hinzufügen benachrichtigt wird 1.
Dann stellen wir uns eine andere Situation vor: Wenn a etwas anderes als Funktion b zurückgibt, ist die Situation völlig anders. Denn nach der Ausführung von a wird b nicht an die Außenwelt von a zurückgegeben, sondern nur von a referenziert. Daher verweisen die Funktionen a und b aufeinander, werden aber nicht gestört von der Außenwelt (von der Außenwelt referenziert), werden die Funktionen a und b von GC recycelt. (Der Garbage-Collection-Mechanismus von Javascript wird später ausführlich vorgestellt)
3. Die mikroskopische Welt innerhalb des Verschlusses
Wenn wir Abschlüsse und die Beziehung zwischen Funktion a und verschachtelter Funktion b besser verstehen möchten, müssen wir mehrere andere Konzepte einführen: Funktionsausführungskontext (Ausführungskontext), aktives Objekt (Aufrufobjekt), Umfang (Bereich). ) ), Bereichskette. Nehmen Sie als Beispiel den Prozess der Funktion a von der Definition bis zur Ausführung, um diese Konzepte zu veranschaulichen.
Wenn Funktion b ausgeführt wird, erfolgt derselbe Vorgang wie in den obigen Schritten. Daher enthält die Gültigkeitskette von b während der Ausführung drei Objekte: das aktive Objekt von b, das aktive Objekt von a und das Fensterobjekt, wie in der folgenden Abbildung dargestellt:
如图所示,当在函数b中访问一个变量的时候,搜索顺序是:
小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:
function f(x) { var g = function () { return x; } return g; } var h = f(1); alert(h());
这段代码中变量h指向了f中的那个匿名函数(由g返回)。
假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。
运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。
四、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的
function Constructor(...) { var that = this; var membername = value; function membername(...) {...} }
以上3点是闭包最基本的应用场景,很多经典案例都源于此。
五、Javascript的垃圾回收机制
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
六、结语
理解了aScript的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。