Heim >Web-Frontend >js-Tutorial >Ein erfahrener Fahrer wird Ihnen helfen, die verschiedenen Fallstricke von JS-Verschlüssen gründlich zu verstehen
Der alte Treiber wird Ihnen helfen, die verschiedenen Fallstricke von JS-Abschlüssen gründlich zu verstehen
Abschlüsse sind js Gängige Entwicklungstechniken, was sind Verschlüsse?
Ein Abschluss bezieht sich auf eine Funktion, die auf Variablen im Rahmen einer anderen Funktion zugreifen kann. Um es klar auszudrücken: Ein Abschluss ist eine Funktion, die im Rahmen anderer Funktionen auf Variablen zugreifen kann. zB:
function outer() { var a = '变量1' var inner = function () { console.info(a) } return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域 }
Viele Leute verstehen die Beziehung zwischen anonymen Funktionen und Abschlüssen nicht. Tatsächlich werden Abschlüsse aus der Perspektive des Bereichs definiert, da inner auf Variablen im äußeren Bereich zugreift, also ist inner eine Abschlussfunktion. Obwohl die Definition sehr einfach ist, gibt es viele Fallstricke, wie z. B. diesen Zeiger und den Umfang der Variablen. Eine kleine Nachlässigkeit kann zu Speicherverlusten führen. Lassen Sie uns das Problem beiseite legen und über eine Frage nachdenken: Warum können Abschlussfunktionen auf den Umfang anderer Funktionen zugreifen?
Js-Funktionen aus der Perspektive des Stapels betrachten
Grundvariablen Der Wert von wird im Allgemeinen im Stapelspeicher gespeichert, während der Wert der Objekttypvariablen im Heapspeicher gespeichert wird und der Stapelspeicher die entsprechende Raumadresse speichert. Grundlegende Datentypen: Zahl, Boolescher Wert, Undefiniert, Zeichenfolge, Null.
var a = 1 //a是一个基本类型 var b = {m: 20 } //b是一个对象
entspricht dem Speicher:
Wenn wir b={m:30} ausführen, gibt es ein neues Objekt {m:30} im Heap-Speicher, b im Stapelspeicher zeigt auf die neue Raumadresse (zeigt auf {m: 30}), und das ursprüngliche {m: 20} im Heap-Speicher wird von der Programm-Engine als Müll gesammelt, wodurch Speicherplatz gespart wird. Wir wissen, dass js-Funktionen auch Objekte sind und auch im Heap- und Stack-Speicher gespeichert werden. Schauen wir uns die Konvertierung an:
var a = 1; function fn(){ var b = 2; function fn1(){ console.log(b); } fn1(); } fn();
**
Der Stapel ist eine First-In-Last-Out-Datenstruktur:
1 Bevor wir fn ausführen, befinden wir uns in der globalen Ausführungsumgebung (der Browser ist der Fensterbereich) und es gibt eine Variable a in der globale Bereich;
2 Geben Sie fn ein. Diese Umgebung enthält die Variable b und das Funktionsobjekt fn1 Umgebung und die globale Ausführungsumgebung
3 Geben Sie fn1 ein. Zu diesem Zeitpunkt wird der Stapelspeicher eine Ausführungsumgebung von fn1 übertragen. Es sind keine anderen Variablen darin definiert, aber wir können auf die Variablen in fn und zugreifen globale Ausführungsumgebung, denn wenn das Programm auf die Variablen zugreift, wird es in den untersten Stapel verschoben. Wenn Sie feststellen, dass in der globalen Ausführungsumgebung keine entsprechende Variable vorhanden ist, gibt das Programm einen unterdefinierten Fehler aus.
4 Während fn1() ausgeführt wird, wird die Ausführungsumgebung von fn1 durch cup zerstört, und wenn fn() ausgeführt wird, wird auch die Ausführungsumgebung von fn zerstört, sodass nur die globale Ausführungsumgebung übrig bleibt Jetzt gibt es keine b-Variablen und fn1-Funktionsobjekte, nur a und fn (der Funktionsdeklarationsbereich liegt unter dem Fenster)
**
Der Zugriff auf eine Variable innerhalb einer Funktion wird anhand der beurteilt Funktionsbereichskette Gibt an, ob die Variable vorhanden ist und die Funktionsbereichskette vom Programm entsprechend dem Ausführungsumgebungsstapel initialisiert wird, in dem sich die Funktion befindet. Im obigen Beispiel drucken wir also die Variable b in fn1 und suchen die entsprechende fn-Ausführungsumgebung gemäß der Bereichskette der fn1-Variablen b. Wenn das Programm also eine Funktion aufruft, führt es die folgende Arbeit aus: Bereiten Sie die Ausführungsumgebung, die anfängliche Funktionsbereichskette und das Argumentparameterobjekt vor >Wenn das Programm die Ausführung von var inner = OUTER () beendet, wird die Ausführungsumgebung von OUTER tatsächlich nicht zerstört, da die darin enthaltene Variable a immer noch von der Funktionsbereichskette von Inner () referenziert wird. Das Buch „JavaScript Advanced Programming“ empfiehlt: „Da Schließungen den Umfang der Funktion übernehmen, die sie enthält, nehmen sie mehr Inhalt ein als andere Funktionen.“ Die Verwendung von Schließungen führt zu einer übermäßigen Speichernutzung.
Jetzt verstehen wir den Abschluss, den entsprechenden Geltungsbereich und die Geltungsbereichskette, kehren zum Thema zurück:
Punkt 1: Die referenzierten Variablen können sich ändernfunction outer() { var a = '变量1' var inner = function () { console.info(a) } return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域 } var inner = outer() // 获得inner闭包函数 inner() //"变量1"
Es scheint, dass jede Abschlussfunktion im Ergebnis die entsprechende Zahl ausgibt, 1, 2, 3, 4, ..., 10. Dies ist jedoch nicht der Fall, da jede Abschlussfunktion auf die Variable i in der äußeren Ausführungsumgebung zugreift. Variable i, am Ende der Schleife ist i zu 10 geworden, also wird jede Abschlussfunktion ausgeführt und das Ergebnis gibt 10, 10, ..., 10 ausWie kann dieses Problem gelöst werden?
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function () { console.info(i) } } return result }Punkt 2: Das weist auf das Problem hin
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function (num) { return function() { console.info(num); // 此时访问的num,是上层函数执行环境的num,数组有10个函数对象,每个对象的执行环境下的number都不一样 } }(i) } return result }
Punkt 3: Speicherverlustproblem
var object = { name: ''object", getName: function() { return function() { console.info(this.name) } } } object.getName()() // underfined // 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向window
Tipp 1: Verwenden Sie Abschlüsse, um rekursive Aufrufprobleme zu lösen
function showId() { var el = document.getElementById("app") el.onclick = function(){ aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放 } } // 改成下面 function showId() { var el = document.getElementById("app") var id = el.id el.onclick = function(){ aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放 } el = null // 主动释放el }
** Tipp 2: Verwenden Sie Abschlüsse, um den Gültigkeitsbereich auf Blockebene zu imitieren**
es6 ist noch nicht verfügbar. Zuvor gab es ein Problem mit der Variablenförderung, wenn var zum Definieren von Variablen verwendet wurde, z. B.:function factorial(num) {
if(num<= 1) {
return 1;
} else {
return num * factorial(num-1)
}
}
var anotherFactorial = factorial
factorial = null
anotherFactorial(4) // 报错 ,因为最好是return num* arguments.callee(num-1),arguments.callee指向当前执行函数,但是在严格模式下不能使用该属性也会报错,所以借助闭包来实现
// 使用闭包实现递归
function newFactorial = (function f(num){
if(num<1) {return 1}
else {
return num* f(num-1)
}
}) //这样就没有问题了,实际上起作用的是闭包函数f,而不是外面的函数newFactorial
Natürlich werden die meisten von ihnen jetzt mit let und const von es6 definiert.
JavaScript-Video-Tutorial
auf der chinesischen PHP-Website!
Das obige ist der detaillierte Inhalt vonEin erfahrener Fahrer wird Ihnen helfen, die verschiedenen Fallstricke von JS-Verschlüssen gründlich zu verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!