Detaillierte Einführung in Verschlüsse in js
1. Was ist Schließung?
Sehen wir uns einige Definitionen von Abschlüssen an:
Ein Abschluss ist eine Funktion, die Zugriff auf eine Variable im Gültigkeitsbereich einer anderen Funktion hat – - „JS Advanced Programming Third Edition“ p178
Funktionsobjekte können über Bereichsketten verknüpft werden, und Variablen innerhalb des Funktionskörpers können im Funktionsbereich gespeichert werden. Diese Funktion wird als „Abschluss“ bezeichnet. . -- „The Definitive Guide to JS“ p183
Interne Funktionen können auf die Parameter und Variablen der externen Funktionen zugreifen, in denen sie definiert sind (außer
this
undarguments
). -- „JS Language Essence“ S. 36
Lassen Sie uns die Definition zusammenfassen
Sie können auf Variablen im Bereich von zugreifen Externe Funktionen
函数
Variablen externer Funktionen, auf die interne Funktionen zugreifen, können im Umfang der externen Funktion gespeichert werden, ohne recycelt zu werden – das ist der Kern. Wir werden auf Schließungen stoßen Später müssen wir uns auf die Variable konzentrieren, auf die der Abschluss verweist.
um einen einfachen Abschluss zu erstellen
var sayName = function(){var name = 'jozo';return function(){ alert(name); } };var say = sayName(); say();
um die folgenden zwei Aussagen zu interpretieren:
-
var say = sayName()
: Gibt eine anonyme interne Funktion zurück, die in der Variablen say gespeichert ist, und verweist auf den Variablennamen der externen Funktion. Aufgrund des Garbage-Collection-Mechanismus wird der Variablenname nach der Ausführung der Funktion sayName nicht zerstört. say()
: Führen Sie die zurückgegebene interne Funktion aus. Sie können weiterhin auf den Variablennamen zugreifen und „jozo“ ausgeben.
2 Verschluss-Scope-Kette
Das Verständnis von Scope-Ketten ist auch hilfreich für das Verständnis von Abschlüssen.
Sie sollten mit der Art und Weise vertraut sein, wie Variablen im Bereich durchsucht werden. Tatsächlich handelt es sich dabei um eine Suche in der Bereichskette.
Wenn die Funktion aufgerufen wird:
Erstellen Sie zunächst einen Ausführungskontext und die entsprechende Bereichskette;
Fügen Sie die Werte von Argumenten und anderen benannten Parametern zum Aktivierungsobjekt der Funktion hinzu- Bereichskette: Die Aktivierungsobjektpriorität der aktuellen Funktion Die höchste, gefolgt von den aktiven Objekten von externen Funktionen, und die aktiven Objekte externer Funktionen nehmen der Reihe nach bis zum Ende der Bereichskette – dem globalen Bereich – ab. Priorität ist die Reihenfolge, in der Variablen durchsucht werden.
Schauen wir uns zunächst eine allgemeine Bereichskette an:
Dieser Code enthält zwei Bereiche:
Global Bereich;function sayName(name){return name; }var say = sayName('jozo');
a.
b.
Dieses Bild befindet sich auch im JS-Programmierbuch für Fortgeschrittene, und ich habe es noch einmal gezeichnet.
Werfen wir einen Blick auf die Scope-Kette der Schließungen:
Diese Schließungsinstanz ist besser als die vorherige Das Beispiel fügt den Bereich einer anonymen Funktion hinzu:
function sayName(name){return function(){return name; } }var say = sayName('jozo');
Nachdem die anonyme Funktion von der Funktion sayName() zurückgegeben wurde, wird ihre Bereichskette initialisiert das aktive Objekt und das globale Variablenobjekt, das die Funktion sayName() enthält. Auf diese Weise kann die anonyme Funktion auf alle in sayName() definierten Variablen und Parameter zugreifen. Noch wichtiger ist, dass ihr aktives Objekt aufgrund der Gültigkeitskette der anonymen Funktion nicht zerstört wird Das Objekt wird immer noch referenziert. Mit anderen Worten: Nachdem die Funktion sayName() ausgeführt wurde, wird die Bereichskette ihrer Ausführungsumgebung zerstört, ihr aktives Objekt bleibt jedoch im Speicher, bis die anonyme Funktion zerstört wird. Dies ist auch das Problem des Speicherverlusts, auf das später noch eingegangen wird.
Beispiel 1: Akkumulation implementieren
Beispiel 2: Klickereignisse zu jedem Li hinzufügen
// 方式1var a = 0;var add = function(){ a++; console.log(a) }add();add();//方式2 :闭包var add = (function(){ var a = 0; return function(){ a++; console.log(a); } })(); console.log(a); //undefinedadd();add(); 相比之下方式2更加优雅,也减少全局变量,将变量私有化
Das Obige ist ein Klassiker Beispielsweise wissen wir alle, dass als Ausführungsergebnis 5 angezeigt wird, und wir wissen auch, dass der Verschluss zur Lösung dieses Problems verwendet werden kann, aber am Anfang konnte ich immer noch nicht verstehen, warum jedes Mal 5 angezeigt wird und warum der Verschluss das Problem lösen kann dieses Problem. Nach einigem Suchen habe ich es endlich herausgefunden:
a. 先来分析没用闭包前的情况:for循环中,我们给每个li点击事件绑定了一个匿名函数,匿名函数中返回了变量i的值,当循环结束后,变量i的值变为5,此时我们再去点击每个li,也就是执行相应的匿名函数(看上面的代码),这是变量i已经是5了,所以每个点击弹出5. 因为这里返回的每个匿名函数都是引用了同一个变量i,如果我们新建一个变量保存循环执行时当前的i的值,然后再让匿名函数应用这个变量,最后再返回这个匿名函数,这样就可以达到我们的目的了,这就是运用闭包来实现的!
b. 再来分析下运用闭包时的情况:
var oli = document.getElementsByTagName('li'); var i; for(i = 0;i < 5;i++){ oli[i].onclick = (function(num){ var a = num; // 为了说明问题 return function(){ alert(a); } })(i) } console.log(i); // 5
这里for循环执行时,给点击事件绑定的匿名函数传递i后立即执行返回一个内部的匿名函数,因为参数是按值传递的,所以此时形参num保存的就是当前i的值,然后赋值给局部变量 a,然后这个内部的匿名函数一直保存着a的引用,也就是一直保存着当前i的值。 所以循环执行完毕后点击每个li,返回的匿名函数执行弹出各自保存的 a 的引用的值。
4. 闭包的运用
我们来看看闭包的用途。事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率。
1. 匿名自执行函数
我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包:
//将全部li字体变为红色 (function(){ var els = document.getElementsByTagName('li');for(var i = 0,lng = els.length;i < lng;i++){ els[i].style.color = 'red'; } })();
我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,
因此els,i,lng这些局部变量在执行完后很快就会被释放,节省内存!
关键是这种机制不会污染全局对象。
2. 实现封装/模块化代码
var person= function(){ //变量作用域为函数内部,外部无法访问 var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }();console.log(person.name);//直接访问,结果为undefined console.log(person.getName()); //default person.setName("jozo"); console.log(person.getName()); //jozo
3. 实现面向对象中的对象
这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,
我们可以模拟出这样的机制。还是以上边的例子来讲:
function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }; var person1= Person(); print(person1.getName()); john.setName("person1"); print(person1.getName()); // person1 var person2= Person(); print(person2.getName()); jack.setName("erson2"); print(erson2.getName()); //person2
Person的两个实例person1 和 person2 互不干扰!因为这两个实例对name这个成员的访问是独立的 。
5. 内存泄露及解决方案
垃圾回收机制
说到内存管理,自然离不开JS中的垃圾回收机制,有两种策略来实现垃圾回收:标记清除 和 引用计数;
标记清除:
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除。 2008年为止,IE,Firefox,opera,chrome,Safari的javascript都用使用了该方式;
引用计数:
跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个值的引用次数就是1,如果这个值再被赋值给另一个变量,则引用次数加1。相反,如果一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,就会等待垃圾收集器的回收。
这个方式存在一个比较大的问题就是循环引用,就是说A对象包含一个指向B的指针,对象B也包含一个指向A的引用。 这就可能造成大量内存得不到回收(内存泄露),因为它们的引用次数永远不可能是 0 。早期的IE版本里(ie4-ie6)采用是计数的垃圾回收机制,闭包导致内存泄露的一个原因就是这个算法的一个缺陷。
我们知道,IE中有一部分对象并不是原生额javascript对象,例如,BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。因此,虽然IE的javascript引擎采用的是标记清除策略,但是访问COM对象依然是基于引用计数的,因此只要在IE中设计COM对象就会存在循环引用的问题!
举个栗子:
window.onload = function(){var el = document.getElementById("id"); el.onclick = function(){ alert(el.id); } }
这段代码为什么会造成内存泄露?
el.onclick= function () { alert(el.id); };
执行这段代码的时候,将匿名函数对象赋值给el的onclick属性;然后匿名函数内部又引用了el对象,存在循环引用,所以不能被回收;
解决方法:
window.onload = function(){var el = document.getElementById("id");var id = el.id; //解除循环引用 el.onclick = function(){ alert(id); } el = null; // 将闭包引用的外部函数中活动对象清除 }
6. 总结闭包的优缺点
优点:
可以让一个变量常驻内存 (如果用的多了就成了缺点
避免全局变量的污染
私有化变量
缺点
Da der Abschluss den Umfang der Funktion trägt, die ihn enthält, belegt er mehr Speicher als andere Funktionen
-
Verursacht Speicherlecks
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Verschlüsse in js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Bei mechanischen Festplatten oder SATA-Solid-State-Laufwerken werden Sie die erhöhte Software-Laufgeschwindigkeit spüren. Wenn es sich um eine NVME-Festplatte handelt, spüren Sie sie möglicherweise nicht. 1. Importieren Sie die Registrierung in den Desktop und erstellen Sie ein neues Textdokument, kopieren Sie den folgenden Inhalt, fügen Sie ihn ein, speichern Sie ihn als 1.reg, klicken Sie dann mit der rechten Maustaste, um den Computer zusammenzuführen und neu zu starten. WindowsRegistryEditorVersion5.00[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\MemoryManagement]"DisablePagingExecutive"=d

typedef struct wird in der C-Sprache zum Erstellen von Strukturtypaliasen verwendet, um die Verwendung von Strukturen zu vereinfachen. Es weist einem neuen Datentyp ein Alias auf eine vorhandene Struktur zu, indem es den Strukturalias angibt. Zu den Vorteilen gehören verbesserte Lesbarkeit, Wiederverwendung von Code und Typprüfung. Hinweis: Die Struktur muss vor der Verwendung eines Alias definiert werden. Der Alias muss im Programm eindeutig sein und nur innerhalb des Bereichs gültig sein, in dem er deklariert ist.

Laut Nachrichten dieser Website vom 3. September berichteten die koreanischen Medien etnews gestern (Ortszeit), dass die „HBM-ähnlichen“ mobilen Speicherprodukte mit Stapelstruktur von Samsung Electronics und SK Hynix nach 2026 kommerzialisiert werden. Quellen zufolge betrachten die beiden koreanischen Speichergiganten gestapelten mobilen Speicher als wichtige zukünftige Einnahmequelle und planen, den „HBM-ähnlichen Speicher“ auf Smartphones, Tablets und Laptops auszudehnen, um End-Side-KI mit Strom zu versorgen. Früheren Berichten auf dieser Website zufolge heißt das Produkt von Samsung Electronics LPWide I/O-Speicher und SK Hynix nennt diese Technologie VFO. Die beiden Unternehmen haben ungefähr den gleichen technischen Weg gewählt, nämlich die Kombination von Fan-Out-Verpackungen und vertikalen Kanälen. Der LPWide I/O-Speicher von Samsung Electronics hat eine Bitbreite von 512

Zu den Vorteilen von JavaScript-Abschlüssen gehören die Aufrechterhaltung des variablen Bereichs, die Aktivierung von modularem Code, die verzögerte Ausführung und die Ereignisbehandlung. Zu den Nachteilen zählen Speicherverluste, erhöhte Komplexität, Leistungsaufwand und Auswirkungen der Bereichskette.

Die Präprozessoranweisung #include in C++ fügt den Inhalt einer externen Quelldatei in die aktuelle Quelldatei ein und kopiert ihren Inhalt an die entsprechende Stelle in der aktuellen Quelldatei. Wird hauptsächlich zum Einschließen von Header-Dateien verwendet, die im Code benötigte Deklarationen enthalten, z. B. #include <iostream>, um Standard-Eingabe-/Ausgabefunktionen einzubinden.

Laut Nachrichten dieser Website vom 7. Juni stellte GEIL seine neueste DDR5-Lösung auf der Taipei International Computer Show 2024 vor und stellte SO-DIMM-, CUDIMM-, CSODIMM-, CAMM2- und LPCAMM2-Versionen zur Auswahl. ▲Bildquelle: Wccftech Wie im Bild gezeigt, verfügt der von Jinbang ausgestellte CAMM2/LPCAMM2-Speicher über ein sehr kompaktes Design, kann eine maximale Kapazität von 128 GB und eine Geschwindigkeit von bis zu 8533 MT/s bieten Stabil auf der AMDAM5-Plattform. Übertaktet auf 9000 MT/s ohne zusätzliche Kühlung. Berichten zufolge kann der Speicher der Polaris RGBDDR5-Serie 2024 von Jinbang bis zu 8400 bereitstellen

Laut einem TrendForce-Umfragebericht hat die KI-Welle erhebliche Auswirkungen auf die Märkte für DRAM-Speicher und NAND-Flash-Speicher. In den Nachrichten dieser Website vom 7. Mai sagte TrendForce heute in seinem neuesten Forschungsbericht, dass die Agentur die Vertragspreiserhöhungen für zwei Arten von Speicherprodukten in diesem Quartal erhöht habe. Konkret schätzte TrendForce ursprünglich, dass der DRAM-Speichervertragspreis im zweiten Quartal 2024 um 3 bis 8 % steigen wird, und schätzt ihn nun auf 13 bis 18 %, bezogen auf NAND-Flash-Speicher, die ursprüngliche Schätzung wird um 13 bis 18 % steigen 18 %, und die neue Schätzung liegt bei 15 %, nur eMMC/UFS weist einen geringeren Anstieg von 10 % auf. ▲Bildquelle TrendForce TrendForce gab an, dass die Agentur ursprünglich damit gerechnet hatte, dies auch weiterhin zu tun

Lebenszyklus von C++-Smartpointern: Erstellung: Smartpointer werden erstellt, wenn Speicher zugewiesen wird. Eigentumsübertragung: Übertragen Sie das Eigentum durch einen Umzugsvorgang. Freigabe: Speicher wird freigegeben, wenn ein Smart Pointer den Gültigkeitsbereich verlässt oder explizit freigegeben wird. Objektzerstörung: Wenn das Objekt, auf das gezeigt wird, zerstört wird, wird der intelligente Zeiger zu einem ungültigen Zeiger.
