J'ai posé une question sur le curry et les fermetures ont été mentionnées. Qu'est-ce qu'une fermeture? Comment est-ce lié au curry?
J'ai posé une question sur le curry et les fermetures ont été mentionnées. Qu'est-ce qu'une fermeture? Comment est-ce lié au curry?
Réponses:
Lorsque vous déclarez une variable locale, cette variable a une portée. Généralement, les variables locales n'existent que dans le bloc ou la fonction dans laquelle vous les déclarez.
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
Si j'essaie d'accéder à une variable locale, la plupart des langues la rechercheront dans la portée actuelle, puis remonteront dans les portées parent jusqu'à ce qu'elles atteignent la portée racine.
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
Lorsqu'un bloc ou une fonction est terminé, ses variables locales ne sont plus nécessaires et sont généralement supprimées de la mémoire.
C'est ainsi que nous nous attendons normalement à ce que les choses fonctionnent.
Une fermeture est une portée persistante qui conserve les variables locales même après que l'exécution du code a quitté ce bloc. Les langages qui prennent en charge la fermeture (tels que JavaScript, Swift et Ruby) vous permettront de conserver une référence à une étendue (y compris ses étendues parentes), même après l'exécution du bloc dans lequel ces variables ont été déclarées, à condition que vous gardiez une référence à ce bloc ou cette fonction quelque part.
L'objet scope et toutes ses variables locales sont liés à la fonction et persisteront tant que cette fonction persistera.
Cela nous donne la portabilité des fonctions. Nous pouvons nous attendre à ce que toutes les variables qui étaient dans la portée lorsque la fonction a été définie pour la première fois soient toujours dans la portée lorsque nous l'appellerons plus tard, même si nous appelons la fonction dans un contexte complètement différent.
Voici un exemple très simple en JavaScript qui illustre le point:
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
Ici, j'ai défini une fonction au sein d'une fonction. La fonction interne accède à toutes les variables locales de la fonction externe, y compris a
. La variable a
est dans la portée de la fonction interne.
Normalement, lorsqu'une fonction se termine, toutes ses variables locales sont supprimées. Cependant, si nous renvoyons la fonction interne et l'affectons à une variable fnc
afin qu'elle persiste après outer
sa sortie, toutes les variables qui étaient dans la portée au moment de inner
sa définition persistent également . La variable a
a été fermée au cours - elle est dans une fermeture.
Notez que la variable a
est totalement privée de fnc
. C'est un moyen de créer des variables privées dans un langage de programmation fonctionnel tel que JavaScript.
Comme vous pourriez le deviner, lorsque j'appelle, fnc()
il affiche la valeur de a
, qui est "1".
Dans un langage sans fermeture, la variable a
aurait été récupérée et jetée à la outer
sortie de la fonction . L'appel de fnc aurait généré une erreur car il a
n'existe plus.
En JavaScript, la variable a
persiste car la portée de la variable est créée lors de la première déclaration de la fonction et persiste aussi longtemps que la fonction continue d'exister.
a
appartient à la portée de outer
. La portée de inner
a un pointeur parent sur la portée de outer
. fnc
est une variable qui pointe vers inner
. a
persiste aussi longtemps que fnc
persiste. a
est dans la fermeture.
Je vais donner un exemple (en JavaScript):
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
Ce que fait cette fonction, makeCounter, c'est qu'elle retourne une fonction, que nous avons appelée x, qui comptera de un à chaque fois qu'elle sera appelée. Puisque nous ne fournissons aucun paramètre à x, il doit en quelque sorte se souvenir du nombre. Il sait où le trouver en fonction de ce qu'on appelle la portée lexicale - il doit chercher l'endroit où il est défini pour trouver la valeur. Cette valeur «cachée» est ce qu'on appelle une fermeture.
Voici à nouveau mon exemple de curry:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
Ce que vous pouvez voir, c'est que lorsque vous appelez add avec le paramètre a (qui est 3), cette valeur est contenue dans la fermeture de la fonction retournée que nous définissons comme étant add3. De cette façon, lorsque nous appelons add3, il sait où trouver la valeur a pour effectuer l'addition.
La réponse de Kyle est plutôt bonne. Je pense que la seule précision supplémentaire est que la fermeture est essentiellement un instantané de la pile au moment où la fonction lambda est créée. Ensuite, lorsque la fonction est réexécutée, la pile est restaurée à cet état avant d'exécuter la fonction. Ainsi, comme le mentionne Kyle, cette valeur cachée ( count
) est disponible lorsque la fonction lambda s'exécute.
Tout d'abord, contrairement à ce que la plupart des gens ici vous disent, la fermeture n'est pas une fonction ! Alors qu'est - est - il?
Il s'agit d'un ensemble de symboles définis dans le "contexte environnant" d'une fonction (connu sous le nom d' environnement ) qui en font une expression CLOSED (c'est-à-dire une expression dans laquelle chaque symbole est défini et a une valeur, de sorte qu'il peut être évalué).
Par exemple, lorsque vous avez une fonction JavaScript:
function closed(x) {
return x + 3;
}
c'est une expression fermée car tous les symboles qui s'y trouvent y sont définis (leur signification est claire), vous pouvez donc l'évaluer. En d'autres termes, il est autonome .
Mais si vous avez une fonction comme celle-ci:
function open(x) {
return x*y + 3;
}
c'est une expression ouverte parce qu'il contient des symboles qui n'y sont pas définis. A savoir, y
. En regardant cette fonction, nous ne pouvons pas dire ce qu'elle y
est et ce qu'elle signifie, nous ne connaissons pas sa valeur, nous ne pouvons donc pas évaluer cette expression. C'est-à-dire que nous ne pouvons pas appeler cette fonction jusqu'à ce que nous disions ce qu'elle y
est censée signifier. C'est ce y
qu'on appelle une variable libre .
Cela y
demande une définition, mais cette définition ne fait pas partie de la fonction - elle est définie ailleurs, dans son "contexte environnant" (également connu sous le nom d' environnement ). C'est du moins ce que nous espérons: P
Par exemple, il pourrait être défini globalement:
var y = 7;
function open(x) {
return x*y + 3;
}
Ou il pourrait être défini dans une fonction qui l'enveloppe:
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
La partie de l'environnement qui donne aux variables libres dans une expression leur signification est la fermeture . Il est appelé ainsi, car il transforme une expression ouverte en expression fermée , en fournissant ces définitions manquantes pour toutes ses variables libres , afin que nous puissions l'évaluer.
Dans l'exemple ci-dessus, la fonction interne (à laquelle nous n'avons pas donné de nom parce que nous n'en avions pas besoin) est une expression ouverte car la variable y
qu'elle contient est libre - sa définition est en dehors de la fonction, dans la fonction qui l'enveloppe . L' environnement de cette fonction anonyme est l'ensemble des variables:
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
Maintenant, la fermeture est la partie de cet environnement qui ferme la fonction interne en fournissant les définitions de toutes ses variables libres . Dans notre cas, la seule variable libre dans la fonction interne était y
, donc la fermeture de cette fonction est ce sous-ensemble de son environnement:
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
Les deux autres symboles définis dans l'environnement ne font pas partie de la fermeture de cette fonction, car elle ne nécessite pas leur exécution. Ils ne sont pas nécessaires pour le fermer .
Plus d'informations sur la théorie derrière cela ici: https://stackoverflow.com/a/36878651/434562
Il convient de noter que dans l'exemple ci-dessus, la fonction wrapper renvoie sa fonction interne sous forme de valeur. Le moment où nous appelons cette fonction peut être éloigné dans le temps à partir du moment où la fonction a été définie (ou créée). En particulier, sa fonction d'habillage n'est plus en cours d'exécution, et ses paramètres qui étaient sur la pile d'appels ne sont plus là: P Cela pose problème, car la fonction interne doit y
être là quand elle est appelée! En d'autres termes, il nécessite les variables de sa fermeture pour survivre en quelque sorte à la fonction wrapper et être là en cas de besoin. Par conséquent, la fonction interne doit faire un instantané de ces variables qui rendent sa fermeture et les stocker dans un endroit sûr pour une utilisation ultérieure. (Quelque part en dehors de la pile d'appels.)
Et c'est pourquoi les gens confondent souvent le terme fermeture comme étant ce type spécial de fonction qui peut faire de tels instantanés des variables externes qu'ils utilisent, ou la structure de données utilisée pour stocker ces variables pour plus tard. Mais j'espère que vous comprenez maintenant qu'il ne s'agit pas de la fermeture elle-même - ce sont juste des moyens d' implémenter des fermetures dans un langage de programmation, ou des mécanismes de langage qui permettent aux variables de la fermeture de la fonction d'être là en cas de besoin. Il y a beaucoup d'idées fausses autour des fermetures qui rendent (inutilement) ce sujet beaucoup plus confus et compliqué qu'il ne l'est réellement.
Une fermeture est une fonction qui peut référencer un état dans une autre fonction. Par exemple, en Python, cela utilise la fermeture "intérieure":
def outer (a):
b = "variable in outer()"
def inner (c):
print a, b, c
return inner
# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
Pour faciliter la compréhension des clôtures, il pourrait être utile d'examiner comment elles pourraient être mises en œuvre dans un langage procédural. Cette explication suivra une implémentation simpliste des fermetures dans Scheme.
Pour commencer, je dois introduire le concept d'espace de noms. Lorsque vous entrez une commande dans un interpréteur Scheme, il doit évaluer les différents symboles dans l'expression et obtenir leur valeur. Exemple:
(define x 3)
(define y 4)
(+ x y) returns 7
Les expressions de définition stockent la valeur 3 à l'endroit pour x et la valeur 4 à l'endroit pour y. Ensuite, lorsque nous appelons (+ xy), l'interpréteur recherche les valeurs dans l'espace de noms et est capable d'effectuer l'opération et de retourner 7.
Cependant, dans Scheme, il existe des expressions qui vous permettent de remplacer temporairement la valeur d'un symbole. Voici un exemple:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
Le mot clé let introduit un nouvel espace de noms avec x comme valeur 5. Vous remarquerez qu'il peut toujours voir que y est 4, ce qui fait que la somme renvoyée est 9. Vous pouvez également voir qu'une fois l'expression terminée x revient à 3. Dans ce sens, x a été temporairement masqué par la valeur locale.
Les langages procéduraux et orientés objet ont un concept similaire. Chaque fois que vous déclarez une variable dans une fonction qui porte le même nom qu'une variable globale, vous obtenez le même effet.
Comment pourrions-nous mettre cela en œuvre? Une manière simple est avec une liste chaînée - la tête contient la nouvelle valeur et la queue contient l'ancien espace de noms. Lorsque vous devez rechercher un symbole, vous commencez par la tête et descendez la queue.
Passons maintenant à l'implémentation de fonctions de première classe pour le moment. Plus ou moins, une fonction est un ensemble d'instructions à exécuter lorsque la fonction est appelée aboutissant à la valeur de retour. Lorsque nous lisons dans une fonction, nous pouvons stocker ces instructions en arrière-plan et les exécuter lorsque la fonction est appelée.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
Nous définissons x pour être 3 et plus-x pour son paramètre, y, plus la valeur de x. Enfin, nous appelons plus-x dans un environnement où x a été masqué par un nouveau x, celui-ci valant 5. Si nous stockons simplement l'opération, (+ xy), pour la fonction plus-x, puisque nous sommes dans le contexte de x étant 5, le résultat renvoyé serait 9. C'est ce qu'on appelle la portée dynamique.
Cependant, Scheme, Common Lisp et de nombreux autres langages ont ce qu'on appelle la portée lexicale - en plus de stocker l'opération (+ xy), nous stockons également l'espace de noms à ce point particulier. De cette façon, lorsque nous recherchons les valeurs, nous pouvons voir que x, dans ce contexte, est vraiment 3. Il s'agit d'une fermeture.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
En résumé, nous pouvons utiliser une liste chaînée pour stocker l'état de l'espace de noms au moment de la définition de la fonction, ce qui nous permet d'accéder aux variables à partir des étendues englobantes, ainsi que de nous fournir la possibilité de masquer localement une variable sans affecter le reste de la programme.
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Voici un exemple concret de la raison pour laquelle les fermetures donnent un coup de pied au cul ... Cela vient tout droit de mon code Javascript. Permettez-moi d'illustrer.
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
Et voici comment vous l'utiliseriez:
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
Imaginez maintenant que vous souhaitiez que la lecture démarre différée, comme par exemple 5 secondes plus tard après l'exécution de cet extrait de code. delay
Et bien c'est facile avec et c'est la fermeture:
startPlayback.delay(5000, someTrack);
// Keep going, do other things
Lorsque vous appelez delay
avec 5000
ms, le premier extrait s'exécute et stocke les arguments passés dans sa fermeture. Puis, 5 secondes plus tard, lorsque le setTimeout
rappel se produit, la fermeture conserve toujours ces variables, de sorte qu'elle peut appeler la fonction d'origine avec les paramètres d'origine.
Il s'agit d'un type de curry ou d'une décoration fonctionnelle.
Sans fermetures, vous devriez en quelque sorte maintenir l'état de ces variables en dehors de la fonction, jetant ainsi le code en dehors de la fonction avec quelque chose qui appartient logiquement à l'intérieur. L'utilisation de fermetures peut améliorer considérablement la qualité et la lisibilité de votre code.
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
Une fermeture est une fonction et sa portée affectée (ou utilisée comme) une variable. Ainsi, la fermeture du nom: la portée et la fonction sont enfermées et utilisées comme n'importe quelle autre entité.
Selon Wikipedia, une fermeture est:
Techniques pour implémenter la liaison de noms à portée lexicale dans les langues avec des fonctions de première classe.
Qu'est-ce que ça veut dire? Regardons quelques définitions.
Je vais expliquer les fermetures et autres définitions associées en utilisant cet exemple:
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
Fondamentalement, cela signifie que nous pouvons utiliser des fonctions comme n'importe quelle autre entité . Nous pouvons les modifier, les passer comme arguments, les renvoyer des fonctions ou les affecter à des variables. Techniquement parlant, ce sont des citoyens de première classe , d'où le nom: fonctions de première classe.
Dans l'exemple ci-dessus, startAt
renvoie une fonction ( anonyme ) à laquelle la fonction est affectée à closure1
et closure2
. Ainsi, comme vous le voyez, JavaScript traite les fonctions comme toutes les autres entités (citoyens de première classe).
La liaison de nom consiste à savoir quelles données une variable (identifiant) référence . La portée est vraiment importante ici, car c'est la chose qui déterminera comment une liaison est résolue.
Dans l'exemple ci-dessus:
y
est lié à 3
.startAt
la portée de, x
est lié à 1
ou 5
(selon la fermeture).À l'intérieur de la portée de la fonction anonyme, x
n'est liée à aucune valeur, elle doit donc être résolue dans une startAt
portée supérieure ( 's).
Comme le dit Wikipedia , la portée:
Région d'un programme informatique où la liaison est valide: où le nom peut être utilisé pour faire référence à l'entité .
Il existe deux techniques:
Pour plus d'explications, consultez cette question et jetez un œil à Wikipedia .
Dans l'exemple ci-dessus, nous pouvons voir que JavaScript a une portée lexicale, car lorsqu'il x
est résolu, la liaison est recherchée dans la startAt
portée supérieure ( s), en fonction du code source (la fonction anonyme qui recherche x est définie à l'intérieur startAt
) et pas basé sur la pile d'appels, la façon (la portée où) la fonction a été appelée.
Dans notre exemple, lorsque nous appelons startAt
, il retournera une fonction (de première classe) qui sera affectée closure1
et closure2
donc une fermeture est créée, car les variables passées 1
et 5
seront enregistrées dans startAt
la portée de ', qui seront jointes avec le retour fonction anonyme. Lorsque nous appelons cette fonction anonyme via closure1
et closure2
avec le même argument ( 3
), la valeur de y
sera trouvée immédiatement (car c'est le paramètre de cette fonction), mais x
n'est pas liée dans la portée de la fonction anonyme, donc la résolution continue dans la portée de fonction supérieure (lexicalement) (qui a été enregistrée dans la fermeture) où x
se trouve être lié à 1
ou5
. Maintenant, nous savons tout pour la sommation afin que le résultat puisse être retourné, puis imprimé.
Vous devez maintenant comprendre les fermetures et leur comportement, ce qui est un élément fondamental de JavaScript.
Oh, et vous avez également appris ce qu'est le curry : vous utilisez des fonctions (fermetures) pour passer chaque argument d'une opération au lieu d'utiliser une fonction avec plusieurs paramètres.
La fermeture est une fonctionnalité en JavaScript où une fonction a accès à ses propres variables de portée, accès aux variables de fonction externes et accès aux variables globales.
La fermeture a accès à sa portée de fonction externe même après le retour de la fonction externe. Cela signifie qu'une fermeture peut mémoriser et accéder aux variables et arguments de sa fonction externe même après la fin de la fonction.
La fonction interne peut accéder aux variables définies dans sa propre portée, la portée de la fonction extérieure et la portée globale. Et la fonction externe peut accéder à la variable définie dans sa propre portée et la portée globale.
Exemple de fermeture :
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
Le résultat sera 20, somme de sa propre fonction interne, de sa fonction externe et de sa valeur de variable globale.
Dans une situation normale, les variables sont liées par une règle de portée: les variables locales ne fonctionnent que dans la fonction définie. La fermeture est un moyen d'enfreindre temporairement cette règle pour plus de commodité.
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
dans le code ci-dessus, lambda(|n| a_thing * n}
est la fermeture car elle a_thing
est référencée par le lambda (un créateur de fonction anonyme).
Maintenant, si vous mettez la fonction anonyme résultante dans une variable de fonction.
foo = n_times(4)
foo enfreindra la règle de cadrage normale et commencera à utiliser 4 en interne.
foo.call(3)
renvoie 12.
• Une fermeture est un sous-programme et l'environnement de référence où il a été défini
- L'environnement de référencement est nécessaire si le sous-programme peut être appelé à partir de n'importe quel endroit arbitraire du programme
- Un langage à portée statique qui n'autorise pas les sous-programmes imbriqués n'a pas besoin de fermetures
- Les fermetures ne sont nécessaires que si un sous-programme peut accéder aux variables dans les étendues d'imbrication et il peut être appelé de n'importe où
- Pour prendre en charge les fermetures, une implémentation peut devoir fournir une étendue illimitée à certaines variables (car un sous-programme peut accéder à une variable non locale qui n'est normalement plus active)
Exemple
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
Voici un autre exemple réel, et en utilisant un langage de script populaire dans les jeux - Lua. J'ai dû changer légèrement la façon dont une fonction de bibliothèque fonctionnait pour éviter un problème avec stdin non disponible.
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
La valeur de old_dofile disparaît lorsque ce bloc de code termine sa portée (car il est local), mais la valeur a été incluse dans une fermeture, donc la nouvelle fonction de fichier redéfini PEUT y accéder, ou plutôt une copie stockée avec la fonction en tant que «upvalue».
De Lua.org :
Lorsqu'une fonction est écrite enfermée dans une autre fonction, elle a un accès complet aux variables locales à partir de la fonction englobante; cette fonction est appelée portée lexicale. Bien que cela puisse sembler évident, ce n'est pas le cas. La portée lexicale, plus les fonctions de première classe, est un concept puissant dans un langage de programmation, mais peu de langages prennent en charge ce concept.
Si vous êtes du monde Java, vous pouvez comparer une fermeture avec une fonction membre d'une classe. Regardez cet exemple
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
La fonction g
est une fermeture: se g
ferme a
. Elle g
peut donc être comparée à une fonction membre, a
peut être comparée à un champ de classe et la fonction f
à une classe.
Fermetures Chaque fois que nous avons une fonction définie à l'intérieur d'une autre fonction, la fonction interne a accès aux variables déclarées dans la fonction externe. Les fermetures sont mieux expliquées avec des exemples. Dans l'extrait 2-18, vous pouvez voir que la fonction interne a accès à une variable (variableInOuterFunction) à partir de la portée externe. Les variables de la fonction externe ont été fermées par (ou liées dans) la fonction interne. D'où le terme de fermeture. Le concept en lui-même est assez simple et assez intuitif.
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
source: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf
Veuillez regarder ci-dessous le code pour comprendre la fermeture de manière plus approfondie:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
Voici ce qui sera produit? 0,1,2,3,4
ce ne sera pas à 5,5,5,5,5
cause de la fermeture
Alors, comment ça va résoudre? La réponse est ci-dessous:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
Permettez-moi d'expliquer simplement, quand une fonction créée rien ne se passe jusqu'à ce qu'elle appelle ainsi pour la boucle dans le 1er code appelé 5 fois mais pas appelé immédiatement alors quand elle est appelée, c'est-à-dire après 1 seconde et aussi c'est asynchrone donc avant que pour la boucle terminée et stocke la valeur 5 dans var i et enfin exécuter la setTimeout
fonction cinq fois et imprimer5,5,5,5,5
Voici comment cela se résout en utilisant IIFE, c'est-à-dire l'expression de la fonction d'appel immédiat
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
Pour en savoir plus, veuillez comprendre le contexte d'exécution pour comprendre la clôture.
Il existe une autre solution pour résoudre ce problème en utilisant let (fonctionnalité ES6), mais sous le capot, la fonction ci-dessus fonctionne
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
=> Plus d'explications:
En mémoire, quand pour boucle exécuter l'image, faites comme ci-dessous:
Boucle 1)
setTimeout(function(){
console.log(i);
},1000);
Boucle 2)
setTimeout(function(){
console.log(i);
},1000);
Boucle 3)
setTimeout(function(){
console.log(i);
},1000);
Boucle 4)
setTimeout(function(){
console.log(i);
},1000);
Boucle 5)
setTimeout(function(){
console.log(i);
},1000);
Ici, i n'est pas exécuté, puis après la boucle complète, var i a stocké la valeur 5 en mémoire, mais sa portée est toujours visible dans sa fonction enfants, donc quand la fonction s'exécute à l' setTimeout
envers cinq fois, elle s'imprime5,5,5,5,5
donc pour résoudre cette utilisation IIFE comme expliqué ci-dessus.
Currying: Il vous permet d'évaluer partiellement une fonction en passant uniquement un sous-ensemble de ses arguments. Considère ceci:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
Clôture: Une fermeture n'est rien d'autre que l'accès à une variable en dehors de la portée d'une fonction. Il est important de se rappeler qu'une fonction à l'intérieur d'une fonction ou une fonction imbriquée n'est pas une fermeture. Les fermetures sont toujours utilisées lorsque vous avez besoin d'accéder aux variables en dehors de la portée de la fonction.
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
La fermeture est très simple. On peut le considérer comme suit: Fermeture = fonction + son environnement lexical
Considérez la fonction suivante:
function init() {
var name = “Mozilla”;
}
Quelle sera la fermeture dans le cas ci-dessus? Fonction init () et variables dans son environnement lexical ie nom. Fermeture = init () + nom
Considérons une autre fonction:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
Quelles seront les fermetures ici? La fonction interne peut accéder aux variables de la fonction externe. displayName () peut accéder au nom de variable déclaré dans la fonction parent, init (). Cependant, les mêmes variables locales dans displayName () seront utilisées si elles existent.
Fermeture 1: fonction init + (variable name + fonction displayName ()) -> portée lexicale
Fermeture 2: fonction displayName + (variable de nom) -> portée lexicale
État dans la programmation signifie simplement se souvenir des choses.
Exemple
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
Dans le cas ci-dessus, l'état est stocké dans la variable "a". Nous suivons en ajoutant 1 à "a" plusieurs fois. Nous ne pouvons le faire que parce que nous pouvons «nous souvenir» de la valeur. Le détenteur de l'état, "a", conserve cette valeur en mémoire.
Souvent, dans les langages de programmation, vous voulez garder une trace des choses, vous souvenir des informations et y accéder ultérieurement.
Ceci, dans d'autres langues , est généralement accompli grâce à l'utilisation de classes. Une classe, tout comme les variables, garde une trace de son état. Et les instances de cette classe, à leur tour, ont également un état en leur sein. État signifie simplement des informations que vous pouvez stocker et récupérer ultérieurement.
Exemple
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
Comment pouvons-nous accéder au "poids" depuis la méthode "render"? Eh bien, grâce à l'Etat. Chaque instance de la classe Bread peut rendre son propre poids en la lisant à partir de "l'état", un endroit en mémoire où nous pourrions stocker cette information.
Maintenant, JavaScript est un langage très unique qui n'a historiquement pas de classes (il en a maintenant, mais sous le capot, il n'y a que des fonctions et des variables), donc les fermetures permettent à JavaScript de se souvenir des choses et d'y accéder plus tard.
Exemple
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
L'exemple ci-dessus a atteint l'objectif de "maintien de l'état" avec une variable. C'est bien! Cependant, cela présente l'inconvénient que la variable (le détenteur de "l'état") est maintenant exposée. On peut faire mieux. Nous pouvons utiliser des fermetures.
Exemple
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
Maintenant, notre fonction "count" peut compter. Il ne peut le faire que parce qu'il peut "tenir" l'état. L'état dans ce cas est la variable "n". Cette variable est maintenant fermée. Fermé dans le temps et l'espace. À temps parce que vous ne pourrez jamais le récupérer, le changer, lui attribuer une valeur ou interagir directement avec lui. Dans l'espace car il est géographiquement imbriqué dans la fonction "countGenerator".
Pourquoi est-ce fantastique? Parce que sans impliquer aucun autre outil sophistiqué et compliqué (par exemple des classes, des méthodes, des instances, etc.), nous sommes capables de 1. cacher 2. le contrôle à distance
On cache l'état, la variable "n", ce qui en fait une variable privée! Nous avons également créé une API qui peut contrôler cette variable d'une manière prédéfinie. En particulier, nous pouvons appeler l'API comme "count ()" et cela ajoute 1 à "n" à partir d'une "distance". En aucun cas, la forme ou la forme de quiconque ne pourra jamais accéder à "n" sauf via l'API.
Les fermetures expliquent en grande partie pourquoi.