Explication détaillée de la fermeture js

Fermeture

De vraies questions classiques

  • Qu'est-ce qu'une fermeture ? Quels sont les scénarios d’application des fermetures ? Comment détruire la fermeture ?

Qu'est-ce que la fermeture

La fermeture est un point de connaissance très important en JavaScript , et c'est également l'un des points de connaissance les plus susceptibles d'être posés lors de nos entretiens front-end.

Ouvrez " JavaScript Advanced Programming" et " JavaScript Definitive Guide", et vous constaterez qu'il existe différentes explications sur les fermetures. En recherchant du contenu sur les fermetures sur Internet, vous constaterez également qu'il existe différentes opinions, ce qui fait que cette connaissance est elle-même pertinente. paraître... Un peu mystérieux, voire un peu fantastique.

Alors, ce point de connaissance est-il vraiment si profond ?

Non! En fait, il est très facile de comprendre les fermetures en JavaScript , mais avant cela, vous devez connaître les deux points de connaissances suivants :

  • Portée et chaîne de portée en JavaScript
  • Collecte des déchets en JavaScript

Nous passons ici brièvement en revue ces deux points de connaissance :

1. Portée et chaîne de portée en JavaScript

  • La portée est un territoire indépendant, de sorte que les variables ne seront pas divulguées ou exposées, et que les variables portant le même nom dans différentes portées ne seront pas en conflit.
  • La portée est déterminée lorsqu'elle est définie et ne change pas.
  • Si la valeur n'est pas trouvée dans la portée actuelle,elle sera recherchée dans la portée supérieure jusqu'à ce que la portée globale soit trouvée.La chaîne formée par un tel processus de recherche est appelée chaîne de portée.

2. Collecte des déchets en JavaScript

  • L' environnement d'exécution Javascript est responsable de la gestion de la mémoire utilisée lors de l'exécution du code, ce qui implique un mécanisme de garbage collection.
  • Le garbage collector trouvera périodiquement (périodiquement) les variables qui ne sont plus utilisées. Tant que la variable n'est plus utilisée, elle sera recyclée par le garbage collector puis libérera sa mémoire. Si la variable est toujours utilisée, elle ne sera pas recyclée.

OK , avec ces deux points de connaissance à l'esprit, regardons ce qu'est la fermeture.

La fermeture n'est pas une technique spécifique, mais un phénomène qui fait que lorsqu'une fonction est définie, les informations de l'environnement peuvent être utilisées dans la fonction. En d’autres termes, lors de l’exécution d’une fonction, chaque fois que des données externes sont utilisées dans la fonction, une fermeture est créée.

La chaîne de portée est exactement le moyen de mettre en œuvre la clôture.

Quoi? Tant que des données externes sont utilisées dans une fonction, une fermeture est créée ?

Vraiment? Ci-dessous, nous pouvons le prouver :

image-20211227145016552

Dans le code ci-dessus, nous définissons une variable i dans la fonction a , puis imprimons la variable i . Pour la fonction a , la variable i existe dans sa propre portée de fonction , nous pouvons donc voir que la variable i existe en Local lors du débogage .

Modifions légèrement le code ci-dessus, comme indiqué ci-dessous :

image-20211227145521272

Dans le code ci-dessus, nous plaçons l'action de déclarer la variable i en dehors de la fonction a . Cela signifie que la fonction a ne peut plus trouver la variable i dans sa propre portée . Que fera-t-elle ?

Si vous avez étudié la chaîne de portée, vous devez savoir qu'elle apparaîtra couche par couche tout au long de la chaîne de portée. Cependant, comme mentionné ci-dessus lors de l'introduction des fermetures, si cela se produit, c'est-à-dire lorsque la fonction utilise des données externes, une fermeture sera créée.

En observant attentivement la zone de débogage, nous constaterons que i est placé dans Closure à ce moment-là , confirmant ainsi notre déclaration précédente.

Vous voyez donc que les fermetures ne sont pas si difficiles à comprendre. Lorsque vous sentez qu'un mot est particulièrement difficile pour vous, vous pouvez également utiliser la méthode de séparation des mots. C'est également une méthode éprouvée que je recommande.

"Fermé" peut être compris comme "fermé, boucle fermée", et "emballage" peut être compris comme "un espace similaire à un emballage". Par conséquent, la fermeture peut en fait être considérée comme un espace fermé. Alors à quoi sert cet espace ? En fait, il sert à stocker des variables.

image-20211227163947135

Alors, toutes les déclarations de variables sous une fonction seront-elles placées dans l'espace fermé de la fermeture ?

Pas vraiment, le fait de le mettre dans la fermeture dépend du fait que la variable soit référencée ailleurs, par exemple :

image-20211227164333723

Dans le code ci-dessus, aucune variable n'est créée dans la fonction c, mais i, j, k et x sont imprimés . Ces variables existent respectivement dans les fonctions a, b et la portée globale. Par conséquent, trois fermetures sont créées et la portée globale La valeur de i est stockée dans la fermeture , les valeurs des variables j et k sont stockées dans la fermeture a et la valeur de la variable x est stockée dans la fermeture b .

Mais si vous regardez attentivement, vous constaterez que la variable y dans la fonction b n'est pas placée dans la fermeture, donc le fait de la mettre dans la fermeture dépend du fait que la variable soit référencée.

Bien sûr, vous pourriez avoir un nouveau problème en ce moment : avec autant de fermetures, ne prennent-elles pas de place mémoire ?

En effet, si la fermeture se forme automatiquement, elle sera détruite. Par exemple:

image-20211227174043786

Dans le code ci-dessus, nous essayons d'imprimer la variable k à la ligne 16. Évidemment, une erreur sera signalée à ce moment-là. En définissant un point d'arrêt sur la ligne 16 pour le débogage, nous pouvons clairement voir qu'il n'y a pas de fermeture à ce moment Le garbage collector recyclera automatiquement les variables non référencées sans aucune utilisation de mémoire.

Bien sûr, je fais référence ici à la génération automatique de fermetures. Concernant les fermetures, nous devons parfois créer manuellement une fermeture en fonction des besoins.

Prenons l'exemple suivant :

function eat(){
    
    
    var food = "鸡翅";
    console.log(food);
}
eat(); // 鸡翅
console.log(food); // 报错

Dans l'exemple ci-dessus, nous avons déclaré une fonction appelée eat et l'avons appelée.

Le moteur JavaScript créera un contexte d'exécution pour la fonction eat , dans lequel la variable food est déclarée et affectée d'une valeur.

Lorsque cette méthode est exécutée, le contexte est détruit et la variable food disparaît. En effet, la variable food est une variable locale de la fonction eat . Elle agit dans la fonction eat et sera créée et détruite au fur et à mesure de la création du contexte d'exécution de eat . Ainsi, lorsque nous imprimerons à nouveau la variable alimentaire , une erreur sera signalée, nous indiquant que la variable n'existe pas.

Mais modifions légèrement ce code :

function eat(){
    
    
    var food = '鸡翅';
    return function(){
    
    
        console.log(food);
    }
}
var look = eat();
look(); // 鸡翅
look(); // 鸡翅

Dans cet exemple, la fonction eat renvoie une fonction et la variable locale food est accessible dans cette fonction interne . Appelez la fonction eat et attribuez le résultat à la variable look . Ce look pointe vers la fonction interne de la fonction eat , puis l'appelle et génère enfin la valeur de food .

La raison pour laquelle la nourriture est accessible est très simple : comme nous l'avons dit plus haut, le garbage collector ne recyclera que les variables qui ne sont pas référencées, mais une fois qu'une variable est encore référencée, le garbage collector ne recyclera pas cette variable. Dans l'exemple ci-dessus, il va de soi que la nourriture doit être détruite après avoir mangé est appelée , mais nous avons renvoyé la fonction anonyme à l'intérieur de eat vers l'extérieur , et cette fonction anonyme fait référence à food , donc le ramasse-miettes ne la recyclera pas. Oui, ceci c'est pourquoi lorsque cette fonction anonyme est appelée à l'extérieur, la valeur de la variable food peut toujours être imprimée.

À ce stade, un des avantages ou caractéristiques des fermetures a été révélé, à savoir :

  • Les fermetures permettent à l'environnement externe d'accéder aux variables locales à l'intérieur d'une fonction.
  • Les fermetures permettent aux variables locales d'être conservées et non détruites avec leur contexte.

Grâce à cette fonctionnalité, nous pouvons résoudre le problème de la pollution variable globale. Au début, lorsque JavaScript ne pouvait pas être modularisé, lorsque plusieurs personnes collaboraient, la définition d'un trop grand nombre de variables globales pouvait provoquer des conflits de noms de variables globales. Les fermetures étaient utilisées pour résoudre les appels de fonction aux variables et écrire des variables dans un espace indépendant. À l'intérieur, cela peut résoudre dans une certaine mesure, le problème de la pollution variable globale.

Par exemple:

var name = "GlobalName";
// 全局变量
var init = (function () {
    
    
    var name = "initName";
    function callName() {
    
    
        console.log(name);
        // 打印 name
    }
    return function () {
    
    
        callName();
        // 形成接口
    }
}());
init(); // initName
var initSuper = (function () {
    
    
    var name = "initSuperName";
    function callName() {
    
    
        console.log(name);
        // 打印 name
    }
    return function () {
    
    
        callName();
        // 形成接口
    }
}());
initSuper(); // initSuperName

Bon, à la fin de cette section, faisons un petit récapitulatif des fermetures :

  • Une fermeture est un espace fermé qui stocke les valeurs de la portée qui sont référencées ailleurs. En JavaScript , les fermetures sont implémentées via des chaînes de portée.

  • Tant que des données externes sont utilisées dans la fonction, une fermeture est créée. Dans ce cas, nous n'avons pas besoin de nous soucier de la fermeture créée lors du codage.

  • Nous pouvons également créer manuellement des fermetures par certains moyens, afin que l'environnement externe puisse accéder aux variables locales à l'intérieur de la fonction, afin que les variables locales puissent être continuellement enregistrées et non détruites avec leur contexte.

Problème classique de fermeture

Après avoir parlé des fermetures, intéressons-nous à un problème classique des fermetures.

for (var i = 1; i <= 3; i++) {
    
    
    setTimeout(function () {
    
    
        console.log(i);
    }, 1000);
}

Dans le code ci-dessus, notre résultat attendu est d'afficher les valeurs de la variable i sous la forme 1, 2 et 3 respectivement après 1 seconde . Cependant, le résultat de l'exécution est : 4, 4, 4 .

En fait, le problème réside dans le corps de fermeture. Vous voyez, setTimeout dans la boucle accède à sa variable externe i , formant une fermeture.

Il n'y a qu'une seule variable i , donc la même variable est accessible dans setTimeout qui boucle trois fois . Lorsque la boucle atteint la 4ème fois , la variable i augmente à 4 , la condition de boucle n'est pas remplie, la boucle se termine et le contexte se termine après l'exécution du code. Cependant, les trois setTimeouts attendent 1 seconde avant de s'exécuter. En raison de la fermeture, ils peuvent toujours accéder à la variable i , mais la valeur de la variable i est déjà 4 à ce moment- .

Pour résoudre ce problème, nous pouvons laisser la fonction anonyme dans setTimeout ne plus accéder aux variables externes, mais accéder à ses propres variables internes, comme suit :

for (var i = 1; i <= 3; i++) {
    
    
    (function (index) {
    
    
        setTimeout(function () {
    
    
            console.log(index);
        }, 1000);
    })(i)
}

De cette façon, il n'est pas nécessaire d'accéder à la variable que j'ai déclarée dans la boucle for dans setTimeout . Au lieu de cela, je transmets la valeur de la variable i à setTimeout en appelant une fonction pour passer des paramètres , afin qu'ils ne créent plus de fermeture, car la variable i peut être trouvée dans mon propre scope .

Bien sûr, il existe un moyen plus simple de résoudre ce problème, qui consiste à utiliser le mot-clé let dans ES6 .

La variable qu'elle déclare a une portée de bloc. Si vous la mettez dans une boucle, alors il y aura une nouvelle variable i à chaque fois qu'elle boucle , donc même s'il y a une fermeture, ce ne sera pas un problème, car chaque fermeture enregistre un i différent variable, alors le problème sera résolu maintenant.

for (let i = 1; i <= 3; i++) {
    
    
    setTimeout(function () {
    
    
        console.log(i);
    }, 1000);
}

vraie question réponse

  • Qu'est-ce qu'une fermeture ? Quels sont les scénarios d’application des fermetures ? Comment détruire la fermeture ?

Une fermeture est un espace fermé qui stocke les valeurs de la portée qui sont référencées ailleurs. En JavaScript , les fermetures sont implémentées via des chaînes de portée.

Tant que des données externes sont utilisées dans la fonction, une fermeture est créée. Dans ce cas, nous n'avons pas besoin de nous soucier de la fermeture créée lors du codage.

Nous pouvons également créer manuellement des fermetures par certains moyens, afin que l'environnement externe puisse accéder aux variables locales à l'intérieur de la fonction, afin que les variables locales puissent être continuellement enregistrées et non détruites avec leur contexte.

Le recours aux fermetures peut résoudre le problème de la pollution variable globale.

S'il s'agit d'une fermeture générée automatiquement, nous n'avons pas à nous soucier de la destruction de la fermeture. S'il s'agit d'une fermeture créée manuellement, nous pouvons définir la variable référencée sur null, c'est-à-dire effacer manuellement la variable, afin que la prochaine heure à laquelle le garbage collector JavaScript effectue le garbage collection Lors du recyclage, s'il s'avère que cette variable n'a plus de référence, la quantité définie sur null sera recyclée.


-EOF- _ _

Je suppose que tu aimes

Origine blog.csdn.net/qq_53461589/article/details/132740029
conseillé
Classement