Dans l'article précédent, nous avons donné un aperçu de la pré-explication. Avant d'écrire cet article de blog, nous avions prévu d'écrire quelques cas classiques. Étant donné que ces cas sont plus complets, nous avons ce billet de blog étape par étape, ce qui est plus facile à apprendre et en profondeur JavaScript.
séquence
Un collègue est allé à l'interview et l'intervieweur a posé une question: écrivez-vous une fermeture et je vais le lire? Mon collègue a donc rapidement écrit le code suivant:
La copie de code est la suivante:
fonction fn () {
alert ('Hello JavaScript Close !!!'); // putain, le texte n'est pas bon, vous ne pouvez écrire les mots de la fermeture qu'après avoir trouvé une traduction
}
fn ();
Ensuite, l'intervieweur secoua la tête et dit: "Comment cela peut-il être appelé une fermeture?" En fin de compte, les deux avaient un différend et le collègue est parti de manière décisive. Quel est le truc de l'intervieweur? (Cette histoire est purement fictive, s'il y a une similitude, c'est purement coïncidence)
Les fermetures peuvent être une technologie «haut de gamme et difficile» dans les yeux de nombreuses personnes, mais aux yeux de nombreuses personnes, ils peuvent être considérés comme des fermetures:
Exemple 1:
La copie de code est la suivante:
fonction fn () {
return function () {
alert ('Exemple 1');
}
}
fn () ();
Exemple 1 PS: Celui-ci n'a pas l'air très avancé, il semble que cette personne ne soit pas très bonne!
Exemple 2:
La copie de code est la suivante:
;(fonction () {
alert ('Exemple 2');
}) ();
Exemple 2 PS: Cela semble plus avancé que le précédent, et il y a un point-virgule avant le premier support. Pourquoi ajouter un point-virgule? Eh bien, laissons d'abord cette question ici, et nous en parlerons plus tard.
Exemple 3:
La copie de code est la suivante:
~ fonction fn () {
alerte ('Exemple 3')
} ();
Exemple 3 PS: C'est le plus avancé, c'est tellement incroyable. Je lis moins, ne me mens pas!
Le maître de Lun n'est pas très bon en lecture, et il ne peut qu'écrire ces trois "fermetures". Je crois que les blogueurs peuvent écrire des "fermetures" plus et meilleures; Ici, veuillez d'abord en suspendre mes bêtises, puis étudier le mécanisme de fonctionnement de la fonction. Il semble que quelqu'un le sait déjà, ce doit être la portée. Je ne veux vraiment pas ajouter cette portée au titre, donc je pense toujours qu'elle est presque significative. Ces choses sont à l'origine ensemble, alors pourquoi devrais-je les répéter? Vieille habitude, téléchargez d'abord le code:
La copie de code est la suivante:
var n = 10;
fonction fn () {
alerte (n);
var n = 9;
alerte (n);
}
fn ();
Pour le dire simplement, nous dessinons des images (le maître utilise uniquement le logiciel de dessin qui est livré avec Windows. S'il y en a de meilleurs, veuillez le recommander aux blogueurs) pour l'analyser:
Analyse 1
De la figure, nous voyons deux lunettes, l'une est la portée de la fenêtre (portée de niveau supérieur), et l'autre est une portée privée formée lorsque FN est appelé; Alors qu'est-ce que la portée? La portée est en fait l'environnement de l'exécution du code. Par exemple, l'environnement d'apprentissage d'un élève est une école, ce qui équivaut à sa portée. Si l'étudiant est méchant et se rend souvent au cycle de café pour jouer à des jeux la nuit, cela équivaut à former un environnement privé, qui est un cybercafé. D'accord! Cette Lizi est ainsi jusqu'à ce qu'elle ressemble au maître lui-même, et elle ne peut s'empêcher de soupirer: "Si vous ne travaillez pas dur quand vous êtes jeune, vous serez botté lorsque vous grandissez." Revenons au sujet. En fait, la définition de la fonction FN fait référence à la description d'un morceau de code (la boîte rouge sur la figure). Lorsque ce FN est appelé (la boîte verte de la figure), une portée sera formée. Bien sûr, le code de cette étendue sera également pré-expulsé avant exécution. Je ne vous dirai pas que cette portée sera détruite après l'exécution. Ce FN est à nouveau appelé, et il formera également une nouvelle portée, puis pré-expulsé avant l'exécution, puis l'exécution du code, et finalement détruit après l'exécution.
Comprendre les fermetures
Nous savons que lorsqu'une fonction est appelée, une portée privée (environnement d'exécution) formera une portée privée (environnement d'exécution), et cette portée privée est une fermeture. Avec le recul, la fermeture est-elle toujours le légendaire "haut de gamme et pas facile à monter"? Revenons sur la première histoire de l'interview et les trois exemples que j'ai écrits, ce sont en fait des fermetures. Pour être précis, ces trois exemples sont des formes courantes de fermetures.
Scénarios d'application
Maintenant, il y a une exigence: il y a une balise UL dans la page HTML, et il y a 5 étiquettes Li sous UL. Vous avez besoin de cliquez sur un Li, et l'index (index commence à partir de 0) où le li cliqué est apparu. La structure HTML est la suivante:
La copie de code est la suivante:
<ul id = "ul">
<li> Liste 1 </li>
<li> Liste 2 </li>
<li> Liste 3 </li>
<li> Liste 4 </li>
<li> Liste 5 </li>
</ul>
J'étais intelligent et j'ai rapidement écrit le code suivant:
La copie de code est la suivante:
var lis = document.getElementById ('ul'). GetElementsByTagName ('li');
pour (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = function () {
alerte (i);
};
}
Test final pour voir si cette exigence est parfaitement mise en œuvre:
J'ai trouvé que peu importe le nombre de fois où j'ai cliqué, ce résultat apparaîtra à la fin. Le résultat souhaité est: Cliquez sur Liste 1 pour faire apparaître 0, cliquez sur Liste 2 pour faire apparaître 1, cliquez sur Liste 3 pour faire apparaître 2 ... En ce moment, je veux juste utiliser cette image pour décrire mon humeur actuelle:
(À quoi cela ressemble-t-il lorsque le prototype ne fonctionne pas comme les exigences de conception pendant la démonstration)
Que dois-je faire? Pourquoi 5 apparaît-il toujours? C'est très correct en théorie! Dessinez une image pour analyser:
En fait, nous donnons simplement à chaque li onclick, qui est en fait une chaîne de description de fonction qui enregistre. Le contenu de cette chaîne est le contenu dans la boîte rouge de l'image ci-dessus. Si vous ne le croyez toujours pas, j'ai l'image et la vérité:
Entrez: lis [4] .onclick dans la console Chrome, et sa valeur est la description de la fonction. Lorsque nous cliquons sur la cinquième liste, il est en fait équivalent à LIS [4] .OnClick (). Après avoir appelé cette description de la fonction, nous savons que lorsque la fonction est exécutée, elle formera une portée privée. Dans cette portée privée, il sera d'abord pré-expliqué, puis le code sera exécuté. En ce moment, nous chercherons i. Il n'y a pas de moi sous la portée privée actuelle, puis je trouve que je sous la portée de la fenêtre, donc 5 apparaît à chaque fois que vous cliquez.
De toute évidence, le code ci-dessus ne peut pas répondre à cette exigence. Il est incorrect d'écrire notre code. Réfléchissons aux raisons du problème? En fait, la raison en est que chaque fois que je clique, il est lu dans la fenêtre I Under. Pour le moment, la valeur de ceci est déjà 5, donc le code suivant est disponible:
Méthode 1:
La copie de code est la suivante:
var lis = document.getElementById ('ul'). GetElementsByTagName ('li');
fonction fn (i) {
return function () {
alerte (i);
}
}
pour (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = fn (i);
}
Méthode 2:
La copie de code est la suivante:
var lis = document.getElementById ('ul'). GetElementsByTagName ('li');
pour (var i = 0, len = lis.length; i <len; i ++) {
; (fonction (i) {
lis [i] .onclick = function () {
alerte (i);
};
})(je);
}
Méthode 3:
La copie de code est la suivante:
var lis = document.getElementById ('ul'). GetElementsByTagName ('li');
pour (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = fonction fn (i) {
return function () {
alerte (i);
}
}(je);
}
J'ai écrit trois méthodes en une seule respiration, et les idées sont les mêmes, qui est de stocker cette variable I dans une variable privée. Ici, je ne parlerai que de la méthode deux. Bien sûr, si vous comprenez l'un d'eux, vous comprendrez le reste. Selon la convention, dessions une image et analysons-la pas à pas:
J'ai décrit l'ensemble de l'exécution de code en détail. Il convient de noter que l'attribut onClick de chaque Li doit occuper la portée (Fonction (i) {…}) (i). Lorsque cette fonction est exécutée, elle ne sera pas détruite car elle est occupée par le LI externe (ce Li est sous la portée de la fenêtre), de sorte que cette portée ne sera pas détruite. Lorsque vous cliquez sur n'importe quel li, fonction () {alert (i); } sera exécuté et une portée se formera également. Cette portée n'a pas i. Il ira à (function () {…}) (i) SPOPE pour trouver i, et enfin trouver i dans le paramètre formel, et la valeur de ce paramètre formel est transmise pendant la boucle for; Cet exemple utilise intelligemment les fermetures pour stocker la valeur, résolvant parfaitement le problème.
PS: Je viens de dire (fonction (i) {…}) (i) Pourquoi j'ai ajouté un point-virgule devant lui pour empêcher la déclaration précédente d'oublier pour ajouter un point-virgule, ce qui fait que JavaScript fait des erreurs pendant l'analyse, et c'est tout. Bien sûr, l'un des scénarios d'application ci-dessus est le principe d'implémentation des TAB. Il peut y avoir d'autres méthodes d'implémentation, telles que les méthodes d'attribut personnalisées et la recherche d'index via des relations de nœud DOM. Le maître adopte cette méthode juste pour approfondir sa compréhension des fermetures.
Résumer
Les fermetures ne sont pas aussi grandes que les légendaires. Le noyau est de comprendre les définitions et les appels de la fonction. Une nouvelle portée privée sera formée lors de l'appel d'une fonction. Lorsqu'une portée est occupée par l'extérieur, la portée ne sera pas détruite. Le maître de Lu lit très peu. S'il y a de mauvaises choses, veuillez me corriger par les blogueurs. En même temps, je voudrais vous remercier pour votre soutien à l'article du maître de Luke.