variable - Comment fonctionnent les fermetures de JavaScript?




medium closure javascript (20)

Comment expliqueriez-vous les fermetures JavaScript à une personne connaissant les concepts qui la composent (par exemple, les fonctions, les variables, etc.) sans comprendre les fermetures elles-mêmes?

J'ai vu l'exemple de Scheme donné sur Wikipedia, mais malheureusement cela n'a pas aidé.


Les enfants se souviendront toujours des secrets qu’ils ont partagés avec leurs parents, même après le départ de leurs parents. C'est ce que les fermetures sont pour les fonctions.

Les secrets pour les fonctions JavaScript sont les variables privées

var parent = function() {
 var name = "Mary"; // secret
}

Chaque fois que vous l'appelez, la variable locale "nom" est créée et appelée "Marie". Et chaque fois que la fonction quitte la variable est perdue et le nom est oublié.

Comme vous pouvez le deviner, étant donné que les variables sont recréées chaque fois que la fonction est appelée et que personne d'autre ne les connaît, il doit exister un endroit secret où elles sont stockées. Cela pourrait s'appeler la chambre des secrets ou pile ou portée locale, mais ce n'est pas grave. Nous savons qu'ils sont là, quelque part, cachés dans la mémoire.

Mais, dans JavaScript, il y a cette chose très spéciale que les fonctions qui sont créées dans d'autres fonctions peuvent également connaître les variables locales de leurs parents et les conserver aussi longtemps qu'elles vivent.

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Donc, tant que nous sommes dans la fonction parent, il peut créer une ou plusieurs fonctions enfants qui partagent les variables secrètes à partir de l'endroit secret.

Mais ce qui est triste, c’est que si l’enfant est également une variable privée de sa fonction parent, il mourrait également lorsque le parent prend fin et les secrets mourraient avec eux.

Donc, pour vivre, l'enfant doit partir avant qu'il ne soit trop tard

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

Et maintenant, même si Mary "ne court plus", son souvenir n'est pas perdu et son enfant se souviendra toujours de son nom et des autres secrets qu'ils ont partagés au cours de leur vie commune.

Donc, si vous appelez l'enfant "Alice", elle répondra

child("Alice") => "My name is Alice, child of Mary"

C'est tout ce qu'il y a à dire.


Fermetures JavaScript pour les débutants

Soumis par Morris le mar, 2006-02-21 10:19. Publié par la communauté depuis.

Les fermetures ne sont pas magiques

Cette page explique les fermetures afin qu'un programmeur puisse les comprendre - à l'aide d'un code JavaScript fonctionnel. Ce n'est pas pour les gourous ou les programmeurs fonctionnels.

Les fermetures ne sont pas difficiles à comprendre une fois que le concept de base est adopté. Cependant, ils sont impossibles à comprendre en lisant des explications théoriques ou académiques!

Cet article est destiné aux programmeurs ayant une certaine expérience en programmation dans un langage grand public et pouvant lire la fonction JavaScript suivante:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

Deux brefs résumés

  • Lorsqu'une fonction (foo) déclare d'autres fonctions (bar et baz), la famille de variables locales créées dans foo n'est pas détruite à la fermeture de la fonction. Les variables deviennent simplement invisibles au monde extérieur. Foo peut donc habilement restituer les fonctions bar et baz, et continuer à lire, écrire et communiquer les unes avec les autres via cette famille fermée de variables ("la fermeture") avec laquelle personne ne peut se mêler, pas même quelqu'un qui appelle foo encore à l'avenir.

  • Une fermeture est un moyen de soutenir des fonctions de première classe ; c'est une expression qui peut référencer des variables dans son étendue (lors de sa première déclaration), être affectée à une variable, être transmise en tant qu'argument à une fonction ou être renvoyée en tant que résultat de la fonction.

Un exemple de fermeture

Le code suivant renvoie une référence à une fonction:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

La plupart des programmeurs JavaScript comprendront comment une référence à une fonction est renvoyée vers une variable (par say2 ) dans le code ci-dessus. Si vous ne le faites pas, alors vous devez examiner cela avant de pouvoir apprendre les procédures de fermeture. Un programmeur utilisant C penserait que la fonction renvoie un pointeur sur une fonction et que les variables say et say2 sont chacune un pointeur sur une fonction.

Il existe une différence critique entre un pointeur C vers une fonction et une référence JavaScript vers une fonction. En JavaScript, vous pouvez considérer une variable de référence de fonction comme ayant à la fois un pointeur sur une fonction et un pointeur masqué sur une fermeture.

