Introduction
Une fermeture est une fonction qui a la permission d'accéder aux variables dans une autre portée de fonction.
Les fermetures sont difficiles à comprendre en JavaScript. De nombreuses applications avancées s'appuient sur les fermetures pour les mettre en œuvre. Regardons d'abord un exemple ci-dessous:
fonction outer () {var i = 100; fonction inner () {console.log (i); }}Dans le code ci-dessus, selon la portée de la variable, toutes les variables locales de la fonction extérieure sont visibles à la fonction intérieure; Les variables locales dans la fonction intérieure sont invisibles en dehors de la fonction intérieure, donc les variables locales dans la fonction intérieure ne peuvent pas être lues en dehors de la fonction intérieure.
Étant donné que la fonction intérieure peut lire les variables locales de la fonction extérieure, tant que l'intérieur est utilisé comme valeur de retour, les variables locales intérieures peuvent être lues directement à l'extérieur de l'ouer.
fonction outer () {var i = 100; fonction inner () {console.log (i); } return inner;} var rs = exter (); rs ();Cette fonction a deux caractéristiques:
Après avoir exécuté var rs = exter () de cette manière, le RS réel pointe vers la fonction intérieure. Ce code est en fait une fermeture. C'est-à-dire que lorsque la fonction intérieure à l'intérieur de la fonction extérieure est référencée par une variable à l'extérieur de l'extérieur de la fonction, une fermeture est créée.
Portée
Autrement dit, la portée est la gamme accessible de variables et de fonctions, c'est-à-dire que la portée contrôle le cycle de visibilité et de vie des variables et des fonctions. En JavaScript, la portée des variables est globale et locale.
Portée mondiale
var num1 = 1; fonction fun1 () {num2 = 2;}Les trois objets ci-dessus Num1, Num2 et Fun1 sont tous des lunettes mondiales. Il convient de noter ici que les variables qui définissent les affectations directes à la fin sont automatiquement déclarées comme ayant des lunettes mondiales;
Portée locale
fonction wrap () {var obj = "Je suis enveloppé par enveloppe, et l'extérieur de l'emballage ne peut pas m'accéder directement"; fonction innerfun () {// l'extérieur ne peut pas m'accès à m'accéder}}Chaîne de portée
Tout dans JavaScript est un objet. Ces objets ont une propriété [[SPOCE]], qui contient une collection d'objets dans la portée créée par la fonction. Cette collection est appelée la chaîne de portée de la fonction, qui détermine les données accessibles par la fonction.
fonction Add (a, b) {return a + b;}Lorsqu'une fonction est créée, sa propriété [[SCOPE]] ajoutera automatiquement la portée globale
var sum = add (3,4);
Lorsqu'une fonction est appelée, un objet interne appelé contexte d'exécution est créé. Cet objet Z définit l'environnement lorsque la fonction est exécutée. Il a également sa propre chaîne de portée pour la résolution de l'identificateur, et sa chaîne de portée est initialisée comme un objet contenu dans [[SCOPE]] de la fonction de fonctionnement actuelle.
Pendant l'exécution de la fonction, chaque fois qu'une variable est rencontrée, un processus d'analyse d'identifiant sera transmis pour décider où obtenir et stocker des données. Ce processus part à partir de la tête de la chaîne de portée, c'est-à-dire rechercher un identifiant du même nom de l'objet actif. S'il est trouvé, utilisez la variable correspondant à cet identifiant. S'il n'est pas trouvé, continuez à rechercher l'objet suivant dans la chaîne de portée, si tous les objets sont recherchés (le dernier est un objet global) ne sont pas trouvés, l'identifiant est considéré comme non défini.
Fermeture
Une fermeture est simplement une fonction qui accède à ses variables externes.
var quo = function (status) {return {getStatus: function () {return status; }}}Le statut est enregistré en quo, il renvoie un objet, la méthode getStatus dans cet objet fait référence à la variable d'état, c'est-à-dire que la fonction GetStatus accède à son état de variable externe;
var newValue = quo ('string'); // renvoie un objet anonyme, référencé par NewValue avec newValue.getStatus (); // accédé à l'état de variable interne de QuoSi la méthode GetStatus n'est pas disponible, l'état sera automatiquement recyclé après Quo («Sting»). C'est précisément parce que l'objet anonyme retourné est référencé par un objet global, et l'objet anonyme dépend du statut, il empêchera donc la libération de l'état.
exemple:
// schéma d'erreur var test = fonction (nœuds) {var i; pour (i = 0; i <nœuds.length; i ++) {nœuds [i] .OnClick = function (e) {alert (i); }}}Une fonction anonyme crée une fermeture, et le I il accède est i dans la fonction de test externe, donc chaque nœud fait référence au même i.
// Solution d'amélioration var test = fonction (nœuds) {var i; pour (i = 0; i <nœuds.length; i ++) {nœuds [i] .OnClick = function (i) {return function () {alert (i); }; }(je); }}Chaque nœud est lié à un événement. Cet événement reçoit un paramètre et s'exécute immédiatement, transmettant i. Parce qu'il est passé par valeur, chaque boucle générera une nouvelle sauvegarde pour le actuel i.
Le rôle de la fermeture
fonction outer () {var i = 100; fonction inner () {console.log (i ++); } return inner;} var rs = exter (); rs (); // 100rs (); // 101rs (); // 102Dans le code ci-dessus, RS est la fonction interne de fermeture. RS a fonctionné trois fois au total, la première fois était de 100, la deuxième fois était de 101 et la troisième fois était de 102. Cela montre que la variable locale I dans l'extérieur de la fonction a été maintenue en mémoire et n'a pas été effacée automatiquement lorsqu'elle est appelée.
Le but de la fermeture est qu'après l'exécution extérieure terminée et retournée, la fermeture rend le mécanisme de collecte des ordures du JavaScript (collection Grabage) ne recyclez pas la mémoire occupée par l'extérieur, car l'exécution de la fonction interne de l'extérieur dépend des variables de l'extérieur. (Une autre explication: Extérieur est la fonction parentale de l'intérieur, l'intérieur est affecté à une variable globale, ce qui fait que l'intérieur est en mémoire, et l'existence de l'intérieur dépend de l'extérieur, car un extérieur est toujours en mémoire et ne sera pas collecté et recyclé après la fin de l'appel).
La fermeture a la permission d'accéder à toutes les variables à l'intérieur de la fonction.
Lorsqu'une fonction renvoie une fermeture, la portée de la fonction sera enregistrée en mémoire jusqu'à ce que la fermeture n'existe pas.
Fermetures et variables
En raison du mécanisme de la chaîne de portée, la fermeture ne peut obtenir que la dernière valeur contenant n'importe quelle variable dans la fonction. Voir l'exemple suivant:
fonction f () {var rs = []; pour (var i = 0; i <10; i ++) {rs [i] = fonction () {return i; }; } return rs;} var fn = f (); for (var i = 0; i <fn.length; i ++) {console.log ('fonction fn [' + i + '] () valeur de retour:' + fn [i] ());}Les fonctions renvoient un tableau. En surface, il semble que chaque fonction doit renvoyer sa propre valeur d'index. En fait, chaque fonction renvoie 10. En effet, la chaîne de portée de la première fonction contient les objets actifs de la fonction f, et ils se réfèrent à la même variable i. Lorsque la fonction f revient, la valeur de la variable I est 10. À l'heure actuelle, chaque fonction enregistre le même objet variable de la variable i. Nous pouvons forcer la fermeture à se comporter comme prévu en créant une autre fonction anonyme.
fonction f () {var rs = []; for (var i = 0; i <10; i ++) {rs [i] = fonction (num) {return function () {return num; }; }(je); } return rs;} var fn = f (); for (var i = 0; i <fn.length; i ++) {console.log ('fonction fn [' + i + '] () valeur de retour:' + fn [i] ());}Dans cette version, au lieu d'attribuer directement la fermeture au tableau, nous définissons une fonction anonyme et attribuons le résultat de l'exécution immédiatement de la fonction anonyme au tableau. Ici, la fonction anonyme a un paramètre num. Lorsque nous appelons chaque fonction, nous passons dans la variable i. Étant donné que les paramètres sont passés par valeur, la variable je sera copiée dans le paramètre num. À l'intérieur de cette fonction anonyme, une fermeture accès à Num est créée et retournée. De cette façon, chaque fonction du tableau RS a une copie de sa propre variable NUM, de sorte que différentes valeurs peuvent être renvoyées.
Cet objet en fermeture
var name = 'jack'; var o = {name: 'bingdian', getName: function () {return function () {return this.name; }; }} console.log (o.getName () () ()); // jackvar name = 'jack'; var o = {name: 'bingdian', getName: function () {var self = this; return function () {return self.name; }; }} console.log (o.getName () () ()); // BingdianFuite de mémoire
function AssignHandler () {var el = document.getElementById ('Demo'); el.onclick = function () {console.log (el.id); }} assignHandler ();Le code ci-dessus crée une fermeture en tant que gestionnaire d'événements El Element, et cette fermeture crée une référence circulaire. Tant que la fonction anonyme existe, le nombre de références EL est d'au moins 1, car la mémoire qu'il occupe ne sera jamais recyclée.
function AssignHandler () {var el = document.getElementById ('Demo'); var id = el.id; el.onclick = function () {console.log (id); } el = null;} assignHandler ();Définition de la variable EL NULL peut déréférence de l'objet DOM et s'assurer qu'il consomme la mémoire normalement.
Imiter la portée au niveau du bloc
L'instruction définie dans n'importe quelle paire d'accès bouclées ({et}) appartient à un bloc, et toutes les variables définies dans ce domaine sont invisibles en dehors du bloc de code, que nous appelons la portée au niveau du bloc.
(function () {// Scope de niveau de bloc}) ();Application des fermetures
Protégez la sécurité des variables dans la fonction. Comme dans l'exemple précédent, seule la fonction intérieure peut accéder à I dans la fonction extérieure, mais ne peut être accessible via d'autres canaux, protégeant ainsi la sécurité de i.
Maintenez une variable en mémoire. Comme dans l'exemple précédent, en raison de la fermeture, le i dans l'extérieur de la fonction existe toujours en mémoire, donc chaque fois que RS () est exécuté, je serai ajouté 1.