Remplir:
La fermeture est une difficulté dans la langue JavaScript et sa fonctionnalité. De nombreuses applications avancées s'appuient sur les fermetures à mettre en œuvre.
Fermeture des caractéristiques
Les fermetures ont trois caractéristiques:
1. Fonction Fonctions imbriquées
2. La fonction peut se référer aux paramètres et variables externes à l'intérieur
3. Les paramètres et variables ne seront pas collectés par le mécanisme de collecte des ordures
Définition de la fermeture et de ses avantages et inconvénients
Les fermetures se réfèrent aux fonctions qui ont accès à des variables dans la portée d'une autre fonction. La façon la plus courante de créer des fermetures est de créer une autre fonction dans une fonction et d'accéder aux variables locales de cette fonction à travers une autre fonction.
L'inconvénient des fermetures est qu'il s'agit de la mémoire résidente, ce qui augmentera l'utilisation de la mémoire, et une mauvaise utilisation peut facilement entraîner une fuite de mémoire.
Les fermetures sont une caractéristique majeure de la langue JavaScript. L'application principale des fermetures est principalement pour: la conception de méthodes et de variables privées.
Une fois la fonction générale exécutée, l'objet actif local est détruit et seule la portée globale est enregistrée en mémoire. Mais la situation de fermeture est différente!
Pour parler du sujet
1. Définition de la fermeture?
Jetons un coup d'œil à certaines définitions sur les fermetures:
1. La fermeture fait référence à une fonction qui a la permission d'accéder aux variables dans une autre portée de fonction.
2. Les objets de fonction peuvent être associés à des chaînes de portée, et les variables à l'intérieur du corps de fonction peuvent être enregistrées dans la portée de la fonction. Cette caractéristique est appelée «fermeture».
3. Les fonctions internes peuvent accéder aux paramètres et aux variables des fonctions externes qui les définissent (sauf ceci et les arguments).
Si vous souhaitez apprendre systématiquement le concept de fermeture JS, vous pouvez vous référer à la colonne JS E-Book du site Web Wulin.com pour apprendre.
Résumons la définition
1. Vous pouvez accéder aux fonctions des variables dans la portée de la fonction externe
2. Les variables des fonctions externes accessibles par les fonctions internes peuvent être enregistrées dans le cadre de la fonction externe sans être recyclée - c'est le noyau. Plus tard, lorsque nous rencontrons des fermetures, nous devons y penser. Nous devons nous concentrer sur la variable référencée par la fermeture.
Créer une fermeture simple
var sayName = function () {var name = 'jozo'; return function () {alert (name);}}; var says = SayName (); dire();Interprétons les deux phrases suivantes:
• var Says = SayName (): Renvoie une fonction interne anonyme stockée dans la variable Say et se réfère au nom de variable de la fonction externe. En raison du mécanisme de collecte des ordures, après l'exécution de la fonction SayName, le nom de la variable n'est pas détruit.
• Say (): Exécutez la fonction interne renvoyée et accédez toujours au nom de la variable et à la sortie «Jozo».
2. Chaîne de portée en fermeture
Comprendre les chaînes de portée est également utile pour comprendre les fermetures.
Les méthodes de recherche des variables dans la portée devraient être très familières, mais en fait, c'est ce qui recherche le long de la chaîne de portée.
Lorsque la fonction est appelée:
1. Créez d'abord un contexte d'exécution et la chaîne de portée correspondante;
2. Ajouter des arguments et d'autres valeurs de paramètres nommés à l'objet actif de la fonction (objet d'activation)
Chaîne de portée: l'objet actif de la fonction de courant a la priorité la plus élevée, suivie de l'objet actif de la fonction externe, et l'objet actif de la fonction externe de la fonction externe diminue en séquence jusqu'à la fin de la chaîne de portée - la portée globale. La priorité est l'ordre des recherches variables;
Regardons d'abord une chaîne de portée normale:
Fonction SayName (name) {return name;} var Says = SayName ('jozo');Ce code contient deux portées: a. portée mondiale; B. Sortie de la fonction de nom, c'est-à-dire qu'il n'y a que deux objets variables. Lorsqu'il est exécuté dans l'environnement d'exécution correspondant, l'objet variable deviendra un objet actif et sera poussé à l'extrémité avant de la chaîne de portée de l'environnement d'exécution, c'est-à-dire qu'il devient la priorité la plus élevée. Parlez à l'image:
Cette image est également disponible dans JS Advanced Programming Books, et j'ai tout repris.
Lors de la création de la fonction SayName (), une chaîne de portée contenant l'objet variable à l'avance est créée, c'est-à-dire la chaîne de portée indexée par 1 sur la figure, et est enregistrée dans l'attribut interne [[SPOCE]]. Lorsque la fonction SayName () est appelée, un environnement d'exécution est créé, puis la chaîne de portée est construite en copiant l'objet dans l'attribut [[SPOCE]] de la fonction. Après cela, un autre objet actif (indexé par 0 sur la figure) est créé et poussé dans l'extrémité avant de la chaîne de portée de l'environnement d'exécution.
D'une manière générale, lorsque la fonction est exécutée, l'objet actif local sera détruit et seule la portée globale est enregistrée en mémoire. Cependant, la situation des fermetures est différente:
Jetons un coup d'œil à la chaîne de fermetures de portée:
function sayName (name) {return function () {return name;}} var says = SayName ('jozo');Cette instance de fermeture a une portée de plus pour la fonction anonyme que l'exemple précédent:
Une fois la fonction anonyme renvoyée de la fonction SayName (), sa chaîne de portée est initialisée dans un objet actif et un objet variable global contenant la fonction SayName (). De cette façon, la fonction anonyme peut accéder à toutes les variables et paramètres définis dans SayName (). Plus important encore, après l'exécution de la fonction SayName (), son objet actif ne sera pas détruit, car la chaîne de portée de la fonction anonyme fait toujours référence à l'objet actif. En d'autres termes, après l'exécution de la fonction SayName (), la chaîne de portée de son environnement d'exécution sera détruite, mais son objet actif sera laissé en mémoire, sachant que la fonction anonyme sera détruite. Il s'agit également du problème de fuite de mémoire qui sera discuté plus tard.
Je n'écris pas autant sur les problèmes de chaîne de portée, et écrire des choses dans le livre est également très fatigant o (□) o
3. Exemple de fermeture
Exemple 1: mise en œuvre de l'accumulation
// Méthode 1var a = 0; var add = function () {a ++; console.log (a)} add (); add (); // méthode 2: fermeture var add = (function () {var a = 0; return function () {a ++; console.log (a);}}) (); console.log (a); // undefinedAdd (); add ();En comparaison, la méthode 2 est plus élégante et réduit également les variables globales et privatise les variables.
Exemple 2: Ajouter un événement de clic à chaque Li
var oli = document.getElementsByTagName ('li'); var i; for (i = 0; i <5; i ++) {oli [i] .onclick = function () {alert (i);}} console.log (i); // 5 // Exécuter une fonction anonyme (function () {alert (i); // 5} ());Ce qui précède est un exemple classique. Nous savons tous que le résultat de l'exécution est que 5 apparaît, et nous savons également que les fermetures peuvent être utilisées pour résoudre ce problème, mais au début, je ne comprends toujours pas pourquoi 5 apparaît et pourquoi les fermetures peuvent résoudre ce problème. Plus tard, je l'ai réglé et je l'ai précisé:
un. Analysons d'abord la situation avant l'utilisation de la fermeture: dans la boucle FOR, nous lions une fonction anonyme à chaque événement de clic Li, et la valeur de la variable que je revient dans la fonction anonyme. Lorsque la boucle se termine, la valeur de la variable i devient 5. À l'heure actuelle, nous cliquons sur chaque LI, c'est-à-dire exécuter la fonction anonyme correspondante (voir le code ci-dessus). Ceci est la variable que je suis déjà 5, donc chaque clic apparaît 5. Parce que chaque fonction anonyme renvoyée ici fait référence à la même variable I, si nous créons une nouvelle variable pour enregistrer la valeur I actuelle de I lorsque la boucle est exécutée, puis laissez la fonction anonyme appliquer cette variable, et enfin renvoyer cette fonction anonyme, afin que notre objectif puisse être atteint. Ceci est réalisé en utilisant des fermetures!
né Analysons la situation lors de l'utilisation de fermetures:
var oli = document.getElementsByTagName ('li'); var i; for (i = 0; i <5; i ++) {oli [i] .OnClick = (function (num) {var a = num; // pour illustrer la fonction de retour du problème () {alert (a);}}) (i)} console.log (i); // 5Lorsque la boucle FOR est exécutée, la fonction anonyme liée à l'événement de clic est passé I et exécutée immédiatement pour renvoyer une fonction anonyme interne. Étant donné que les paramètres sont passés par valeur, le paramètre formel Num enregistre la valeur actuelle de I, puis attribue la valeur à la variable locale a. Ensuite, la fonction anonyme interne maintient la référence de A, c'est-à-dire maintient la valeur actuelle de i. Ainsi, une fois la boucle exécutée, cliquez sur chaque LI et la fonction anonyme renvoyée apparaîtra la valeur du A enregistré.
4. Application des fermetures
Jetons un coup d'œil à l'objectif des fermetures. En fait, en utilisant des fermetures, nous pouvons faire beaucoup de choses. Par exemple, simulez le style de code orienté objet; Exprimez le code plus élégamment et de manière concise; et améliorer l'efficacité de l'exécution du code sous certains aspects.
1. Fonction anonyme auto-exécutante
Dans les situations réelles, nous rencontrons souvent une situation où certaines fonctions ne doivent être exécutées qu'une seule fois et leurs variables internes n'ont pas besoin d'être maintenues, comme l'initialisation de l'interface utilisateur, afin que nous puissions utiliser des fermetures:
// change toutes les polices li en rouge (function () {var els = document.getElementsByTagName ('li'); for (var i = 0, lng = els.length; i <lng; i ++) {els [i] .style.color = 'red';}}) ();Nous créons une fonction anonyme et l'exécutons immédiatement. Étant donné que l'extérieur ne peut pas se référer aux variables à l'intérieur, des variables locales telles que ELS, I et LNG seront publiées peu de temps après l'exécution, enregistrant la mémoire!
La clé est que ce mécanisme ne polluera pas l'objet global.
2. Implémentez l'encapsulation / code modulaire
var person = function () {// La portée de la variable est à l'intérieur de la fonction, et le var name = "par défaut" ne peut pas être accessible à l'extérieur. return {getName: function () {return name; }, setName: function (newname) {name = newname; }}} (); console.log (personne.name); // Accès direct, le résultat est un non défini console.log (personne.getName ()); // Person par défaut.SetName ("Jozo"); console.log (personne.getName ()); // Jozo3. Implémentez les objets
De cette façon, différents objets (instances de classes) ont des membres et des états indépendants et n'interfèrent pas les uns avec les autres. Bien qu'il n'y ait pas de mécanisme tel que la classe en JavaScript, en utilisant des fermetures, nous pouvons simuler de tels mécanismes. Parlons de l'exemple ci-dessus:
fonction personne () {var name = "default"; return {getName: function () {return name; }, setName: function (newname) {name = newname; }}}; var person1 = personne (); print (personne1.getName ()); John.SetName ("Person1"); print (personne1.getName ()); // personne1 var person2 = personne (); print (Person2.getName ()); jack.setName ("erson2"); print (erson2.getName ()); // Person2Les deux instances de la personne Person1 et Person2 n'interfèrent pas les unes avec les autres! Parce que ces deux instances ont un accès indépendant au membre du nom.
5. Fuites et solutions de mémoire
Mécanisme de recyclage des ordures
En parlant de gestion de la mémoire, il est naturellement inséparable du mécanisme de collecte des ordures en JS. Il existe deux stratégies pour réaliser la collecte des ordures: la clairance des marques et le comptage des références ;
Retrait de la marque: lorsque le collecteur des ordures fonctionne, il marquera toutes les variables stockées en mémoire. Ensuite, il supprimera les balises des variables dans l'environnement et les balises des variables référencées par des variables dans l'environnement. Après cela, si la variable est à nouveau marquée, cela signifie que la variable est prête à être supprimée. Jusqu'en 2008, IE, Firefox, Opera, Chrome et le JavaScript de Safari ont tous utilisé cette méthode;
Compte de référence: suivez le nombre de fois que chaque valeur est référencée. Lorsqu'une variable est déclarée et que la valeur d'un type de référence est attribuée à la variable, le nombre de fois que cette valeur est référencée est 1. Si cette valeur est attribuée à une autre variable, le nombre de fois qu'une référence est augmentée de 1. Au contraire, si une variable s'écarte de la référence de la valeur, le nombre de références de la valeur est réduit de 1, et lorsque le nombre de fois est 0, il attendra pour que le collège de grenouilleurait pour 0.
Un gros problème avec cette méthode est la référence circulaire, c'est-à-dire que l'objet A contient un pointeur vers B, et l'objet B contient également une référence à A. Cela peut entraîner une grande quantité de mémoire à recycler (fuites de mémoire), car leurs références ne peuvent jamais être 0. Dans les premières versions IE (IE4-IE6) ont adopté un mécanisme de collecte de dérivation qui a été compté. L'une des raisons pour lesquelles les fermetures ont provoqué des fuites de mémoire ont été un défaut de cet algorithme.
Nous savons que certains objets dans IE ne sont pas des objets JavaScript natifs. Par exemple, les objets de Bom et Dom sont implémentés sous la forme d'objets com, et le mécanisme de collecte des ordures des objets com adopte le comptage de référence. Par conséquent, bien que le moteur JavaScript d'IE adopte une stratégie de compensation de balises, l'accès aux objets COM est toujours basé sur le comptage de référence, donc tant que les objets COM sont conçus dans IE, il y aura un problème de références circulaires!
Prenez un châtaignier:
window.onload = function () {var el = document.getElementById ("id"); el.onclick = function () {alert (el.id);}}Pourquoi ce code provoque-t-il des fuites de mémoire?
el.onclick = function () {alert (el.id);};Lors de l'exécution de ce code, l'objet de fonction anonyme est attribué à l'attribut onClick d'El; Ensuite, la fonction anonyme fait référence à l'objet EL à l'intérieur, et il y a une référence circulaire, il ne peut donc pas être recyclé;
Solution:
window.onload = function () {var el = document.getElementyId ("id"); var id = el.id; // Unreference el.OnClick = function () {alert (id); } el = null; // effacer l'objet actif dans la fonction externe référencée par la fermeture}Ce qui précède est le résumé des connaissances pertinentes sur la fuite de mémoire de la collection de déchets de la chaîne de portée de fermeture JS qui vous est présentée par l'éditeur. J'espère que ce sera utile à tous!