Le code ci-dessus a une fermeture parce que la fonction anonyme function() { console.log(text); } function() { console.log(text); } est déclaré dans une autre fonction, par sayHello2() , sayHello2() . En JavaScript, si vous utilisez le mot clé function dans une autre fonction, vous créez une fermeture.

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 frame de pile 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 leur retour. Ceci est démontré ci-dessus, car nous appelons la fonction say2() après notre retour de sayHello2() . Notez que le code que nous appelons fait référence à la variable text , qui était une variable locale de la fonction sayHello2() .

function() { console.log(text); } // Output of say2.toString();

En regardant la sortie de say2.toString() , nous pouvons voir que le code fait référence à la variable text . La fonction anonyme peut référencer du text sayHello2() la valeur 'Hello Bob' car les variables locales de sayHello2() ont été secrètement maintenues en vie dans une fermeture.

Le génie est qu’en JavaScript, une référence de fonction possède également une référence secrète à la fermeture dans laquelle elle a été créée - de la même manière que les délégués sont un pointeur de méthode plus une référence secrète à un objet.

Plus d'exemples

Pour une raison quelconque, les fermetures semblent vraiment difficiles à comprendre lorsque vous les lisez, mais lorsque vous voyez des exemples, vous voyez clairement comment ils fonctionnent (cela m'a pris un certain temps). Je vous recommande de lire attentivement les exemples jusqu'à ce que vous compreniez leur fonctionnement. Si vous commencez à utiliser des fermetures sans vraiment comprendre leur fonctionnement, vous créerez bientôt des bugs très étranges!

Exemple 3

Cet exemple montre que les variables locales ne sont pas copiées - elles sont conservées par référence. C'est comme si le cadre de pile restait actif en mémoire même après que la fonction externe ait existé!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Exemple 4

Les trois fonctions globales ont une référence commune à la même fermeture car elles sont toutes déclarées dans un même appel à setupSomeGlobals() .

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

Les trois fonctions ont un accès partagé à la même fermeture - les variables locales de setupSomeGlobals() lorsque les trois fonctions ont été définies.

Notez que dans l'exemple ci-dessus, si vous appelez à nouveau setupSomeGlobals() , une nouvelle fermeture (stack-frame!) Est créée. Les anciennes gLogNumber , gIncreaseNumber , gSetNumber sont écrasées par de nouvelles fonctions dotées de la nouvelle fermeture. (En JavaScript, chaque fois que vous déclarez une fonction dans une autre fonction, la ou les fonctions internes sont / sont recréées chaque fois que la fonction externe est appelée.)

Exemple 5

Cet exemple montre que la fermeture contient toutes les variables locales déclarées dans la fonction externe avant sa sortie. Notez que la variable alice est en fait déclarée après la fonction anonyme. La fonction anonyme est déclarée en premier et, lorsque cette fonction est appelée, elle peut accéder à la variable alice car celle-ci est dans la même portée (JavaScript effectue le levage de variable ). En outre, sayAlice()() appelle directement la référence de fonction renvoyée par sayAlice() . Elle est identique à celle qui a été effectuée précédemment, mais sans la variable temporaire.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: notez également que la variable say est également à l'intérieur de la fermeture et peut être accédée par toute autre fonction pouvant être déclarée dans sayAlice() ou de manière récursive dans la fonction inside.

Exemple 6

Celui-ci est un vrai truc pour beaucoup de gens, vous devez donc le comprendre. Soyez très prudent si vous définissez une fonction dans une boucle: les variables locales de la fermeture peuvent ne pas agir comme vous le pensiez.

Pour comprendre cet exemple, vous devez comprendre la fonctionnalité de "levage variable" en Javascript.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

La ligne result.push( function() {console.log(item + ' ' + list[i])} ajoute une référence à une fonction anonyme trois fois dans le tableau de résultats. Si vous n'êtes pas familiarisé avec les fonctions anonymes, pensez à il aime:

pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

Notez que lorsque vous exécutez l'exemple, "item2 undefined" est enregistré trois fois! En effet, comme dans les exemples précédents, il n'y a qu'une fermeture pour les variables locales pour buildList ( result , i et item ). Lorsque les fonctions anonymes sont appelées sur la ligne fnlist[j]() ; ils utilisent tous la même fermeture unique et utilisent la valeur actuelle pour i et le item dans cette fermeture (où i a une valeur de 3 parce que la boucle est terminée et que item a la valeur 'item2' ). Notez que nous indexons à partir de 0 et que item a la valeur de item2 . Et i ++ incrémentera i à la valeur 3 .

Il peut être utile de voir ce qui se passe lorsqu'une déclaration au niveau du bloc de l' item variable est utilisée (via le mot let clé let ) au lieu d'une déclaration de variable à fonction définie par le mot-clé var . Si cette modification est apportée, chaque fonction anonyme du result du tableau a sa propre fermeture; lorsque l'exemple est exécuté, la sortie est la suivante:

item0 undefined
item1 undefined
item2 undefined

Si la variable i est également définie en utilisant let au lieu de var , alors le résultat est:

item0 1
item1 2
item2 3

Exemple 7

Dans ce dernier exemple, chaque appel à la fonction principale crée une fermeture séparée.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Résumé

Si tout semble complètement flou, la meilleure chose à faire est de jouer avec les exemples. Lire une explication est beaucoup plus difficile que de comprendre des exemples. Mes explications sur les fermetures, les empilages, etc. ne sont pas techniquement correctes - ce sont des simplifications grossières destinées à aider à la compréhension. Une fois que l’idée de base est retenue, vous pourrez récupérer les détails ultérieurement.

Points finaux:

  • Chaque fois que vous utilisez la function dans une autre fonction, une fermeture est utilisée.
  • Chaque fois que vous utilisez eval() dans une fonction, une fermeture est utilisée. Le texte que vous eval peut faire référence à des variables locales de la fonction et vous pouvez même créer de nouvelles variables locales à l'aide de eval('var foo = …')
  • Lorsque vous utilisez la new Function(…) (le constructeur de la fonction ) dans une fonction, cela ne crée pas de fermeture. (La nouvelle fonction ne peut pas référencer les variables locales de la fonction externe.)
  • Une fermeture en JavaScript revient à conserver une copie de toutes les variables locales, exactement comme lors de la fermeture d'une fonction.
  • Il est probablement préférable de penser qu'une fermeture est toujours créée simplement comme une entrée dans une fonction et que les variables locales sont ajoutées à cette fermeture.
  • Un nouvel ensemble de variables locales est conservé chaque fois qu'une fonction avec une fermeture est appelée (étant donné que la fonction contient une déclaration de fonction et qu'une référence à cette fonction est renvoyée ou qu'une référence externe lui est conservée d'une manière ou d'une autre. ).
  • Deux fonctions peuvent sembler avoir le même texte source, mais avoir un comportement complètement différent en raison de leur fermeture "cachée". Je ne pense pas que le code JavaScript puisse réellement déterminer si une référence de fonction a une fermeture ou non.
  • Si vous essayez de modifier le code source de façon dynamique (par exemple: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ), cela ne fonctionnera pas si myFunction est une fermeture ( Bien sûr, vous ne penseriez même pas à effectuer une substitution de chaîne de code source au moment de l'exécution, mais ...).
  • Il est possible d'obtenir des déclarations de fonction dans des déclarations de fonction dans des fonctions, et vous pouvez obtenir des fermetures à plusieurs niveaux.
  • Je pense que normalement une fermeture est un terme qui désigne à la fois la fonction et les variables capturées. Notez que je n'utilise pas cette définition dans cet article!
  • Je soupçonne que les fermetures dans JavaScript diffèrent de celles que l'on trouve normalement dans les langages fonctionnels.

Liens

Merci

Si vous venez d’ apprendre les techniques de fermeture (ici ou ailleurs!), Alors je suis intéressé par tout commentaire de votre part concernant tout changement que vous pourriez suggérer et qui pourrait rendre cet article plus clair. Envoyer un email à morrisjohns.com (morris_closure @). S'il vous plaît noter que je ne suis pas un gourou sur JavaScript - ni sur les fermetures.

Le message original de Morris est disponible dans les archives Internet .


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:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

* AC # question


Les fermetures sont simples:

L'exemple simple suivant couvre tous les points principaux des fermetures de JavaScript. *

Voici une usine qui produit des calculatrices qui peuvent ajouter et multiplier:

function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

Le point clé: chaque appel à make_calculatorcréer une nouvelle variable locale n, qui continue à être utilisable par la calculatrice addet multiplyfonctionne longtemps après le make_calculatorretour.

Si vous connaissez bien les cadres de pile, ces calculatrices semblent étranges: comment peuvent-elles continuer à accéder naprès les make_calculatorretours? La solution consiste à imaginer que JavaScript n'utilise pas de "cadres de pile", mais utilise des "cadres de tas", qui peuvent persister après l'appel de la fonction qui les a renvoyés.

Les fonctions internes comme addet multiply, 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 aux fermetures.


* 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 des variables peuvent être utilisées avant d'être déclarées, ce qui est un fait intéressant à connaître mais totalement indépendant des fermetures. Il couvre également tous les points de la réponse acceptée , à l'exception des points (1) qui copient leurs arguments dans des variables locales (les arguments de la fonction nommée) et (2) qui copie des nombres crée un nouveau numéro, mais copie une référence d'objet. vous donne une autre référence au même objet. Celles-ci sont également utiles à connaître, mais là encore, elles n’ont aucun lien 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 "brancher" ne peut être effectuée qu'avec une fonction d'assistance englobant votre fonction interne et appelée à chaque itération de la boucle. (À proprement parler, la fonction interne accède à la copie de la variable de la fonction d'assistance, plutôt que de rien brancher.) Encore une fois, très utile lors de la création de fermetures, mais ne faisant pas partie de ce qu'est une fermeture ou de son fonctionnement. Il existe une confusion supplémentaire en raison du fait que les fermetures fonctionnent différemment dans des langages fonctionnels tels que ML, où les variables sont liées à des valeurs plutôt qu’à l’espace de stockage, fournissant ainsi un flot constant de personnes qui comprennent les fermetures de manière tout simplement incorrect pour JavaScript, où les variables sont toujours liées à un espace de stockage et jamais à des valeurs.

** Toute fonction externe, si plusieurs sont imbriquées, ou même dans le contexte global, comme le montre clairement cette réponse .


Chaque fois que vous voyez le mot-clé de fonction dans une autre fonction, la fonction interne a accès aux variables de la fonction externe.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Cela consignera toujours 16, car bar peut accéder au x défini comme argument de foo et à tmp de foo .

C'est une fermeture. Une fonction n'a pas à retourner pour s'appeler une fermeture. Accéder simplement à des variables en dehors de votre portée lexicale immédiate crée une fermeture .

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

La fonction ci-dessus consignera également le journal 16, car bar peut toujours se référer à x et à tmp , même s’il ne se trouve plus directement dans la portée.

Cependant, étant donné que tmp traîne toujours à l'intérieur de la fermeture du bar , il est également incrémenté. Il sera incrémenté chaque fois que vous appelez bar .

L'exemple le plus simple d'une fermeture est le suivant:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Lorsqu'une fonction JavaScript est appelée, un nouveau contexte d'exécution est créé. Avec les arguments de la fonction et l'objet parent, ce contexte d'exécution reçoit également toutes les variables déclarées en dehors de celui-ci (dans l'exemple ci-dessus, les deux expressions "a" et "b").

Il est possible de créer plusieurs fonctions de fermeture, soit en renvoyant une liste de celles-ci, soit en les définissant comme variables globales. Tous feront référence au même x et au même tmp , ils ne font pas leurs propres copies.

Ici, le nombre x est un nombre littéral. Comme avec les autres littéraux en JavaScript, lorsque foo est appelé, le nombre x est copié dans foo tant qu'argument x .

D'autre part, JavaScript utilise toujours des références lorsqu'il traite des objets. Si par exemple, vous avez appelé foo avec un objet, la fermeture qu'il renvoie fera référence à cet objet d'origine!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Comme prévu, chaque appel à la bar(10) incrémentera x.memb . Ce qui pourrait ne pas être attendu, c'est que x fait simplement référence au même objet que la variable age ! Après quelques appels au bar , age.memb sera 2! Ce référencement constitue la base des fuites de mémoire avec les objets HTML.


Les fermetures sont difficiles à expliquer car elles sont utilisées pour faire fonctionner un comportement que tout le monde s'attend intuitivement à travailler de toute façon. Je trouve 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:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

Que se passerait-il ici si JavaScript ne connaissait pas les fermetures? Il suffit de remplacer l'appel de la dernière ligne par son corps de méthode (ce qui est fondamentalement le cas des appels de fonction) et vous obtenez:

console.log(x + 3);

Maintenant, où est la définition de x ? Nous ne l'avons pas défini dans la portée actuelle. La seule solution consiste à laisser plus5 transporter son périmètre (ou plutôt celui de son parent). De cette façon, x est bien défini et il est lié à la valeur 5.


Wikipedia sur les fermetures :

En informatique, une fermeture est une fonction associée à un environnement de référence pour les noms non locaux (variables libres) de cette fonction.

Techniquement, en JavaScript , chaque fonction est une fermeture . Il a toujours accès aux variables définies dans la portée environnante.

Etant donné que la construction définissant la portée en JavaScript est une fonction et non un bloc de code comme dans beaucoup d'autres langages, ce que nous entendons par fermeture en JavaScript est une fonction utilisant 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).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

ems

L'exemple ci-dessus utilise une fonction anonyme, qui a été exécutée une fois. Mais ce n'est pas obligé. Il peut être nommé (par exemple mkdb) et exécuté ultérieurement, en générant une fonction de base de données chaque fois qu'il est appelé. Chaque fonction générée aura son propre objet de base de données caché. Un autre exemple d'utilisation de fermeture est le cas où 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.


Je ne comprends pas pourquoi les réponses sont si complexes ici.

Voici une fermeture:

var a = 42;

function b() { return a; }

Oui. Vous utilisez probablement cela plusieurs fois par jour.


Il n'y a aucune raison de croire que les fermetures sont un hack de conception complexe pour résoudre des problèmes spécifiques. Non, les fermetures consistent simplement à utiliser une variable qui provient d'une étendue supérieure du point de vue de l'endroit où la fonction a été déclarée (non exécutée) .

Maintenant, ce que cela vous permet de faire peut être plus spectaculaire, voir les autres réponses.


D'accord, parlant avec un enfant de 6 ans, j'utiliserais probablement les associations suivantes.

Imaginez - vous jouez avec vos petits frères et soeurs dans toute la maison, et vous vous déplacez avec vos jouets et en emportez certains dans la chambre de votre grand frère. Après un moment, votre frère est rentré de l'école et est allé dans sa chambre. Il s'est enfermé à l'intérieur. Vous ne pouvez donc plus accéder directement aux jouets qui y sont restés. Mais vous pouvez frapper à la porte et demander à votre frère ces jouets. Ceci s'appelle la fermeture de toy ; votre frère fait pour vous, et il est maintenant en extérieur portée .

Comparez avec une situation où une porte a été verrouillée par un tirage et qu'il n'y a personne à l'intérieur (exécution générale des fonctions), puis un incendie local se produit et brûle la pièce (éboueur: D), puis une nouvelle pièce a été construite et vous pouvez maintenant quitter il y a un autre jouet (nouvelle instance de fonction), mais ne récupérez jamais les mêmes jouets qui ont été laissés dans la première instance.

Pour un enfant avancé, je mettrais quelque chose comme ceci. Ce n'est pas parfait, mais cela vous fait sentir ce que c'est:

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Comme vous pouvez le constater, les jouets laissés dans la pièce sont toujours accessibles via le frère, que la pièce soit ou non verrouillée. Voici un jsbin pour jouer avec.


Exemple pour le premier point de dlaliberte:

Une fermeture n'est pas créée uniquement lorsque vous retournez une fonction interne. En fait, la fonction englobante n'a pas besoin de revenir du tout. Vous pouvez plutôt affecter votre fonction interne à une variable d'une étendue externe ou la transmettre en tant qu'argument à une autre fonction où elle pourrait être utilisée immédiatement. Par conséquent, la fermeture de la fonction englobante existe probablement déjà au moment où la fonction englobante a été appelée, car toute fonction interne y a accès dès qu'elle est appelée.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);

Je sais qu'il existe déjà de nombreuses solutions, mais je suppose que ce script simple et petit peut être utile pour illustrer le concept:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined

Les fonctions JavaScript peuvent accéder à leurs:

  1. Arguments
  2. Locaux (c'est-à-dire leurs variables locales et leurs fonctions locales)
  3. Environnement, qui comprend:
    • globals, y compris le DOM
    • quoi que ce soit dans les fonctions externes

Si une fonction accède à son environnement, elle est une fermeture.

Notez que les fonctions externes ne sont pas nécessaires, bien qu'elles offrent des avantages dont je ne discute pas ici. En accédant aux données de 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 sortir, et pourtant, si une ou plusieurs fonctions internes survivent après la sortie de la fonction externe, 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 de bouton Vote-Up et Vote-Down de dépassement de pile soient implémentés en tant que fermetures, voteUp_click et voteDown_click, qui ont accès aux variables externes isVotedUp et isVotedDown, définies globalement. (Par souci de simplicité, je fais référence aux boutons Question Vote de , et non au tableau de boutons Réponse de vote.)

Lorsque l'utilisateur clique sur le bouton VoteUp, la fonction voteUp_click vérifie si isVotedDown == true pour déterminer s'il faut voter ou simplement annuler un vote négatif. La fonction voteUp_click est une fermeture car elle accède à son environnement.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

Ces quatre fonctions sont des fermetures car elles accèdent toutes à leur environnement.


Une réponse pour un enfant de six ans (en supposant qu'il sache 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 de celui-ci 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 dans la fonction qui l’a créée et à voir les données qu’elle contient.

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Une autre façon très simple d’expliquer cela 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 portée plus petite sera toujours en mesure de voir ce qui est dans la portée plus grande.


En tant que père d'un enfant de 6 ans, enseignant actuellement à de jeunes enfants (et novice relative au codage sans éducation formelle, des corrections seront nécessaires), je pense que la leçon serait meilleure par des jeux pratiques. Si l'enfant de 6 ans est prêt à comprendre ce qu'est une fermeture, il est alors assez vieux pour tenter sa chance. Je suggèrerais de coller le code dans jsfiddle.net, d’en 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.

function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

INSTRUCTIONS

DONNÉES: Les données sont un ensemble de faits. Cela peut être des chiffres, des mots, des mesures, des observations ou même simplement des 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 et un goût au toucher à l'aide d'un ordinateur. Cela peut être rendu utile par un ordinateur utilisant un code.

CODE: Toute l'écriture ci-dessus s'appelle du code . C'est écrit en JavaScript.

JAVASCRIPT: JavaScript est un langage. Comme l'anglais ou le français ou le chinois sont des langues. Il existe de nombreuses langues comprises par les ordinateurs et autres processeurs électroniques. Pour que JavaScript soit compris par un ordinateur, il nécessite un interprète. Imaginez si un enseignant qui ne parle que le russe vient enseigner votre classe à l'école. Lorsque l'enseignant dit "все садятся", la classe ne comprend 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 s'appelle un 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. Des exemples que vous connaissez peut-être sont Internet Explorer, Chrome, Firefox et Safari. Le navigateur peut comprendre JavaScript et dire à l'ordinateur ce qu'il doit faire. Les instructions JavaScript sont appelées fonctions.

FONCTION: Une fonction en JavaScript est comme une usine. C'est peut-être une petite usine avec une seule machine à l'intérieur. Ou bien, il peut contenir de nombreuses autres petites usines, chacune avec de nombreuses machines effectuant différents travaux. Dans une vraie usine de vêtements, il se peut que des tonnes de tissu et des bobines de fil entrent et que des t-shirts et des jeans sortent. Notre usine JavaScript ne traite que des 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 les données.

Toutes ces données semblent un peu ennuyeuses, mais c’est vraiment très cool; nous pourrions avoir une fonction qui dit à un robot quoi faire pour le dîner. Disons que je vous invite avec votre ami chez moi. Vous aimez le mieux 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 d'aller faire les courses, alors 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 des 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:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

Notez cela /*...*/et //arrêtez le code en cours de lecture par le navigateur.

NOM: Vous pouvez appeler une fonction à peu près n'importe quel mot. L'exemple "cookMeal" est typique en joignant deux mots et en donnant au second une lettre majuscule au début - mais ce n'est pas nécessaire. Il ne peut pas y avoir d'espace, ni de numéro.

PARENTHESES: Les "parenthèses" ou ()sont la boîte aux lettres située sur la porte de la fabrique de fonctions JavaScript ou une boîte aux lettres située dans la rue pour l'envoi de paquets d'informations à l'usine. Parfois, la boîte aux lettres peut être marquée, par exemple cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) , auquel cas vous savez quelles données vous devez lui donner.

BRACES: Les "bretelles" qui ressemblent à ceci {}sont les vitres teintées de notre usine. De l'intérieur de l'usine, vous pouvez voir, mais de l'extérieur, vous ne pouvez pas voir dedans.

EXEMPLE DE LONG CODE CI-DESSUS

Notre code commence par le mot fonction , nous savons donc qu'il en 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 attelle comme celle-ci {. Ceci marque le début de la fonction sing () . Il a un partenaire qui marque la fin de sing () comme ceci}

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

Donc, cette fonction peut avoir quelque chose à voir avec le chant et peut nécessiter des données sur une personne. Il contient des instructions pour faire quelque chose avec ces données.

Maintenant, après la fonction sing () , près de la fin du code se trouve la ligne

var person="an old lady";

VARIABLE: Les lettres var représentent "variable". Une variable est comme une enveloppe. À l'extérieur, cette enveloppe est marquée "personne". À l’intérieur, il contient un bout de papier reprenant les informations dont notre fonction a besoin, des lettres et des espaces réunis comme une ficelle (c’est une ficelle) qui forment une phrase disant "une vieille dame". Notre enveloppe peut contenir d'autres types d'éléments, tels que des nombres (appelés entiers), des instructions (appelées fonctions) et des listes (appelées tableaux ). Étant donné que cette variable est écrite en dehors de toutes les accolades {}et 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 "vieille dame" en "jeune homme", elle restera jeune jusqu'à ce que vous décidiez de la changer le code peut voir que c'est un jeune homme. Appuyez sur le F12bouton ou consultez les paramètres Options pour ouvrir la console de développement d'un navigateur et tapez "personne" pour connaître la valeur de cette valeur. Tapez person="a young man"pour le changer, puis tapez "personne" à nouveau pour voir qu'il a changé.

Après cela, nous avons la ligne

sing(person);

Cette ligne appelle la fonction, comme si elle appelait un chien

"Viens chanter , viens chercher quelqu'un !"

Lorsque le navigateur a chargé le code JavaScript et atteint cette ligne, il lance la fonction. Je mets la ligne à la fin pour m'assurer que le navigateur dispose de toutes les informations nécessaires à son exécution.

Les fonctions définissent les actions - la fonction principale est de chanter. Il contient une variable appelée firstPart qui s'applique au chant de la personne et 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 colorées des accolades.

FERMETURES: Les fermetures sont les plus petites fonctions qui se trouvent dans la grande fonction sing () . Les petites usines à l'intérieur de la grande usine. Ils ont chacun leur propre accolade, ce qui signifie que les variables qu’ils contiennent ne peuvent être vus 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 variable 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 firstPart de la fonction sing () , car elles peuvent voir à partir de leurs fenêtres teintées.

Après les fermetures viennent les lignes

fly();
spider();
bird();
cat();

La fonction sing () appelle chacune de ces fonctions dans l'ordre dans lequel elles sont données. Ensuite, le travail de la fonction sing () sera terminé.


J'ai créé un didacticiel JavaScript interactif pour expliquer le fonctionnement des fermetures. Qu'est-ce qu'une fermeture?

Voici l'un des exemples:

var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here

J'ai tendance à apprendre mieux par des comparaisons BON / BAD. J'aime voir le code de travail suivi du code de non-travail que quelqu'un est susceptible de rencontrer J'ai assemblé un jsFiddle qui fait une comparaison et essaie de résumer les différences aux explications les plus simples que je pourrais trouver.

Fermetures bien faites:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • Dans ce qui précède, le code createClosure(n)est invoqué à chaque itération de la boucle. Notez que j'ai nommé la variable npour souligner qu'il s'agit d'une nouvelle variable créée dans une nouvelle portée de fonction et n'est pas la même que indexcelle liée à la portée externe.

  • Cela crée une nouvelle portée et nest lié à cette portée; Cela signifie que nous avons 10 portées distinctes, une pour chaque itération.

  • createClosure(n) renvoie une fonction qui renvoie le n dans cette étendue.

  • Chaque portée nest liée à la valeur qu'elle avait lors de l' createClosure(n)appel, de sorte que la fonction imbriquée renvoyée renvoie toujours la valeur de ncelle qu'elle avait lors de l' createClosure(n)appel.

Fermetures mal faites:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • Dans le code ci-dessus, la boucle a été déplacée dans la createClosureArray()fonction et celle-ci ne fait que renvoyer le tableau complet, ce qui, à première vue, semble plus intuitif.

  • Ce qui n’est peut-être pas évident, c’est que puisque createClosureArray()n’est appelé qu’une fois, une seule portée est créée pour cette fonction au lieu d’une pour chaque itération de la boucle.

  • Dans cette fonction, une variable nommée indexest définie. La boucle s'exécute et ajoute des fonctions au tableau renvoyé index. Notez que cela indexest défini dans la createClosureArrayfonction qui n'est appelée qu'une seule fois.

  • Comme il n'y avait qu'une seule étendue dans la createClosureArray()fonction, elle indexn'est liée qu'à une valeur à l'intérieur de cette étendue. En d'autres termes, chaque fois que la boucle change la valeur de index, elle la modifie pour tout ce qui la référence dans cette étendue.

  • Toutes les fonctions ajoutées au tableau renvoient la indexvariable SAME de la portée parente où elle a été définie au lieu de 10 variables différentes parmi 10 étendues différentes, comme dans le premier exemple. Le résultat final est que toutes les 10 fonctions renvoient la même variable à partir de la même portée.

  • Une fois la boucle terminée et en indexcours de modification, la valeur finale était de 10; par conséquent, chaque fonction ajoutée au tableau renvoie la valeur de la indexvariable unique , qui est maintenant définie sur 10.

Résultat

FERMETURES FAITES À DROITE
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

FERMETURES fait de mal
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10


L'auteur de Closures a très bien expliqué les fermetures, en expliquant pourquoi nous en avons besoin et en expliquant LexicalEnvironment, qui est nécessaire à la compréhension des fermetures.
Voici le résumé:

Que se passe-t-il 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' LexicalEnvironmentobjet externe .

Le processus comprend deux étapes:

  1. Tout d'abord, lorsqu'une fonction f est créée, elle n'est pas créée dans un espace vide. Il existe un objet LexicalEnvironment en cours. Dans le cas ci-dessus, il s'agit de la fenêtre (a n'est pas défini au moment de la création de la fonction).

Lorsqu'une fonction est créée, elle obtient une propriété caché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 d’environnements Lexical pouvant également être appelée chaîne d’étendue.

Donc, la fonction g a accès à g, a et f.

Fermetures

Une fonction imbriquée peut continuer à vivre après la fin de la fonction externe:

Marquage des environnements lexicaux:

Comme nous le voyons, il this.says’agit d’une propriété dans l’objet utilisateur qui continue à vivre après la fin de l’utilisateur.

Et si vous vous en souvenez, lorsqu’elle this.sayest créée, elle (comme chaque fonction) obtient une référence interne this.say.[[Scope]]au LexicalEnvironment actuel. Ainsi, l'environnement LexicalEnvironment de l'exécution actuelle de l'utilisateur reste en mémoire. Toutes les variables de l'utilisateur sont également ses propriétés, de sorte qu'elles sont également conservées avec soin, et non mises à l'abri de la manière habituelle.

Le but est de s'assurer que si la fonction interne veut accéder à une variable externe dans le futur, elle peut le faire.

Résumer:

  1. La fonction interne conserve une référence à l'environnement LexicalEnvironment externe.
  2. La fonction interne peut accéder aux variables à partir de celle-ci à tout moment, même si la fonction externe est terminée.
  3. Le navigateur conserve le LexicalEnvironment et toutes ses propriétés (variables) en mémoire jusqu'à ce qu'une fonction interne le référence.

Ceci s'appelle une fermeture.


OK, fan de fermetures de 6 ans. Voulez-vous entendre le plus simple exemple de fermeture?

Imaginons la situation suivante: un conducteur est assis dans une voiture. Cette voiture est dans un avion. L'avion est à l'aéroport. La possibilité pour le conducteur d'accéder à des objets extérieurs à sa voiture, mais à l'intérieur de l'avion, même si cet avion quitte un aéroport, constitue une fermeture. C'est tout.Quand vous aurez 27 ans, regardez l' explication plus détaillée ou l'exemple ci-dessous.

Voici comment je peux convertir mon histoire d'avion dans le code.

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");


Une fermeture est l'endroit où une fonction interne a accès à des variables dans sa fonction externe. C'est probablement l'explication d'une ligne la plus simple que vous pouvez obtenir pour les fermetures.


Une fonction en JavaScript n’est pas simplement une référence à un ensemble d’instructions (comme en langage C), elle inclut également une structure de données masquée 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. C'est un peu similaire à "this" dans le sens où "this" fournit également un état pour une fonction mais function et "this" sont des objets séparés ("this" n'est qu'un paramètre de fantaisie, et le seul moyen de le lier de manière permanente à la fonction est de créer une fermeture). Bien que "this" 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.

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 englobant lexicalement (les variables globales peuvent être supposées être des variables locales d'une fonction racine), et chaque exécution d'une fonction crée de nouvelles instances de Il en résulte que chaque exécution d’une fonction renvoyant (ou transférant d’une autre manière, par exemple son enregistrement en tant que callback) 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ésente son exécution. le contexte).

De plus, il faut bien comprendre que les variables locales en JavaScript ne sont pas créées sur le cadre de la pile, mais sur le tas, et ne sont détruites que lorsque personne ne les référence. Quand une fonction est retournée, les références à ses variables locales sont décrémentées, mais elles peuvent rester non nul si, au cours de l'exécution en cours, elles font partie d'une fermeture et sont toujours référencées par ses fonctions imbriquées lexiquement ces fonctions imbriquées ont été retournées ou autrement transférées à un code externe).

Un exemple:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();






closures