Dans le processus d'apprentissage front-end, nous rencontrerons inévitablement de nombreux problèmes, c'est pourquoi nous parlerons aujourd'hui de deux questions du point de vue d'un débutant :
Qu'est-ce qu'une fermeture ?
Quelles sont les fonctions des fermetures ?
En fait, les fermetures sont partout lorsque l’on apprend JavaScript, il suffit de pouvoir les reconnaître et les accepter. Les fermetures ne sont pas un outil qui nécessite l'apprentissage d'une nouvelle syntaxe ou d'un nouveau modèle. Les fermetures sont une conséquence naturelle de l'écriture de code basé sur la portée lexicale. Nous avons rarement besoin de créer intentionnellement des fermetures lors de l'écriture de code.
Je crois que beaucoup d'amis marmonnent déjà dans leur cœur en ce moment, quelle est cette portée lexicale ? Ne paniquez pas, écoutez-moi lentement. Bref, la portée lexicale est la portée définie au stade lexical. En d'autres termes, la portée lexicale est déterminée par l'endroit où vous placez les variables et les portées au niveau du bloc lorsque vous écrivez votre code, de sorte que la portée reste inchangée lorsque l'analyseur lexical traite le code (la plupart du temps). ——"JavaScript que vous ne connaissez pas"
Prenons d'abord un exemple :
function test(){ vararr = [] pour(var i=0;i<10;i++){ arr[i]=fonction(){ console.log(i); } } retour arr } var monArr = test() // monArr[0]() // monArr[1]() //... pour(var j = 0; j < 10; j++){ monArr[j]() } //Afin d'éviter tout ennui, une deuxième boucle est utilisée ici pour appeler la fonction dans la première boucle de la fonction de test et imprimer dix résultats.
Analysons d'abord ce code : lorsque ce code est exécuté, selon le bon sens, il. doit être analysé Il affiche dix nombres de 0 à 9 dans l'ordre ; mais la boucle for ne prend pas de temps à s'exécuter (négligée en microsecondes). Lorsque la fonction test renvoie arr, il y a 10 function(){console. );}, la fonction dans le tableau n'est pas exécutée à ce moment-là. Lorsque var myArr = test() appelle la fonction de test, puisque le temps d'exécution de la boucle for est ignoré, i est déjà 10 à ce moment, alors qu'est-ce que c'est ? imprimé est 10 sur 10.
Je pense que quelqu'un demandera à ce moment-là, qu'est-ce que cela a à voir avec la fermeture dont nous allons parler ? Donc si nous modifions légèrement ce code et le transformons en accumulateur, comment pouvons-nous l'implémenter ?
Je crois qu’il y aura des gros bonnets à ce moment-là qui diront : n’est-ce pas simple ?
Remplacez la définition var par une définition let afin que la première boucle for devienne une portée au niveau du bloc, puis qu'elle puisse devenir un accumulateur. Bien sûr, il n'y a pas de problème,
mais ce dont nous parlons aujourd'hui, c'est de savoir comment implémenter un accumulateur dans ES5. Jetons ensuite un œil au code suivant :
function test(){ vararr = [] pour(var i=0;i<10;i++){ (fonction(j){ arr[j]=fonction(){ console.log(j); } })(je) } retour arr } var monArr = test() pour(var j = 0; j < 10; j++){ monArr[j]() }
Les amis prudents découvriront certainement qu'il s'agit de changer le corps de la fonction dans la boucle en une fonction auto-exécutable, mais le résultat de sortie à ce moment est de sortir dix nombres de 0 à 9 dans l'ordre, et cela inclut le package de fermeture, lorsque nous commencerons à exécuter ce code, la deuxième boucle for sera appelée dix fois. Lorsque chaque fonction auto-exécutable sera exécutée, un objet AO de la fonction auto-exécutable sera créé. Il y a un objet AO dans cette fonction auto-exécutable. .Le nom de l'attribut est j.Normalement, une fois la fonction d'exécution exécutée, son objet AO doit être détruit. Cependant, lorsque myarr[j] () est exécuté, l'objet AO de arr[j] est en haut de la chaîne de portée. C'est maintenant que j'ai cherché le nom de l'attribut j, mais je ne l'ai pas trouvé. J'ai cherché dans la chaîne de portée et je l'ai trouvé dans l'objet AO de la fonction auto-exécutable. Par conséquent, lorsque la fonction auto-exécutable se termine, son AO. L'objet ne sera pas recyclé par le mécanisme de récupération de place, sinon une erreur sera signalée lors de l'exécution de myarr[j] () et une fermeture sera formée.
Prenons un autre exemple
de fonction a(){. fonction b(){ var bbb = 234 console.log(aaa); } var aaa = 123 return b // b est né en a, mais a été sauvé} varglob = 100 var démo = a()Nous utilisons d'abord la pré-compilation pour analyser le code de
demo()
.Définissons d'abord un objet GO global et trouvons la déclaration de variable globale qui sera utilisée comme nom d'attribut de GO. n'est pas défini.Trouvez-le dans la déclaration globale.Pour la déclaration de fonction, le nom de la fonction est utilisé comme nom d'attribut de l'objet GO et la valeur est affectée au corps de la fonction. À ce stade, cela devrait être GO{ glob: undefined--->100; demo: undefined; a: fa(){} }; Créez ensuite un AO{ aaa: undefined--->123;b: fb() pour function a {} }, et enfin précompiler la fonction b dans la fonction a pour créer un AO de b { b : undefined--->234} ; à ce stade, l'ordre de la chaîne de portée est 1. Objet AO de la fonction b ; 2. Objet AO de la fonction a ; 3. Objet GO global. Lorsque nous imprimons aaa dans la fonction b, nous commençons par le haut de la chaîne de portée. S'il n'y a pas d'aaa dans l'objet AO de la fonction b, nous rechercherons le long de la chaîne de portée pour trouver l'AO de la fonction de deuxième niveau a. . Le but est de trouver la valeur de aaa comme 123 et d'afficher le résultat.
Si nous ne l'analysions pas du point de vue de la précompilation, nous penserions que aaa devrait signaler une erreur à ce moment-là. Lorsque var demo = a() est exécuté, lorsque l'exécution de la fonction a se termine, alors l'objet AO correspondant à. a doit être détruit. Selon l'analyse du bon sens : lorsque nous exécutons la démo, la chaîne de portée doit créer l'objet AO et l'objet GO de b. À ce stade, il n'y a que l'objet AO de b et aucun objet AO de a. La valeur de aaa ne doit pas être imprimée, mais à ce moment, la valeur de aaa La valeur est 123, ce qui signifie que l'objet AO de a n'a pas été détruit, alors pourquoi ? La raison en est qu'une fermeture est créée ici. Lorsque l'exécution de var demo = a() est terminée, le mécanisme de récupération de place demandera : Frère une fonction, je pense que vous avez fini de l'exécuter. Votre mémoire en cours d'exécution peut-elle m'être libérée. ? , mais à ce moment-là, la fonction a ne pouvait que secouer la tête, impuissante, et dire : Frère, je ne suis pas sûr d'avoir terminé l'exécution. Après l'avoir exécutée, j'ai créé a b, mais b n'est pas sous mon contrôle, donc je le suis. Je ne sais pas si b. a été appelé, donc je ne suis pas sûr d'avoir terminé l'exécution. Puisque vous ne le savez pas, je ne le recyclerai pas. Je devrais signaler une erreur, donc pour le moment, un objet AO n'est pas recyclé.
Je pense qu'à travers ces deux exemples, vous avez déjà une compréhension générale des fermetures. Parlons ensuite des fonctions des fermetures.
La fonctionde
fermeture est
- peut
- d'implémenter des variables publiques. Par exemple : l'accumulateur (3.js)
- être mis en cache
- pour réaliser l'encapsulation, la privatisation
- et le développement modulaire des attributs pour éviter la contamination des variables globales.
3.js).
nombre de variables = 0 fonction ajouter() { nombre de retours++ } console.log(ajouter()); console.log(ajouter()); console.log(add());
Il s'agit d'un code d'accumulation relativement courant, mais si pendant notre stage ou même au travail, l'entreprise vous demande d'encapsuler l'accumulateur dans un code modulaire, alors à ce moment-là, pour le bien du module Nous essayons d'éviter autant que possible de définir des variables globales, mais comment pouvons-nous y parvenir sans définir de variables globales. À ce stade, nous pouvons utiliser des fermetures ;
fonction ajouter() { nombre de variables = 0 fonction a() { ++compte console.log(count); } retourner un } var res = ajouter() res() res() //Une fois la fonction add terminée, l'objet AO de add n'est pas détruit, car après l'exécution de la fonction add, le a renvoyé forme une fermeture sans savoir s'il a été appelé, afin qu'il puisse être encapsulé dans un module sans utiliser variables globales.
Voici donc quelques-unes de mes opinions personnelles sur les fermetures et leurs fonctions. À l'heure actuelle, je n'ai qu'une compréhension superficielle des fermetures. Après une étude et une amélioration ultérieures, il y aura des articles ultérieurs sur les fermetures. Merci d'avoir regardé. corriger et progresser ensemble.