Heim  >  Artikel  >  Web-Frontend  >  Erlernen Sie weiterhin Javascript-Closure_Javascript-Fähigkeiten

Erlernen Sie weiterhin Javascript-Closure_Javascript-Fähigkeiten

WBOY
WBOYOriginal
2016-05-16 15:27:55940Durchsuche

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.

    Beim Definieren der Funktion a legt der js-Interpreter die
  • Bereichskette (Scope-Kette) der Funktion a auf die „Umgebung“ fest, in der a ist, wenn a ist eine globale Funktion, dann gibt es nur das Fensterobjekt in der Bereichskette.
  • Beim Ausführen der Funktion a gelangt a in den entsprechenden
  • Ausführungskontext.
  • Beim Erstellen der Ausführungsumgebung wird zunächst ein Bereichsattribut zu a hinzugefügt, dh dem
  • -Bereich eines , und der Wert von ist die Bereichskette in Schritt 1. Das heißt, die Bereichskette von a.scope=a.
  • Dann erstellt die Ausführungsumgebung ein
  • Aufrufobjekt. Das aktive Objekt ist ebenfalls ein Objekt, das Eigenschaften hat, aber keinen Prototyp hat und nicht direkt über JavaScript-Code aufgerufen werden kann. Nachdem Sie das aktive Objekt erstellt haben, fügen Sie das aktive Objekt an der Spitze der Bereichskette von a hinzu. Zu diesem Zeitpunkt enthält die Bereichskette von a zwei Objekte: das aktive Objekt von a und das Fensterobjekt.
  • Der nächste Schritt besteht darin, dem aktiven Objekt ein Argumentattribut hinzuzufügen, das die beim Aufruf von Funktion a übergebenen Parameter enthält.
  • Fügen Sie abschließend alle formalen Parameter der Funktion a und Verweise auf die interne Funktion b zum aktiven Objekt von a hinzu. In diesem Schritt ist die Definition der Funktion b abgeschlossen. Genau wie in Schritt 3 wird die Bereichskette der Funktion b auf die Umgebung festgelegt, in der b definiert ist, dh auf den Bereich von a.
An diesem Punkt sind die Schritte von der Definition bis zur Ausführung der gesamten Funktion a abgeschlossen. Zu diesem Zeitpunkt gibt a den Verweis der Funktion b auf c zurück, und die Bereichskette der Funktion b enthält einen Verweis auf das aktive Objekt der Funktion a, was bedeutet, dass b auf alle in a definierten Variablen und Funktionen zugreifen kann. Funktion b wird von c referenziert und Funktion b hängt von Funktion a ab, sodass Funktion a nach der Rückkehr nicht von GC recycelt wird.

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中访问一个变量的时候,搜索顺序是:

  • 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  • 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  • 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤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的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn