Comment expliqueriez-vous les fermetures JavaScript à une personne connaissant les concepts qui les composent (par exemple, les fonctions, les variables, etc.), mais ne comprenant pas les fermetures elles-mêmes?
J'ai vu l'exemple de Scheme donné sur Wikipédia, mais malheureusement, cela n'a pas aidé.
closure
code difficile à lire , par opposition à celuiObject Literal
qui se réutilise et réduit tout de même les frais généraux, tout en nécessitant 100% moins de code d'emballage.Réponses:
Une fermeture est un couple de:
Un environnement lexical fait partie de chaque contexte d'exécution (cadre de pile) et est une correspondance entre les identificateurs (c'est-à-dire les noms de variables locaux) et les valeurs.
Chaque fonction en JavaScript conserve une référence à son environnement lexical externe. Cette référence est utilisée pour configurer le contexte d'exécution créé lors de l'appel d'une fonction. Cette référence permet au code à l'intérieur de la fonction de "voir" les variables déclarées à l'extérieur de la fonction, indépendamment du moment et de l'endroit où la fonction est appelée.
Si une fonction a été appelée par une fonction, qui à son tour a été appelée par une autre fonction, une chaîne de références aux environnements lexicaux externes est créée. Cette chaîne est appelée chaîne de portée.
Dans le code suivant,
inner
forme une fermeture avec l'environnement lexical du contexte d'exécution créé quandfoo
est invoqué, se fermant sur la variablesecret
:En d'autres termes: en JavaScript, les fonctions portent une référence à une "boîte d'état" privée, à laquelle seules elles (et toutes les autres fonctions déclarées dans le même environnement lexical) ont accès. Cette boîte d'état est invisible pour l'appelant de la fonction, offrant un excellent mécanisme de masquage et d'encapsulation des données.
Et rappelez-vous: les fonctions en JavaScript peuvent être transmises comme des variables (fonctions de première classe), ce qui signifie que ces paires de fonctionnalités et d'état peuvent être transmises autour de votre programme: similaire à la façon dont vous pourriez passer une instance d'une classe en C ++.
Si JavaScript n'avait pas de fermetures, alors plus d'état devrait être passé explicitement entre les fonctions , rendant les listes de paramètres plus longues et le code plus bruyant.
Donc, si vous voulez qu'une fonction ait toujours accès à un état privé, vous pouvez utiliser une fermeture.
... et souvent nous ne voulons l' état associé à une fonction. Par exemple, en Java ou C ++, lorsque vous ajoutez une variable d'instance privée et une méthode à une classe, vous associez l'état à la fonctionnalité.
En C et dans la plupart des autres langages courants, après le retour d'une fonction, toutes les variables locales ne sont plus accessibles car le stack-frame est détruit. En JavaScript, si vous déclarez une fonction dans une autre fonction, les variables locales de la fonction externe peuvent rester accessibles après son retour. De cette façon, dans le code ci-dessus,
secret
reste disponible pour l'objet fonctioninner
, après son retourfoo
.Utilisations des fermetures
Les fermetures sont utiles lorsque vous avez besoin d'un état privé associé à une fonction. Il s'agit d'un scénario très courant - et rappelez-vous: JavaScript n'avait pas de syntaxe de classe jusqu'en 2015, et il n'a toujours pas de syntaxe de champ privé. Les fermetures répondent à ce besoin.
Variables d'instance privée
Dans le code suivant, la fonction se
toString
ferme sur les détails de la voiture.Programmation fonctionnelle
Dans le code suivant, la fonction se
inner
ferme sur les deuxfn
etargs
.Programmation orientée événement
Dans le code suivant, la fonction se
onClick
ferme sur la variableBACKGROUND_COLOR
.Modularisation
Dans l'exemple suivant, tous les détails d'implémentation sont masqués dans une expression de fonction immédiatement exécutée. Les fonctions
tick
ettoString
fermer sur l'état privé et les fonctions dont ils ont besoin pour terminer leur travail. Les fermetures nous ont permis de modulariser et d'encapsuler notre code.Exemples
Exemple 1
Cet exemple montre que les variables locales ne sont pas copiées dans la fermeture: la fermeture conserve une référence aux variables d'origine elles-mêmes . C'est comme si le cadre de pile restait vivant en mémoire même après la sortie de la fonction externe.
Exemple 2
Dans le code suivant, trois méthodes
log
,increment
etupdate
toutes se ferment sur le même environnement lexical.Et à chaque
createObject
appel, un nouveau contexte d'exécution (cadre de pile) est créé et une toute nouvelle variablex
, et un nouvel ensemble de fonctions (log
etc.) sont créés, qui se ferment sur cette nouvelle variable.Exemple 3
Si vous utilisez des variables déclarées en utilisant
var
, veillez à bien comprendre quelle variable vous fermez. Les variables déclarées à l'aidevar
sont hissées. C'est beaucoup moins un problème dans le JavaScript moderne en raison de l'introduction delet
etconst
.Dans le code suivant, chaque fois dans la boucle, une nouvelle fonction
inner
est créée, qui se refermei
. Mais parce qu'ellevar i
est hissée hors de la boucle, toutes ces fonctions internes se ferment sur la même variable, ce qui signifie que la valeur finale dei
(3) est imprimée, trois fois.Points finaux:
function
de l'intérieur d'une autre fonction est l'exemple classique d'une fermeture, car l'état à l'intérieur de la fonction externe est implicitement disponible pour la fonction interne renvoyée, même après l'exécution de la fonction externe.eval()
intérieur d'une fonction, une fermeture est utilisée. Le texte que vouseval
pouvez référencer les variables locales de la fonction, et en mode non strict, vous pouvez même créer de nouvelles variables locales en utilisanteval('var foo = …')
.new Function(…)
(le constructeur Function ) à l'intérieur d'une fonction, il ne se ferme pas sur son environnement lexical: il se ferme sur le contexte global à la place. La nouvelle fonction ne peut pas référencer les variables locales de la fonction externe.Liens
la source
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
de developer.mozilla.org/en-US/docs/Web/JavaScript/Closureslet i = 0
au lieu devar i = 0
dans l'exemple 5, alors letestList()
imprimera ce que vous voulez à l'origine.Chaque fonction en JavaScript maintient un lien vers son environnement lexical externe. Un environnement lexical est une carte de tous les noms (par exemple, variables, paramètres) dans une étendue, avec leurs valeurs.
Ainsi, chaque fois que vous voyez le
function
mot - clé, le code à l'intérieur de cette fonction a accès aux variables déclarées à l'extérieur de la fonction.Cela se connectera
16
car la fonction sebar
ferme sur le paramètrex
et la variabletmp
, qui existent tous les deux dans l'environnement lexical de la fonction externefoo
.La fonction
bar
, avec son lien avec l'environnement lexical de la fonction,foo
est une fermeture.Une fonction n'a pas besoin de revenir pour créer une fermeture. Simplement en vertu de sa déclaration, chaque fonction se referme sur son environnement lexical englobant, formant une fermeture.
La fonction ci-dessus enregistrera également 16, car le code à l'intérieur
bar
peut toujours faire référence à un argumentx
et à une variabletmp
, même s'ils ne sont plus directement dans la portée.Cependant, comme il
tmp
est toujours suspendu à l'intérieur debar
la fermeture, il est disponible pour être incrémenté. Il sera incrémenté à chaque appelbar
.L'exemple le plus simple de fermeture est le suivant:
Lorsqu'une fonction JavaScript est invoquée, un nouveau contexte d'exécution
ec
est créé. Avec les arguments de fonction et l'objet cible, ce contexte d'exécution reçoit également un lien vers l'environnement lexical du contexte d'exécution appelant, ce qui signifie que les variables déclarées dans l'environnement lexical externe (dans l'exemple ci-dessus, les deuxa
etb
) sont disponibles à partir deec
.Chaque fonction crée une fermeture parce que chaque fonction a un lien avec son environnement lexical externe.
Notez que les variables elles - mêmes sont visibles de l'intérieur d'une fermeture, pas des copies.
la source
delete
échoue. Néanmoins, l'environnement lexical que la fonction transportera en tant que [[Scope]] (et utilisera finalement comme base pour son propre environnement lexical lorsqu'elle sera invoquée) est déterminé lorsque l'instruction qui définit la fonction est exécutée. Cela signifie que la fonction se ferme sur le contenu ENTIER de la portée en cours d'exécution, quelles que soient les valeurs auxquelles elle se réfère réellement et si elle échappe à la portée. Veuillez consulter les sections 13.2 et 10 duAVANT-PROPOS: cette réponse a été écrite lorsque la question était:
Je suis presque sûr que j'étais l'une des seules personnes à avoir tenté de prendre la question initiale au pied de la lettre. Depuis lors, la question a muté plusieurs fois, donc ma réponse peut maintenant sembler incroyablement idiote et hors de propos. Espérons que l'idée générale de l'histoire reste amusante pour certains.
Je suis un grand fan d'analogie et de métaphore lorsque j'explique des concepts difficiles, alors laissez-moi tenter ma chance avec une histoire.
Il était une fois:
Il y avait une princesse ...
Elle vivait dans un monde merveilleux plein d'aventures. Elle a rencontré son prince charmant, parcouru son monde sur une licorne, combattu des dragons, rencontré des animaux parlants et bien d'autres choses fantastiques.
Mais elle devrait toujours retourner dans son monde terne de corvées et d'adultes.
Et elle leur racontait souvent sa dernière aventure incroyable en tant que princesse.
Mais tout ce qu'ils verraient, c'est une petite fille ...
... raconter des histoires sur la magie et la fantaisie.
Et même si les adultes connaissaient de vraies princesses, ils ne croiraient jamais aux licornes ou aux dragons parce qu'ils ne pourraient jamais les voir. Les adultes ont dit qu'ils n'existaient que dans l'imagination de la petite fille.
Mais nous connaissons la vraie vérité; que la petite fille avec la princesse à l'intérieur ...
... est vraiment une princesse avec une petite fille à l'intérieur.
la source
story()
fonction, qui est la seule interface que l'littleGirl
instance expose dans le monde de la magie.story
donc la fermeture, mais si le code avait étévar story = function() {}; return story;
alors, celittleGirl
serait la fermeture. C'est du moins l'impression que j'ai de l'utilisation par MDN des méthodes «privées» avec fermetures : «Ces trois fonctions publiques sont des fermetures qui partagent le même environnement.story
est une fermeture faisant référence à l'environnement fourni dans le cadre deprincess
.princess
est également une autre fermeture implicite , c'est-à-dire que leprincess
et lelittleGirl
partageraient toute référence à unparents
tableau qui existerait dans l'environnement / la portée oùlittleGirl
existe etprincess
est défini.princess
ce qui est écrit. Malheureusement, cette histoire est maintenant un peu hors de propos sur ce fil. À l'origine, la question demandait «d'expliquer les fermetures JavaScript à un ancien de 5 ans»; ma réponse a été la seule à avoir tenté de le faire. Je ne doute pas que cela aurait échoué lamentablement, mais au moins cette réponse aurait pu avoir la chance de retenir l'intérêt d'un enfant de 5 ans.Prenant la question au sérieux, nous devrions découvrir ce qu'un enfant typique de 6 ans est capable de faire sur le plan cognitif, mais il est vrai que celui qui s'intéresse à JavaScript n'est pas si typique.
Sur le développement de l'enfant: 5 à 7 ans, il dit:
Nous pouvons utiliser cet exemple pour expliquer les fermetures, comme suit:
Nous pouvons coder cela en JavaScript comme ceci:
Autres points qui expliquent pourquoi les fermetures sont intéressantes:
makeKitchen()
appelez, une nouvelle fermeture est créée avec sa propre séparationtrashBags
.trashBags
variable est locale à l'intérieur de chaque cuisine et n'est pas accessible à l'extérieur, mais la fonction intérieure de lagetTrashBag
propriété y a accès.getTrashBag
fonction fait cela ici.la source
L'homme de paille
J'ai besoin de savoir combien de fois un bouton a été cliqué et de faire quelque chose à chaque troisième clic ...
Solution assez évidente
Maintenant, cela fonctionnera, mais cela empiète sur la portée externe en ajoutant une variable, dont le seul but est de garder une trace du nombre. Dans certaines situations, cela serait préférable car votre application externe pourrait avoir besoin d'accéder à ces informations. Mais dans ce cas, nous ne modifions que le comportement de chaque troisième clic, il est donc préférable de placer cette fonctionnalité dans le gestionnaire d'événements .
Considérez cette option
Remarquez quelques choses ici.
Dans l'exemple ci-dessus, j'utilise le comportement de fermeture de JavaScript. Ce comportement permet à n'importe quelle fonction d'avoir accès à l'étendue dans laquelle elle a été créée, indéfiniment. Pour l'appliquer pratiquement, j'appelle immédiatement une fonction qui renvoie une autre fonction, et parce que la fonction que je retourne a accès à la variable de comptage interne (en raison du comportement de fermeture expliqué ci-dessus), cela entraîne une étendue privée pour l'utilisation par le résultat fonction ... Pas si simple? Allons le diluer ...
Une fermeture simple sur une ligne
Toutes les variables en dehors de la fonction retournée sont disponibles pour la fonction retournée, mais elles ne sont pas directement disponibles pour l'objet fonction retourné ...
Tu piges? Ainsi, dans notre exemple principal, la variable count est contenue dans la fermeture et toujours disponible pour le gestionnaire d'événements, de sorte qu'il conserve son état d'un clic à l'autre.
En outre, cet état de variable privée est entièrement accessible, tant pour les lectures que pour l'affectation à ses variables de portée privées.
Voilà; vous encapsulez maintenant complètement ce comportement.
Article de blog complet (y compris les considérations jQuery)
la source
Les fermetures sont difficiles à expliquer car elles sont utilisées pour faire fonctionner certains comportements que tout le monde s'attend intuitivement à travailler de toute façon. Je trouve que la meilleure façon de les expliquer (et la façon dont j'ai appris ce qu'ils font) est d'imaginer la situation sans eux:
Que se passerait-il ici si JavaScript ne connaissait pas les fermetures? Remplacez simplement l'appel dans la dernière ligne par son corps de méthode (qui est essentiellement ce que font les appels de fonction) et vous obtenez:
Maintenant, où est la définition de
x
? Nous ne l'avons pas défini dans le champ d'application actuel. La seule solution est de laisserplus5
porter sa portée (ou plutôt celle de son parent). De cette façon,x
est bien défini et il est lié à la valeur 5.la source
TLDR
Une fermeture est un lien entre une fonction et son environnement lexical externe (c'est-à-dire tel qu'écrit), de sorte que les identificateurs (variables, paramètres, déclarations de fonction, etc.) définis dans cet environnement sont visibles de l'intérieur de la fonction, indépendamment du moment ou de où la fonction est invoquée.
Détails
Dans la terminologie de la spécification ECMAScript, une fermeture peut être dite implémentée par la
[[Environment]]
référence de chaque objet-fonction, ce qui pointe vers l' environnement lexical dans lequel la fonction est définie.Lorsqu'une fonction est invoquée via la
[[Call]]
méthode interne , la[[Environment]]
référence sur l'objet fonction est copiée dans la référence d'environnement externe de l' enregistrement d'environnement du contexte d'exécution nouvellement créé (cadre de pile).Dans l'exemple suivant, la fonction se
f
ferme sur l'environnement lexical du contexte d'exécution global:Dans l'exemple suivant, la fonction se
h
ferme sur l'environnement lexical de la fonctiong
, qui, à son tour, se ferme sur l'environnement lexical du contexte d'exécution global.Si une fonction interne est renvoyée par un externe, l'environnement lexical externe persistera après le retour de la fonction externe. En effet, l'environnement lexical externe doit être disponible si la fonction interne est finalement invoquée.
Dans l'exemple suivant, la fonction se
j
ferme sur l'environnement lexical de la fonctioni
, ce qui signifie que la variablex
est visible de l'intérieur de la fonctionj
, longtemps après l'i
exécution de la fonction:Dans une fermeture, les variables de l'environnement lexical externe elles - mêmes sont disponibles, pas des copies.
La chaîne des environnements lexicaux, liée entre les contextes d'exécution via des références d'environnement externes, forme une chaîne de portée et définit les identifiants visibles à partir d'une fonction donnée.
Veuillez noter que dans le but d'améliorer la clarté et la précision, cette réponse a été substantiellement modifiée par rapport à l'original.
la source
console.log
comme ça. Si quelqu'un d'autre est intéressé, il y en a plus: developer.mozilla.org/en-US/docs/DOM/…var
).OK, fan de fermetures de 6 ans. Voulez-vous entendre l'exemple le plus simple de fermeture?
Imaginons la situation suivante: un conducteur est assis dans une voiture. Cette voiture est à l'intérieur d'un avion. L'avion est à l'aéroport. La capacité du conducteur d'accéder à des choses à l'extérieur de sa voiture, mais à l'intérieur de l'avion, même si cet avion quitte un aéroport, est une fermeture. C'est ça. Lorsque vous atteignez 27 ans, regardez l' explication plus détaillée ou l'exemple ci-dessous.
Voici comment je peux convertir mon histoire d'avion en code.
la source
Il s'agit d'une tentative de dissiper plusieurs malentendus (possibles) sur les fermetures qui apparaissent dans certaines des autres réponses.
la source
J'ai écrit un article de blog il y a quelque temps pour expliquer les fermetures. Voici ce que j'ai dit à propos des fermetures pour savoir pourquoi vous en voudriez une.
En ce sens, ils laissent une fonction agir un peu comme un objet avec des attributs privés.
Article complet:
Alors, quels sont ces trucs de fermeture?
la source
devError = emailError("[email protected]", errorString)
et avoir ma propre version personnalisée d'une fonction emailError partagée?Les fermetures sont simples:
L'exemple simple suivant couvre tous les points principaux des fermetures JavaScript. *
Voici une usine qui produit des calculatrices qui peuvent s'ajouter et se multiplier:
Le point clé: chaque appel à
make_calculator
crée une nouvelle variable localen
, qui continue d'être utilisable par cette calculatriceadd
etmultiply
fonctionne longtemps après lesmake_calculator
retours.Si vous connaissez les cadres de pile, ces calculatrices semblent étranges: comment peuvent-elles continuer à accéder
n
après lesmake_calculator
retours? La réponse est d'imaginer que JavaScript n'utilise pas de "cadres de pile", mais utilise à la place des "cadres de tas", qui peuvent persister après l'appel de la fonction qui les a renvoyés.Les fonctions internes comme
add
etmultiply
, qui accèdent aux variables déclarées dans une fonction externe ** , sont appelées fermetures .C'est à peu près tout ce qu'il y a à fermer.
* Par exemple, il couvre tous les points de l'article "Fermetures pour les nuls" donné dans une autre réponse , à l'exception de l'exemple 6, qui montre simplement que les variables peuvent être utilisées avant d'être déclarées, un fait agréable à savoir mais sans aucun rapport avec les fermetures. Il couvre également tous les points de la réponse acceptée , à l'exception des points (1) que les fonctions copient leurs arguments dans des variables locales (les arguments de la fonction nommée) et (2) que la copie des nombres crée un nouveau nombre, mais la copie d'une référence d'objet vous donne une autre référence au même objet. Celles-ci sont également bonnes à savoir, mais encore une fois sans aucun rapport avec les fermetures. Il est également très similaire à l'exemple de cette réponse mais un peu plus court et moins abstrait. Il ne couvre pas le point decette réponse ou ce commentaire , qui est que JavaScript rend difficile de brancher le courantvaleur d'une variable de boucle dans votre fonction interne: l'étape de "connexion" ne peut être effectuée qu'avec une fonction d'assistance qui entoure votre fonction interne et est invoquée à chaque itération de boucle. (Strictement parlant, la fonction interne accède à la copie de la fonction d'aide de la variable, plutôt que d'avoir quoi que ce soit branché.) Encore une fois, très utile lors de la création de fermetures, mais ne fait pas partie de ce qu'est une fermeture ou de son fonctionnement. Il y a une confusion supplémentaire due aux fermetures fonctionnant différemment dans des langages fonctionnels comme ML, où les variables sont liées à des valeurs plutôt qu'à de l'espace de stockage, fournissant un flux constant de personnes qui comprennent les fermetures d'une manière (à savoir la manière de "se brancher") qui est tout simplement incorrect pour JavaScript, où les variables sont toujours liées à l'espace de stockage et jamais aux valeurs.
** Toute fonction externe, si plusieurs sont imbriquées, ou même dans le contexte global, comme cette réponse le montre clairement.
la source
first_calculator
c'est un objet (pas une fonction), vous ne devez pas utiliser de parenthèsessecond_calculator = first_calculator;
, car c'est une affectation, pas un appel de fonction. Pour répondre à votre question, il n'y aurait alors qu'un seul appel à make_calculator, donc une seule calculatrice serait effectuée, et les variables first_calculator et second_calculator feraient toutes deux référence à la même calculatrice, donc les réponses seraient 3, 403, 4433, 44330.Comment je l'expliquerais à un enfant de six ans:
Vous savez comment les adultes peuvent posséder une maison, et ils l'appellent chez eux? Quand une maman a un enfant, l'enfant ne possède vraiment rien, non? Mais ses parents possèdent une maison, donc chaque fois que quelqu'un demande à l'enfant "Où est ta maison?", Il / elle peut répondre "cette maison!" Et pointer la maison de ses parents. Une «fermeture» est la capacité de l'enfant de toujours (même à l'étranger) être en mesure de dire qu'il a un foyer, même si ce sont vraiment les parents qui sont propriétaires de la maison.
la source
Pouvez-vous expliquer les fermetures à un enfant de 5 ans? *
Je pense toujours que l'explication de Google fonctionne très bien et est concise:
* Question AC #
la source
J'ai tendance à mieux apprendre en comparant BON / MAUVAIS. J'aime voir du code de travail suivi d'un code non fonctionnel que quelqu'un est susceptible de rencontrer. J'ai mis en place un jsFiddle qui fait une comparaison et essaie de résumer les différences avec les explications les plus simples que j'ai pu trouver.
Fermetures bien faites:
Dans le code ci-dessus
createClosure(n)
est invoqué à chaque itération de la boucle. Notez que j'ai nommé la variablen
pour souligner qu'il s'agit d'une nouvelle variable créée dans une nouvelle étendue de fonction et n'est pas la même variable que celleindex
qui est liée à l'étendue externe.Cela crée une nouvelle étendue et
n
est lié à cette étendue; cela signifie que nous avons 10 étendues distinctes, une pour chaque itération.createClosure(n)
renvoie une fonction qui renvoie le n dans cette étendue.Dans chaque portée
n
est liée à la valeur qu'elle avait lorsqu'elle acreateClosure(n)
été invoquée, de sorte que la fonction imbriquée qui est renvoyée renvoie toujours la valeur den
celle qu'elle avait lors decreateClosure(n)
son appel.Fermetures mal faites:
Dans le code ci-dessus, la boucle a été déplacée à l'intérieur de la
createClosureArray()
fonction et la fonction ne renvoie maintenant que le tableau terminé, ce qui à première vue semble plus intuitif.Ce qui pourrait ne pas être évident, c'est que puisque
createClosureArray()
n'est invoqué qu'une seule fois, une seule étendue est créée pour cette fonction au lieu d'une pour chaque itération de la boucle.Dans cette fonction, une variable nommée
index
est définie. La boucle s'exécute et ajoute des fonctions au tableau qui retournentindex
. Notez que celaindex
est défini dans lacreateClosureArray
fonction qui n'est invoquée qu'une seule fois.Parce qu'il n'y avait qu'une seule étendue dans la
createClosureArray()
fonction,index
n'est lié qu'à une valeur dans cette étendue. En d'autres termes, chaque fois que la boucle change la valeur deindex
, elle la change pour tout ce qui la référence dans cette étendue.Toutes les fonctions ajoutées au tableau renvoient la
index
variable SAME de l'étendue parent où elle a été définie au lieu de 10 différentes de 10 étendues différentes comme le premier exemple. Le résultat final est que les 10 fonctions renvoient la même variable de la même portée.Une fois la boucle terminée et la
index
modification terminée, la valeur finale était 10, donc chaque fonction ajoutée au tableau renvoie la valeur de laindex
variable unique qui est maintenant définie sur 10.Résultat
la source
let
forvar
corrige la différence.n
créées dans une nouvelle fermeture. Nous retournons simplement une fonction afin de pouvoir la stocker dans le tableau et l'invoquer plus tard.arr[index] = (function (n) { return 'n = ' + n; })(index);
. Mais alors vous stockez la chaîne résultante dans le tableau plutôt qu'une fonction à invoquer, ce qui va à l'encontre du point de mon exemple.Wikipédia sur les fermetures :
Techniquement, en JavaScript , chaque fonction est une fermeture . Il a toujours accès aux variables définies dans le périmètre environnant.
Étant donné que la construction définissant la portée en JavaScript est une fonction , et non un bloc de code comme dans de nombreux autres langages, ce que nous entendons généralement par fermeture en JavaScript est une fonction fonctionnant avec des variables non locales définies dans une fonction environnante déjà exécutée .
Les fermetures sont souvent utilisées pour créer des fonctions avec des données privées cachées (mais ce n'est pas toujours le cas).
ems
L'exemple ci-dessus utilise une fonction anonyme, qui a été exécutée une fois. Mais cela ne doit pas l'être. Il peut être nommé (par exemple
mkdb
) et exécuté plus tard, générant une fonction de base de données chaque fois qu'il est invoqué. Chaque fonction générée aura son propre objet de base de données caché. Un autre exemple d'utilisation des fermetures est lorsque nous ne renvoyons pas une fonction, mais un objet contenant plusieurs fonctions à des fins différentes, chacune de ces fonctions ayant accès aux mêmes données.la source
J'ai mis en place un tutoriel JavaScript interactif pour expliquer le fonctionnement des fermetures. Qu'est-ce qu'une fermeture?
Voici l'un des exemples:
la source
Les secrets des fonctions JavaScript sont les variables privées
Chaque fois que vous l'appelez, la variable locale "nom" est créée et prend le nom "Mary". Et chaque fois que la fonction se termine, la variable est perdue et le nom est oublié.
Comme vous pouvez le deviner, parce que les variables sont recréées à chaque appel de la fonction et que personne d'autre ne les connaîtra, il doit y avoir un endroit secret où elles sont stockées. Cela pourrait s'appeler Chamber of Secrets ou pile ou portée locale, mais cela n'a pas vraiment d'importance. Nous savons qu'ils sont là, quelque part, cachés dans la mémoire.
Mais, en JavaScript, il y a cette chose très spéciale que les fonctions qui sont créées à l'intérieur d'autres fonctions, peuvent également connaître les variables locales de leurs parents et les conserver aussi longtemps qu'elles vivent.
Donc, tant que nous sommes dans la fonction parent, il peut créer une ou plusieurs fonctions enfants qui partagent les variables secrètes de l'endroit secret.
Mais ce qui est triste, c'est que si l'enfant est aussi une variable privée de sa fonction parentale, il mourrait également à la fin du parent et les secrets mourraient avec eux.
Donc pour vivre, l'enfant doit partir avant qu'il ne soit trop tard
Et maintenant, même si Mary ne "court plus", sa mémoire n'est pas perdue et son enfant se souviendra toujours de son nom et des autres secrets qu'il a partagés pendant son temps ensemble.
Donc, si vous appelez l'enfant "Alice", elle répondra
C'est tout ce qu'il y a à dire.
la source
Je ne comprends pas pourquoi les réponses sont si complexes ici.
Voici une fermeture:
Oui. Vous l'utilisez probablement plusieurs fois par jour.
la source
a
. À mon avis, la surprise est que l'objet scope JS fournit effectivementa
une propriété plutôt qu'une constante. Et vous ne remarquerez ce comportement important que si vous le modifiez, comme dansreturn a++;
Exemple pour le premier point de dlaliberte:
la source
Une fermeture est l'endroit où une fonction interne a accès aux variables de sa fonction externe. C'est probablement l'explication d'une ligne la plus simple que vous pouvez obtenir pour les fermetures.
la source
Je sais qu'il existe déjà de nombreuses solutions, mais je suppose que ce petit et simple script peut être utile pour démontrer le concept:
la source
Vous dormez et vous invitez Dan. Vous dites à Dan d'apporter un contrôleur XBox.
Dan invite Paul. Dan demande à Paul d'apporter un contrôleur. Combien de contrôleurs ont été amenés à la fête?
la source
L'auteur de Closures a assez bien expliqué les fermetures, expliquant la raison pour laquelle nous en avons besoin et expliquant également LexicalEnvironment qui est nécessaire pour comprendre les fermetures.
Voici le résumé:
Que faire si une variable est accédée, mais qu'elle n'est pas locale? Comme ici:
Dans ce cas, l'interpréteur trouve la variable dans l'
LexicalEnvironment
objet externe .Le processus comprend deux étapes:
Lorsqu'une fonction est créée, elle obtient une propriété masquée, nommée [[Scope]], qui fait référence au LexicalEnvironment actuel.
Si une variable est lue, mais ne peut être trouvée nulle part, une erreur est générée.
Fonctions imbriquées
Les fonctions peuvent être imbriquées les unes dans les autres, formant une chaîne de LexicalEnvironments qui peut également être appelée une chaîne de portée.
Ainsi, la fonction g a accès à g, a et f.
Fermetures
Une fonction imbriquée peut continuer à vivre une fois la fonction externe terminée:
Marquage de LexicalEnvironments:
Comme nous le voyons,
this.say
est une propriété dans l'objet utilisateur, il continue donc à vivre après la fin de l'utilisateur.Et si vous vous en souvenez, quand
this.say
est créé, il (comme chaque fonction) obtient une référence internethis.say.[[Scope]]
à l'environnement Lexical actuel. Ainsi, l'environnement Lexical de l'exécution actuelle de l'utilisateur reste en mémoire. Toutes les variables de l'utilisateur sont également ses propriétés, elles sont donc également soigneusement conservées, et non junk comme d'habitude.L'objectif est de garantir que si la fonction interne souhaite accéder à une variable externe à l'avenir, elle est en mesure de le faire.
Résumer:
C'est ce qu'on appelle une fermeture.
la source
Les fonctions JavaScript peuvent accéder à leurs:
Si une fonction accède à son environnement, alors la fonction est une fermeture.
Notez que les fonctions externes ne sont pas requises, bien qu'elles offrent des avantages dont je ne parle pas ici. En accédant aux données dans son environnement, une fermeture maintient ces données en vie. Dans le sous-cas des fonctions externes / internes, une fonction externe peut créer des données locales et éventuellement quitter, et pourtant, si une ou plusieurs fonctions internes survivent après la sortie de la fonction externe, alors la ou les fonctions internes conservent les données locales de la fonction externe vivant.
Exemple de fermeture utilisant l'environnement global:
Imaginez que les événements des boutons Vote-Up et Vote-Down de Stack Overflow soient implémentés comme des fermetures, voteUp_click et voteDown_click, qui ont accès aux variables externes isVotedUp et isVotedDown, qui sont définies globalement. (Par souci de simplicité, je fais référence aux boutons de vote aux questions de StackOverflow, pas au tableau des boutons de vote aux réponses.)
Lorsque l'utilisateur clique sur le bouton VoteUp, la fonction voteUp_click vérifie si isVotedDown == true pour déterminer s'il faut voter pour ou simplement annuler un vote négatif. La fonction voteUp_click est une fermeture car elle accède à son environnement.
Ces quatre fonctions sont des fermetures car elles accèdent toutes à leur environnement.
la source
En tant que père d'un enfant de 6 ans, qui enseigne actuellement aux jeunes enfants (et un novice relatif au codage sans éducation formelle, des corrections seront nécessaires), je pense que la leçon se prolongerait mieux par le jeu pratique. Si l'enfant de 6 ans est prêt à comprendre ce qu'est une fermeture, il est alors assez âgé pour tenter sa chance. Je suggère de coller le code sur jsfiddle.net, d'expliquer un peu et de les laisser seuls pour concocter une chanson unique. Le texte explicatif ci-dessous est probablement plus approprié pour un enfant de 10 ans.
INSTRUCTIONS
DONNÉES: Les données sont un ensemble de faits. Il peut s'agir de chiffres, de mots, de mesures, d'observations ou même simplement de descriptions de choses. Vous ne pouvez pas le toucher, le sentir ou le goûter. Vous pouvez l'écrire, le parler et l'entendre. Vous pouvez l'utiliser pour créer une odeur tactile et un goût à l'aide d'un ordinateur. Il peut être rendu utile par un ordinateur utilisant du code.
CODE: Toute l'écriture ci-dessus est appelée code . Il est écrit en JavaScript.
JAVASCRIPT: JavaScript est un langage. Comme l'anglais ou le français ou le chinois sont des langues. De nombreux langages sont compris par les ordinateurs et autres processeurs électroniques. Pour que JavaScript soit compris par un ordinateur, il a besoin d'un interprète. Imaginez si un enseignant qui ne parle que le russe vient enseigner votre classe à l'école. Quand le professeur dit "все садятся", la classe ne comprendrait pas. Mais heureusement, vous avez un élève russe dans votre classe qui dit à tout le monde que cela signifie "tout le monde s'assoit" - alors vous le faites tous. La classe est comme un ordinateur et l'élève russe est l'interprète. Pour JavaScript, l'interpréteur le plus courant est appelé navigateur.
NAVIGATEUR: Lorsque vous vous connectez à Internet sur un ordinateur, une tablette ou un téléphone pour visiter un site Web, vous utilisez un navigateur. Vous connaissez peut-être par exemple Internet Explorer, Chrome, Firefox et Safari. Le navigateur peut comprendre JavaScript et indiquer à l'ordinateur ce qu'il doit faire. Les instructions JavaScript sont appelées fonctions.
FONCTION: Une fonction en JavaScript est comme une usine. Ce pourrait être une petite usine avec une seule machine à l'intérieur. Ou il peut contenir de nombreuses autres petites usines, chacune avec de nombreuses machines effectuant des tâches différentes. Dans une véritable usine de vêtements, vous pourriez avoir des rames de tissu et des bobines de fil qui entrent et des t-shirts et des jeans qui sortent. Notre usine JavaScript ne traite que les données, elle ne peut pas coudre, percer un trou ou faire fondre du métal. Dans notre usine JavaScript, les données entrent et sortent.
Toutes ces données semblent un peu ennuyeuses, mais c'est vraiment très cool; nous pourrions avoir une fonction qui indique à un robot ce qu'il faut faire pour le dîner. Disons que je vous invite, vous et votre ami, chez moi. Vous préférez les cuisses de poulet, j'aime les saucisses, votre ami veut toujours ce que vous voulez et mon ami ne mange pas de viande.
Je n'ai pas le temps de faire du shopping, donc la fonction doit savoir ce que nous avons dans le réfrigérateur pour prendre des décisions. Chaque ingrédient a un temps de cuisson différent et nous voulons que tout soit servi chaud par le robot en même temps. Nous devons fournir à la fonction les données sur ce que nous aimons, la fonction pourrait «parler» au réfrigérateur et la fonction pourrait contrôler le robot.
Une fonction a normalement un nom, des parenthèses et des accolades. Comme ça:
Notez cela
/*...*/
et//
arrêtez la lecture du code par le navigateur.NOM: Vous pouvez appeler une fonction à peu près n'importe quel mot que vous voulez. L'exemple "cookMeal" est typique de l'union de deux mots et de la mise en majuscule du second - mais ce n'est pas nécessaire. Il ne peut pas y avoir d'espace, ni un nombre à lui seul.
PARENTHÈSES: "Parenthèses" ou
()
sont la boîte aux lettres sur la porte de l'usine de la fonction JavaScript ou une boîte aux lettres dans la rue pour l'envoi de paquets d'informations à l'usine. Parfois, la boîte aux lettres peut être marquée par exemplecookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, auquel cas vous savez quelles données vous devez lui fournir.BRACELETS: "Bretelles" qui ressemblent à ceci
{}
sont les fenêtres teintées de notre usine. De l'intérieur de l'usine, vous pouvez voir dehors, mais de l'extérieur, vous ne pouvez pas voir dedans.L'EXEMPLE DE CODE LONG CI-DESSUS
Notre code commence par le mot fonction , nous savons donc que c'est un! Ensuite, le nom de la fonction chante - c'est ma propre description de la fonction. Puis entre parenthèses () . Les parenthèses sont toujours là pour une fonction. Parfois , ils sont vides, et parfois ils ont quelque chose dans celui - ci a un mot.:
(person)
. Après cela, il y a une accolade comme celle-ci{
. Cela marque le début de la fonction sing () . Il a un partenaire qui marque la fin du chant () comme celui-ci}
Cette fonction pourrait donc avoir quelque chose à voir avec le chant et pourrait avoir besoin de certaines données sur une personne. Il contient des instructions pour faire quelque chose avec ces données.
Maintenant, après la fonction sing () , vers la fin du code se trouve la ligne
VARIABLE: Les lettres var représentent "variable". Une variable est comme une enveloppe. A l'extérieur, cette enveloppe est marquée "personne". À l'intérieur, il contient une feuille de papier avec les informations dont notre fonction a besoin, des lettres et des espaces réunis comme un morceau de ficelle (ce qu'on appelle une ficelle) qui font une phrase se lisant "une vieille dame". Notre enveloppe pourrait contenir d'autres types de choses comme des nombres (appelés entiers), des instructions (appelées fonctions), des listes (appelées tableaux ). Parce que cette variable est écrite en dehors de tous les accolades
{}
et parce que vous pouvez voir à travers les fenêtres teintées lorsque vous êtes à l'intérieur des accolades, cette variable peut être vue de n'importe où dans le code. Nous appelons cela une «variable globale».VARIABLE GLOBALE: personne est une variable globale, ce qui signifie que si vous changez sa valeur de "une vieille dame" en "un jeune homme", la personne continuera d'être un jeune homme jusqu'à ce que vous décidiez de la changer à nouveau et que toute autre fonction dans le code peut voir que c'est un jeune homme. Appuyez sur le F12bouton ou regardez les paramètres Options pour ouvrir la console développeur d'un navigateur et tapez "personne" pour voir quelle est cette valeur. Tapez
person="a young man"
pour le modifier, puis tapez à nouveau "personne" pour voir qu'il a changé.Après cela, nous avons la ligne
Cette ligne appelle la fonction, comme si elle appelait un chien
Lorsque le navigateur a chargé le code JavaScript et atteint cette ligne, il démarre la fonction. Je mets la ligne à la fin pour m'assurer que le navigateur a toutes les informations dont il a besoin pour l'exécuter.
Les fonctions définissent les actions - la fonction principale consiste à chanter. Il contient une variable appelée firstPart qui s'applique au chant sur la personne qui s'applique à chacun des versets de la chanson: "Il y avait" + personne + "qui a avalé". Si vous tapez firstPart dans la console, vous n'obtiendrez pas de réponse car la variable est verrouillée dans une fonction - le navigateur ne peut pas voir à l'intérieur des fenêtres teintées des accolades.
FERMETURES: Les fermetures sont les petites fonctions qui se trouvent à l'intérieur de la grande fonction sing () . Les petites usines à l'intérieur de la grande usine. Ils ont chacun leurs propres accolades, ce qui signifie que les variables à l'intérieur ne peuvent pas être vues de l'extérieur. C'est pourquoi les noms des variables ( créature et résultat ) peuvent être répétés dans les fermetures mais avec des valeurs différentes. Si vous tapez ces noms de variables dans la fenêtre de la console, vous n'obtiendrez pas sa valeur car elle est masquée par deux couches de fenêtres teintées.
Les fermetures savent toutes ce qu'est la variable de la fonction sing () appelée firstPart , car elles peuvent voir depuis leurs fenêtres teintées.
Après les fermetures viennent les lignes
La fonction sing () appellera chacune de ces fonctions dans l'ordre où elles sont données. Ensuite, le travail de la fonction sing () sera terminé.
la source
D'accord, en parlant avec un enfant de 6 ans, j'utiliserais peut-être les associations suivantes.
Comparez avec une situation où une porte était verrouillée par le courant d'air et personne à l'intérieur (exécution de la fonction générale), puis un incendie local se produit et brûle la pièce (ramasse-miettes: D), puis une nouvelle pièce a été construite et maintenant vous pouvez partir un autre jouet là-bas (nouvelle instance de fonction), mais n'obtenez jamais les mêmes jouets qui ont été laissés dans la première instance de la pièce.
Pour un enfant avancé, je mettrais quelque chose comme ceci. Ce n'est pas parfait, mais cela vous fait ressentir ce que c'est:
Comme vous pouvez le voir, les jouets laissés dans la pièce sont toujours accessibles via le frère et peu importe si la pièce est verrouillée. Voici un jsbin pour jouer avec.
la source
Une réponse pour un enfant de six ans (en supposant qu'il sait ce qu'est une fonction et ce qu'est une variable, et quelles sont les données):
Les fonctions peuvent renvoyer des données. Un type de données que vous pouvez renvoyer d'une fonction est une autre fonction. Lorsque cette nouvelle fonction est renvoyée, toutes les variables et tous les arguments utilisés dans la fonction qui l'a créée ne disparaissent pas. Au lieu de cela, cette fonction parent «se ferme». En d'autres termes, rien ne peut regarder à l'intérieur et voir les variables qu'il a utilisées à l'exception de la fonction qu'il a renvoyée. Cette nouvelle fonction a une capacité spéciale à regarder en arrière à l'intérieur de la fonction qui l'a créée et à voir les données à l'intérieur.
Une autre façon très simple de l'expliquer est en termes de portée:
Chaque fois que vous créez une portée plus petite à l'intérieur d'une portée plus grande, la plus petite portée sera toujours en mesure de voir ce qui est dans la plus grande portée.
la source
Une fonction en JavaScript n'est pas seulement une référence à un ensemble d'instructions (comme en langage C), mais elle comprend également une structure de données cachée qui est composée de références à toutes les variables non locales qu'elle utilise (variables capturées). Ces fonctions en deux parties sont appelées fermetures. Chaque fonction en JavaScript peut être considérée comme une fermeture.
Les fermetures sont des fonctions avec un état. Il est quelque peu similaire à "ceci" dans le sens où "ceci" fournit également un état pour une fonction mais la fonction et "ceci" sont des objets séparés ("ceci" n'est qu'un paramètre de fantaisie, et le seul moyen de le lier de manière permanente à un est de créer une fermeture). Bien que "ceci" et la fonction vivent toujours séparément, une fonction ne peut pas être séparée de sa fermeture et le langage ne fournit aucun moyen d'accéder aux variables capturées.
Parce que toutes ces variables externes référencées par une fonction lexicalement imbriquée sont en fait des variables locales dans la chaîne de ses fonctions englobantes lexicales (les variables globales peuvent être supposées être des variables locales d'une fonction racine), et chaque exécution unique d'une fonction crée de nouvelles instances de ses variables locales, il s'ensuit que chaque exécution d'une fonction renvoyant (ou autrement la transférant, comme l'enregistrer comme rappel) une fonction imbriquée crée une nouvelle fermeture (avec son propre ensemble potentiellement unique de variables non locales référencées qui représentent son exécution le contexte).
En outre, il faut comprendre que les variables locales en JavaScript sont créées non pas sur le cadre de pile, mais sur le tas et détruites uniquement lorsque personne ne les référence. Lorsqu'une fonction retourne, les références à ses variables locales sont décrémentées, mais elles peuvent toujours être non nulles si pendant l'exécution en cours elles font partie d'une fermeture et sont toujours référencées par ses fonctions lexicalement imbriquées (ce qui ne peut se produire que si les références à ces fonctions imbriquées ont été retournées ou autrement transférées vers un code externe).
Un exemple:
la source
Peut-être un peu au-delà de tous, mais le plus précoce des enfants de six ans, mais quelques exemples qui ont aidé à faire en sorte que le concept de fermeture en JavaScript clique pour moi.
Une fermeture est une fonction qui a accès à la portée d'une autre fonction (ses variables et fonctions). La façon la plus simple de créer une fermeture est d'utiliser une fonction dans une fonction; la raison étant qu'en JavaScript, une fonction a toujours accès à la portée de sa fonction conteneur.
ALERTE: singe
Dans l'exemple ci-dessus, la fonction externalFunction est appelée, elle-même appelée innerFunction. Notez la façon dont externalVar est disponible pour innerFunction, comme en témoigne son alerte correcte de la valeur de externalVar.
Considérez maintenant ce qui suit:
ALERTE: singe
referenceToInnerFunction est définie sur externalFunction (), qui renvoie simplement une référence à innerFunction. Lorsque referenceToInnerFunction est appelé, il retourne externalVar. Encore une fois, comme ci-dessus, cela démontre que innerFunction a accès à externalVar, une variable de externalFunction. De plus, il est intéressant de noter qu'il conserve cet accès même après l'exécution de la fonction externalFunction.
Et c'est là que les choses deviennent vraiment intéressantes. Si nous devions nous débarrasser de externalFunction, disons le mettre à null, vous pourriez penser que referenceToInnerFunction perdrait son accès à la valeur de externalVar. Mais ce n'est pas le cas.
ALERTE: singe ALERTE: singe
Mais comment en est-il ainsi? Comment referenceToInnerFunction peut-il toujours connaître la valeur de externalVar maintenant que externalFunction a été défini sur null?
La raison pour laquelle referenceToInnerFunction peut toujours accéder à la valeur de externalVar est que, lorsque la fermeture a été créée pour la première fois en plaçant innerFunction à l'intérieur de externalFunction, innerFunction a ajouté une référence à la portée de externalFunction (ses variables et fonctions) à sa chaîne de portée. Cela signifie que innerFunction a un pointeur ou une référence à toutes les variables de externalFunction, y compris externalVar. Ainsi, même lorsque la fonction externalFunction a terminé son exécution, ou même si elle est supprimée ou définie sur null, les variables de sa portée, comme externalVar, restent en mémoire en raison de la référence exceptionnelle à elles de la part de la fonction innerFunction qui a été renvoyée à referenceToInnerFunction. Pour vraiment libérer externalVar et le reste des variables de externalFunction de la mémoire, vous devez vous débarrasser de cette référence exceptionnelle à eux,
//////////
Deux autres choses sur les fermetures à noter. Tout d'abord, la fermeture aura toujours accès aux dernières valeurs de sa fonction conteneur.
ALERTE: gorille
Deuxièmement, lorsqu'une fermeture est créée, elle conserve une référence à toutes les variables et fonctions de sa fonction englobante; il ne peut pas choisir. Et pourtant, les fermetures doivent être utilisées avec parcimonie, ou du moins avec précaution, car elles peuvent consommer beaucoup de mémoire; de nombreuses variables peuvent être conservées en mémoire longtemps après la fin de l'exécution d'une fonction conteneur.
la source
Je les pointerais simplement vers la page des fermetures de Mozilla . C'est l' explication la meilleure, la plus concise et la plus simple des bases de la fermeture et de l'utilisation pratique que j'ai trouvée. Il est fortement recommandé à quiconque apprend JavaScript.
Et oui, je le recommanderais même à un enfant de 6 ans - si l'enfant de 6 ans apprend les fermetures, il est logique qu'il soit prêt à comprendre l' explication concise et simple fournie dans l'article.
la source