Comment fonctionnent les fermetures JavaScript?

comment expliqueriez-vous les fermetures JavaScript à quelqu'un qui connaît les concepts qu'elles consistent (par exemple, les fonctions, les variables et autres), mais qui ne comprend pas les fermetures elles-mêmes?

j'ai vu L'exemple de schéma donné sur Wikipedia, mais malheureusement il n'a pas aidé.

7654
demandé sur Zaheer Ahmed 2008-09-21 18:12:07

30 réponses

fermetures JavaScript pour débutants

Présenté par Morris le Mar, 2006-02-21 10:19. Communauté-édité depuis.

Fermetures ne sont pas de la magie

cette page explique les fermetures afin qu'un programmeur puisse les comprendre - en utilisant du code JavaScript. Ce n'est pas pour les gourous ou fonctionnelle des programmeurs.

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

cet article est destiné aux programmeurs avec une certaine expérience de programmation dans un langage courant, et qui peuvent 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 fonctions (bar et baz), La Famille de variables locales créées dans foo est non détruit lorsque la fonction se termine. Les variables deviennent simplement invisibles pour le monde extérieur. Foo peut donc habilement retourner les fonctions bar et baz, et ils peuvent continuer à lire, écrire et communiquer entre eux à travers cette famille fermée de variables ("la fermeture") que personne d'autre ne peut se mêler, pas même quelqu'un qui appelle foo à nouveau à l'avenir.

  • une fermeture est une façon de supporter fonctions de première classe ; c'est une expression qui peut référencer des variables dans son champ d'application (lorsqu'elle a été déclarée pour la première fois), être assignée à une variable, être passée comme argument à une fonction, OU être retournée comme résultat de fonction.

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 retournée à une variable ( say2 ) dans le code ci-dessus. Si vous ne le faites pas, alors vous devez regarder cela avant de pouvoir apprendre les fermetures. Un programmeur utilisant C penserait que la fonction renvoie un pointeur vers une fonction, et que les variables say et say2 étaient chacune un pointeur vers une fonction.

Il y a une différence essentielle entre un pointeur C vers une fonction et une référence JavaScript vers une fonction. Dans JavaScript, vous pouvez penser à une variable de référence de fonction comme ayant à la fois un pointeur vers une fonction ainsi que comme un pointeur caché vers une fermeture.

le code ci-dessus a une fermeture parce que la fonction anonyme function() { console.log(text); } est déclarée à l'intérieur de une autre fonction, sayHello2() dans cet exemple. 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 langues courantes, après une fonction retourne, toutes les variables locales ne sont plus accessibles parce que le cadre de la pile est détruit.

en JavaScript, si vous déclarez une fonction à l'intérieur d'une autre fonction, alors les variables locales de la fonction externe peuvent rester accessibles après en être revenues. C'est démontré ci-dessus, parce que nous appelons la fonction say2() après que nous soyons revenus 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 se réfère à la variable text . La fonction anonyme peut faire référence à text qui contient la valeur 'Hello Bob' parce que les variables locales de sayHello2() ont été secrètement maintenues vivantes dans une fermeture.

le génie est que dans JavaScript une référence de fonction a également une référence secrète à la fermeture qu'il a été créé dans - semblable à la façon dont les délégués sont un pointeur de méthode plus une référence secrète à un objet.

autres exemples

pour une raison quelconque, les fermetures semblent vraiment difficiles à comprendre quand vous lisez à leur sujet, mais quand vous voyez quelques exemples, il devient clair comment ils fonctionnent (il m'a fallu un certain temps). Je recommande d'étudier attentivement les exemples jusqu'à ce que vous compreniez comment ils fonctionnent. Si vous commencez à utiliser des fermetures sans bien comprendre comment elles fonctionnent, vous créerez bientôt des bogues très bizarres!

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 la pile-cadre reste vivant dans la mémoire après la fonction externe existe!

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 fermeture même parce qu'elles sont toutes déclarées dans un appel unique à 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 encore setupSomeGlobals() , alors une nouvelle fermeture (stack-frame!) est créé. Le vieux gLogNumber , gIncreaseNumber , gSetNumber les variables sont remplacées par nouvelles fonctions qui ont la nouvelle fermeture. (En JavaScript, chaque fois que vous déclarez une fonction à l'intérieur d'une autre fonction, la fonction à l'intérieur est / sont recréés à nouveau chaque time le la fonction extérieure est appelée.)

exemple 5

cet exemple montre que la fermeture contient toutes les variables locales qui ont été déclarées à l'intérieur de la fonction externe avant sa sortie. Notez que la variable alice est en fait déclarée après la fonction anonymous. La fonction anonyme est déclarée en premier, et lorsque cette fonction est appelée, elle peut accéder à la variable alice parce que alice est dans le même champ d'application (JavaScript fait levage variable ). En outre sayAlice()() appelle juste directement la référence de fonction retournée de sayAlice() - il est exactement le même que ce qui a été fait auparavant, 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 aussi que la variable say est également à l'intérieur de la fermeture, et peut être accessible par toute autre fonction qui pourrait être déclarée dans sayAlice() , ou il peut être accédé de manière récursive à l'intérieur de la à l'intérieur de la fonction.

exemple 6

celui-ci est un vrai gotcha pour beaucoup de gens, donc vous devez le comprendre. Soyez très prudent si vous définissez une fonction à l'intérieur d'une boucle: les variables locales de la fermeture peuvent ne pas agir comme vous pourriez le penser en premier.

pour comprendre cet exemple, vous devez comprendre la fonction" levage de variables " dans 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 au tableau de résultats. Si vous n'êtes pas familier avec les fonctions anonymes pensez-y comme:

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

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

il peut être utile de voir ce qui se passe lorsqu'une déclaration au niveau du bloc de la variable item est utilisée (via le mot-clé let ) au lieu d'une déclaration de la variable à portée fonctionnelle via le mot-clé var . Si ce changement est fait, alors chaque fonction anonyme dans le tableau result a sa propre fermeture; lorsque l'exemple est lancé, la sortie est comme suit:

item0 undefined
item1 undefined
item2 undefined

si la variable i est également définie en utilisant let au lieu de var , alors la sortie est:

item0 1
item1 2
item2 3

exemple 7

Dans ce dernier exemple, chaque appel à la fonction principale de créer une fermeture.

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, alors la meilleure chose à faire est de jouer avec les exemples. Lire une explication est bien plus difficile que comprendre exemple. Mes explications sur les fermetures et les cadres de piles, etc. ne sont pas techniquement corrects - ce sont des simplifications grossières destinées à aider à comprendre. Une fois l'idée de base est grokked, vous pouvez ramasser les détails plus tard.

points Finals:

  • chaque fois que vous utilisez function dans une autre fonction, une fermeture est utilisée.
  • chaque fois que vous utilisez eval() à l'intérieur d'une fonction, une fermeture est utilisée. Le texte que vous eval peut faire référence aux variables locales de la fonction, et dans eval vous pouvez même créer de nouvelles variables locales en utilisant eval('var foo = …')
  • lorsque vous utilisez new Function(…) (le Function constructor ) à l'intérieur d'une fonction, cela ne crée pas de fermeture. (La nouvelle fonction ne peut pas faire référence aux variables locales de la fonction externe.)
  • une fermeture en JavaScript est comme garder une copie de toutes les variables locales, juste comme ils étaient quand une fonction est sorti.
  • il est probablement préférable de penser qu'une fermeture est toujours créée juste une entrée à une fonction, et que les variables locales sont ajoutées à cette fermeture.
  • Un nouvel ensemble de variables locales est conservé à chaque fois une fonction avec une fermeture est appelé (étant donné que la fonction contient une déclaration de fonction à l'intérieur, et une référence qu'à l'intérieur de la fonction est soit retourné ou une référence externe est maintenue dans certains façon.)
  • 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écouvrir si une référence de fonction a une fermeture ou non.
  • si vous essayez de faire des modifications de code source dynamique (par exemple: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ), cela ne marchera pas si myFunction est une fermeture (bien sûr, vous ne penserez jamais à faire du code source substitution de chaîne à l'exécution, mais...).
  • il est possible d'obtenir des déclarations de fonctions dans des déclarations de fonctions dans des fonctions &mdash, et vous pouvez obtenir des fermetures à plus d'un niveau.
  • je pense que normalement une fermeture est un terme à la fois pour la fonction avec les variables qui sont capturées. Notez que je n'utilise pas cette définition dans cet article!
  • je soupçonne que les fermetures en JavaScript diffèrent de celles normalement trouvé dans les langues fonctionnelles.

Liens

Merci

si vous avez just a appris les fermetures (ici ou ailleurs!), alors je suis intéressé par toute rétroaction de votre part au sujet de tout changement que vous pourriez suggérer qui pourrait rendre cet article plus clair. Envoyer un e-mail à morrisjohns.com (morris_closure"). S'il vous plaît noter que je ne suis pas un gourou sur JavaScript - ni sur les fermetures.


Original post par Morris peut être trouvé dans le Internet Archive .

6335
répondu flying sheep 2018-09-15 09:13:27

chaque fois que vous voyez le mot-clé de fonction dans une autre fonction, la fonction interne a accès aux variables dans 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 va toujours log 16, parce que bar peut accéder au x qui a été défini comme un argument à foo , et il peut également accéder à tmp de foo .

que est fermeture. Une fonction n'a pas besoin de retourner pour être appelée une fermeture. le simple fait d'accéder à des variables en dehors de votre champ lexical immédiat 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 va aussi log 16, parce que bar peut toujours se référer à x et tmp , même si elle n'est plus directement à l'intérieur de la portée.

cependant, comme tmp est toujours à l'intérieur de la fermeture de 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 celui-ci:

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 invoquée, un nouveau contexte d'exécution est créé. Avec les arguments de fonction et l'objet parent, ceci contexte d'exécution reçoit également toutes les variables déclarées en dehors d'elle (dans l'exemple ci-dessus, à la fois 'a' et 'b').

il est possible de créer plus d'une fonction de fermeture, soit en retournant une liste d'entre elles, soit en les définissant à des variables globales. Tous se référeront 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 D'autres littérales en JavaScript, quand foo est appelé, le nombre x est copié dans foo comme son argument x .

par contre, JavaScript utilise toujours des références lorsqu'il s'agit d'objets. Si vous dites, vous avez appelé foo avec un objet, la fermeture qu'il renvoie sera référence cet objet original!

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 à bar(10) augmentera x.memb . Ce qui n'est pas prévu, c'est que x se réfère simplement au même objet que la variable age ! Après quelques appels à bar , age.memb sera 2! Ce référencement est à la base des fuites de mémoire avec les objets HTML.

3826
répondu Ali 2018-10-10 18:16:11

AVANT-PROPOS: cette réponse a été écrite alors que la question était:

comme disait Le Vieux Albert: "si vous ne pouvez pas l'expliquer à un enfant de six ans, vous ne le comprenez pas vous-même.". J'ai essayé d'expliquer la fermeture de JS à un ami de 27 ans et j'ai échoué.

est-ce que quelqu'un peut considérer que j'ai 6 ans et que je m'intéresse étrangement à ce sujet ?

je suis presque sûr que j'étais l'un des seuls des gens qui ont essayé de prendre la question initiale au pied de la lettre. Depuis lors, la question a muté plusieurs fois, de sorte que ma réponse peut maintenant sembler incroyablement stupide et hors de propos. Espérons que l'idée générale de l'histoire reste un plaisir pour certains.


je suis un grand fan de l'analogie et de la métaphore en expliquant des concepts difficiles, alors laissez-moi essayer ma main avec une histoire.

il était une fois:

il y avait une princesse...

function princess() {

elle vivait dans un monde merveilleux plein d'aventures. Elle rencontra son Prince charmant, parcourut son monde sur une licorne, lutta contre les dragons, rencontra des animaux qui parlaient, et bien d'autres choses fantastiques.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

mais elle aurait toujours à revenir à son monde terne de corvées et d'adultes.

    return {

et elle leur racontait souvent sa dernière merveille l'aventure comme une princesse.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

mais ils ne voient qu'une petite fille...

var littleGirl = princess();

...raconter des histoires sur la magie et le fantasme.

littleGirl.story();

et même si les adultes connaissaient les 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 savons la vraie vérité; que la petite fille de la princesse à l'intérieur...

...c'est vraiment une princesse avec une petite fille à l'intérieur.

2273
répondu Jacob Swartwood 2017-11-01 11:40:09

prendre la question au sérieux, nous devrions découvrir ce qu'un enfant de 6 ans typique est capable de cognitivement, bien que, il est vrai, celui qui est intéressé par JavaScript n'est pas si typique.

On développement de l'enfance: 5 à 7 ans it says:

votre enfant pourra suivre des directions en deux étapes. Par exemple, si vous dites à votre enfant, "allez à la cuisine et apportez-moi un sac poubelle" ils seront en mesure pour se souvenir de cette direction.

Nous pouvons utiliser cet exemple pour expliquer les fermetures, comme suit:

La cuisine est une fermeture qui a une variable locale, appelée trashBags . Il ya une fonction à l'intérieur de la cuisine appelée getTrashBag qui reçoit un sac poubelle et le renvoie.

nous pouvons le coder en JavaScript comme ceci:

function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

autres points qui expliquent pourquoi les fermetures sont intéressantes:

  • chaque fois que makeKitchen() est appelé, une nouvelle fermeture est créée avec sa propre trashBags .
  • la variable trashBags est locale à l'intérieur de chaque cuisine et n'est pas accessible à l'extérieur, mais la fonction interne sur la propriété getTrashBag y a accès.
  • chaque appel de fonction crée une fermeture, mais il y aurait pas besoin de garder la clôture autour de moins qu'une fonction interne, qui a accès à l'intérieur de la clôture, peut être appelé de l'extérieur de la clôture. Retourner l'objet avec la fonction getTrashBag fait cela ici.
696
répondu dlaliberte 2018-10-10 17:50:14

L'Homme De Paille

j'ai besoin de savoir combien de fois un bouton a été cliqué et faire quelque chose sur chaque troisième clic...

Assez Évident Solution

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

maintenant cela va fonctionner, mais il ne empiète dans la portée extérieure en ajoutant une variable, dont le seul but est de garder la trace du compte. Dans certaines situations, ce serait préférable que votre application externe peut avoir besoin d'accéder à cette information. Mais dans ce cas, nous ne changeons que le comportement d'un clic sur trois, il est donc préférable que enferme cette fonctionnalité dans le gestionnaire d'événements .

envisager cette option

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

remarquez quelques petites 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 à la portée dans laquelle elle a été créée, indéfiniment. pour appliquer pratiquement ceci, j'invoque immédiatement une fonction qui renvoie une autre fonction, et parce que la fonction que je renvoie a accès à la variable de comptage interne (en raison du comportement de fermeture expliqué ci-dessus) il en résulte un champ d'utilisation privé pour la fonction résultante... Pas si simple? Nous allons le diluer...

une simple fermeture à une ligne

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

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 de la fonction retournée...

func();  // Alerts "val"
func.a;  // Undefined

compris? 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'elle conserve son état de clic En clic.

aussi, cet état variable privée est fully accessible, pour les deux lectures et en assignant à ses variables scoped privées.

voilà; vous êtes maintenant entièrement encapsuler ce comportement.

article Complet (y compris jQuery)

537
répondu jondavidjohn 2017-11-13 04:54:05
Les fermetures de

sont difficiles à expliquer parce qu'elles sont utilisées pour faire fonctionner certains comportements que tout le monde s'attend de toute façon à voir fonctionner intuitivement. Je trouve que la meilleure façon de les expliquer (et la façon dont I a 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 savait pas fermetures? Il suffit de remplacer l'appel dans la dernière ligne par son méthode corps (qui est essentiellement ce que les appels de fonction font) 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 est de laisser plus5 porter son champ d'application (ou plutôt, le champ d'application de sa société mère). De cette façon, x est bien défini et il est lié à la valeur 5.

450
répondu Konrad Rudolph 2017-03-17 08:51:15

C'est une tentative pour effacer plusieurs (possible) des malentendus au sujet des fermetures qui apparaissent dans certaines des autres réponses.

  • une fermeture n'est pas seulement créée lorsque vous retournez une fonction interne. en fait, la fonction d'enveloppe n'a pas besoin de retourner du tout pour que sa fermeture soit créée. Vous pourriez à la place assigner votre fonction interne à une variable dans une portée externe, ou passer comme une argument à une autre fonction, où il pourrait être appelé immédiatement ou ultérieurement. Par conséquent, la fermeture de la fonction de fermeture est probablement créée dès que la fonction de fermeture est appelée puisque toute fonction interne a accès à cette fermeture chaque fois que la fonction interne est appelée, avant ou après le retour de la fonction de fermeture.
  • une fermeture ne fait pas référence à une copie des anciennes valeurs de variables. dans son champ d'application. les variables elles-mêmes font partie de la fermeture, et donc la valeur vue lors de l'accès à l'une de ces variables est la dernière valeur au moment où il est accédé. C'est pourquoi les fonctions internes créées à l'intérieur des boucles peuvent être délicates, puisque chacun a accès aux mêmes variables externes plutôt que de saisir une copie des variables au moment où la fonction est créée ou appelée.
  • les "variables" dans une fermeture comprennent toutes les fonctions nommées déclaré dans la fonction. Ils incluent également des arguments de la fonction. Une fermeture a également accès à ses variables contenant la fermeture, jusqu'à la portée globale.
  • les fermetures utilisent la mémoire, mais elles ne causent pas de fuites de mémoire car JavaScript nettoie par lui-même ses propres structures circulaires qui ne sont pas référencées. Les fuites de mémoire d'Internet Explorer impliquant des fermetures sont créées lorsque les valeurs des attributs DOM ne sont pas déconnectées. cette référence renvoie à des fermetures, ce qui permet de conserver des références à des structures éventuellement circulaires.
344
répondu dlaliberte 2016-05-05 15:00:49

OK, fan des 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 dans un avion. Avion de l'aéroport. La capacité du conducteur à accéder aux 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 tout. Lorsque vous tournez 27, regardez les explication plus détaillée ou l'exemple ci-dessous.

Voici comment je peux convertir mon histoire d'avion en 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");
332
répondu Max Tkachenko 2018-10-10 18:38:01

A fermeture ressemble beaucoup à un objet. Il s'instancie à chaque fois que vous appelez une fonction.

la portée d'une fermeture en JavaScript est lexicale, ce qui signifie que tout ce qui est contenu dans la fonction fermeture appartient, a accès à toute variable qui y est.

Une variable est contenue dans le fermeture si vous

  1. attribuer var foo=1; ou
  2. il suffit d'écrire var foo;

si une fonction interne (une fonction contenue dans une autre fonction) accède à une telle variable sans la définir dans sa propre portée avec var, elle modifie le contenu de la variable dans la "fermeture" extérieure 151950920 .

A closure survit à l'exécution de la fonction qui a engendré il. Si d'autres fonctions le font hors du closure/scope dans lequel elles sont définies (par exemple comme des valeurs de retour), ceux-ci continueront de se référer que closure .

exemple

function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

sortie

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
323
répondu Florian Bösch 2018-10-10 18:39:20

j'ai écrit un billet de blog un peu en arrière expliquant les fermetures. Voici ce que j'ai dit à propos des fermetures en termes de pourquoi vous en voulez une.

fermetures sont une façon de laisser une fonction ont persistant, variables privées - c'est-à-dire des variables qui seule la fonction connaît, où il peut Gardez une trace des informations des heures précédentes qu'il a été exécuté.

en ce sens, ils laissez une fonction agir un peu comme un objet avec des attributs privés.

Plein de post:

alors, quels sont ces trucs de fermeture?

216
répondu Nathan Long 2013-01-28 02:23:21

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 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_calculator crée une nouvelle variable locale n , qui continue à être utilisable par ce les fonctions add et multiply de calculatrice longtemps après les retours make_calculator .

si vous êtes familier avec les cadres de pile, ces calculatrices semblent étranges: comment peuvent-elles continuer à accéder à n après les retours make_calculator ? La réponse est d'imaginer que JavaScript n'utilise pas" stack frames", mais plutôt" heap frames", qui peut persister après l'appel de fonction qui les a fait revenir.

intérieur des fonctions telles que add et multiply , dont les variables d'accès sont 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, elle couvre tous les points des fermetures. pour les Nuls "article donné dans une autre réponse , à l'exception de l'exemple 6, qui montre simplement que les variables peuvent être utilisées avant qu'elles ne soient déclarées, un fait agréable à savoir mais complètement sans rapport avec les fermetures. Il couvre également tous les points dans la réponse acceptée , à l'exception des points (1) que les fonctions copient leurs arguments en variables locales (les arguments de fonction nommés), et (2) que la copie de nombres crée un nouveau nombre, mais la copie d'un objet référence vous donne une autre référence au même objet. Ils sont également bons à savoir, mais encore une fois tout à fait sans 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 de cette réponse ou ce commentaire , qui est que JavaScript rend difficile de brancher la valeur actuelle d'une variable de boucle dans votre intérieur fonction: l'étape" brancher " ne peut être effectuée qu'avec une fonction d'aide qui entoure votre fonction interne et est invoquée sur chaque itération de boucle. (À proprement parler, la fonction interne accède à la copie de la variable de la fonction helper, 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 comment elle fonctionne. Il y a une confusion supplémentaire due au fait que les fermetures fonctionnent différemment dans les langages fonctionnels comme ML, où les variables sont liées. aux valeurs plutôt qu'à l'espace de stockage, en fournissant un flux constant de personnes qui comprennent les fermetures d'une manière (à savoir la manière de "brancher") qui est tout simplement incorrecte 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 souligne clairement.

199
répondu Matt 2017-05-23 12:10:46

comment je l'expliquerais à un enfant de six ans:

vous savez comment les adultes peuvent posséder une maison, et ils l'appellent la maison? Quand une mère a un enfant, l'enfant ne possède rien, n'est-ce pas? Mais ses parents possèdent une maison, donc quand quelqu'un demande à l'enfant "où est ta maison?", il/elle peut répondre " cette maison!"et pointe vers la maison de ses parents. Une "fermeture" est la capacité de l'enfant à toujours (même à l'étranger) pouvoir dire qu'il a un foyer, même si c'est vraiment le parents qui sont propriétaires de la maison.

196
répondu Magne 2016-01-16 02:30:44

pouvez-vous expliquer la fermeture d'un enfant de 5 ans?*

je pense toujours l'explication de Google fonctionne très bien et est concis:

/*
*    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);​

Proof that this example creates a closure even if the inner function doesn't return

*A C # question

187
répondu Chris S 2017-05-23 11:47:32

j'ai tendance à mieux apprendre par de BONNES/MAUVAISES comparaisons. J'aime voir le code de travail suivi du code de non-travail que quelqu'un est susceptible de rencontrer. J'ai mis ensemble un jsFiddle qui fait une comparaison et tente de faire bouillir les différences aux explications les plus simples que je pourrais trouver.

les Fermetures de fait:

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 le code ci-dessus createClosure(n) est invoqué dans chaque itération de la boucle. Notez que j'ai nommé la variable n pour souligner qu'il s'agit d'une nouvelle variable créée dans une nouvelle fonction portée et n'est pas la même variable que index qui est liée à la portée extérieure.

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

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

  • dans chaque domaine d'application n est lié à n'importe quelle valeur qu'il avait quand createClosure(n) a été invoqué ainsi la fonction imbriquée qui obtient retourné retournera toujours la valeur de n qu'il avait quand createClosure(n) a été invoqué.

Fermetures fait de mal:

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 fonction createClosureArray() et la fonction renvoie maintenant le tableau complet, ce qui à première vue semble plus intuitif.

  • ce qui n'est peut-être pas évident, c'est que createClosureArray() n'est invoqué qu'une fois qu'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é index est défini. La boucle exécute et ajoute des fonctions au tableau qui renvoie index . Notez que index est défini dans la fonction createClosureArray qui n'est invoquée qu'une seule fois.

  • comme il n'y avait qu'une seule portée dans la fonction createClosureArray() , index n'est lié qu'à une valeur dans cette portée. En d'autres termes, chaque fois que la boucle change la valeur de index , il le modifie pour tout que les références à l'intérieur de ce champ d'application.

  • toutes les fonctions ajoutées au tableau renvoient la même variable index de la portée mère où elle a été définie au lieu de 10 différentes de 10 portées différentes comme dans le premier exemple. Le résultat final est que les 10 fonctions renvoient la même variable à partir du même champ d'application.

  • après la fin de la boucle et index a été fait être modifié la valeur finale était 10, donc chaque fonction ajoutée au tableau retourne la valeur de la variable index unique qui est maintenant fixée à 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 MAL FAITES

n = 10

n = 10

n = 10

n = 10

n = 10

n = 10

n = 10

n = 10

n = 10

n = 10

164
répondu Chev 2017-03-27 17:56:11

Wikipédia sur les fermetures :

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

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

depuis la construction définissant la portée en JavaScript est une fonction , pas un bloc de code comme dans beaucoup d'autres langues, ce que nous entendons habituellement par fermeture en JavaScript est une fonction travaillant avec des variables non locales définies dans la fonction d'environnement 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 il n'a pas à ê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 retournons pas une fonction, mais un objet contenant plusieurs fonctions pour des fins différentes, chacune de ces fonctions ayant accès aux mêmes données.

154
répondu mykhal 2013-12-18 16:48:34

j'ai créé un tutoriel interactif en JavaScript pour expliquer comment fonctionnent les fermetures. Qu'est-ce qu'une fermeture?

voici 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
128
répondu Nathan Whitehead 2014-10-25 22:38:03

Les enfants se souviendront toujours des secrets qu'ils ont partagé avec leurs parents, même après que leurs parents sont disparu. 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 le prénom "Marie"est donné. Et chaque fois que la fonction sort, la variable est perdue et le nom est oublié.

comme vous pouvez le deviner, parce que les variables sont recréées chaque fois que la fonction est appelée, et que personne d'autre ne les connaîtra, il doit y avoir un endroit secret où elles sont stockées. Il pourrait être appelé Chambre des Secrets ou pile ou locale mais elle n'a pas vraiment d'importance. Nous savons qu'ils sont là, quelque part, caché dans la mémoire.

mais, en JavaScript il y a ce très chose 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 garder aussi longtemps qu'ils vivent.

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

ainsi, tant que nous sommes dans la fonction parent, elle peut créer une ou plusieurs fonctions enfant qui partagent les variables secrètes de la place secrète.

Mais la chose triste est, si l'enfant est également une variable privée de sa fonction parente, elle aussi mourir lorsque le parent les secrets mourraient avec eux.

pour vivre, l'enfant doit quitter 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 Marie" ne court plus", le souvenir d'elle n'est pas perdu et son enfant se souviendra toujours de son nom et d'autres secrets qu'ils ont partagés pendant leur temps ensemble.

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.

121
répondu Tero Tolonen 2017-07-13 11:27:32

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

Voici une fermeture:

var a = 42;

function b() { return a; }

Oui. Tu l'utilises probablement plusieurs fois par jour.



il n'y a aucune raison de croire que les fermetures sont un piratage de conception complexe pour résoudre des problèmes spécifiques. Non, les fermetures sont sur le point d'utiliser une variable qui vient d'un plus haut champ d'application du point de vue de l'endroit où la fonction a été déclarée (non exécutée) .

Maintenant, ce qu'il permet vous faire peut-être plus spectaculaire, voir les autres réponses.

100
répondu floribon 2015-02-21 23:48:56

exemple pour le premier point par dlaliberte:

une fermeture n'est pas seulement créée lorsque vous retournez une fonction interne. En fait, la fonction englobante n'a pas besoin de retourner le tout. Vous pourriez à la place assigner votre fonction interne à une variable dans une portée externe, ou la passer comme argument à une autre fonction où elle pourrait être utilisée immédiatement. Par conséquent, la fermeture de la fonction enveloppante existe probablement déjà au moment où la fonction enveloppante a été appelé car toute fonction interne y a accès dès qu'il est appelé.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
87
répondu someisaac 2016-01-16 02:39:35

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

81
répondu Rakesh Pai 2012-12-24 11:10:56

je sais qu'il y a déjà beaucoup de solutions, mais je suppose que ce petit et simple script peut être utile pour démontrer 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
81
répondu Gerardo Lima 2016-05-09 11:32:47

tu passes la nuit ici et tu invites Dan. 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?

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
77
répondu StewShack 2011-07-20 15:16:26

les fonctions JavaScript peuvent accéder à leur:

  1. Arguments
  2. locaux (c'est-à-dire leurs variables locales et leurs fonctions locales)
  3. environnement, qui comprend:
    • globaux, y compris les DOM
    • quoi que ce soit à l'extérieur de fonctions

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

notez que les fonctions externes ne sont pas nécessaires, 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 vivantes. Dans le sous-ensemble 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, alors la ou les fonctions internes maintiennent les données locales de la fonction externe vivantes.

Exemple d'une fermeture qui utilise l'environnement mondial:

Imaginez que les évènements de Vote-Up et de vote-Down de débordement de pile 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. (Pour des raisons de simplicité, je fais référence aux boutons de Vote Question de StackOverflow, et non au tableau des boutons de vote réponse.)

lorsque l'utilisateur clique sur le bouton VoteUp, la fonction voteUp_click vérifie si isvoeddown = = vrai pour déterminer si de voter vers le haut ou simplement annuler un vote vers le bas. La fonction voteUp_click est une fermeture parce qu'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 ont toutes accès à leur environnement.

73
répondu John Pick 2016-06-08 22:16:22

l'auteur de fermetures a assez bien expliqué les fermetures, expliquant la raison pour laquelle nous en avons besoin et expliquant également L'environnement lexical qui est nécessaire pour comprendre les fermetures.

Voici le résumé:

que se passe-t-il si une variable est accessible, mais qu'elle n'est pas locale? Comme ici:

Enter image description here

dans ce cas, l'interprète trouve la variable dans le extérieur LexicalEnvironment objet.

le processus se compose de deux étapes:

  1. tout D'abord, lorsqu'une fonction f est créée, elle n'est pas créée dans un vide espace. Il existe actuellement un objectifenvironnemental lexical. Dans le cas ci-dessus, la fenêtre (une n'est pas définie au moment de fonction création.)

Enter image description here

Lorsqu'une fonction est créée, elle obtient une propriété cachée, nommée [[Scope]], qui fait référence à l'environnement lexique actuel.

Enter image description here

si une variable est lue, mais ne peut être trouvée nulle part, une erreur est générée.

fonctions imbriquées

fonctions peuvent être imbriquées les unes dans les autres, elles forment une chaîne D'environnements lexicaux que l'on peut aussi appeler chaîne de portée.

Enter image description here

ainsi, 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:

Enter image description here

Marking up Lexicalenvironnements:

Enter image description here

comme nous le voyons, this.say est une propriété dans l'objet utilisateur, il continue donc à vivre après que L'Utilisateur a terminé.

et si vous vous souvenez, quand this.say est créé, il (comme chaque fonction) obtient une référence interne this.say.[[Scope]] à l'environnement lexical actuel. Ainsi, l' L'environnement lexique de L'utilisateur actuel reste l'exécution en mémoire. Toutes les variables de L'utilisateur sont également ses propriétés, de sorte qu'ils sont également soigneusement conservés, pas junked comme d'habitude.

le point entier est de s'assurer que si la fonction interne veut accéder à une variable externe dans le futur, il est en mesure de le faire.

pour résumer:

  1. La fonction interne conserve une référence à la extérieur Lexicalenvironnement.
  2. la fonction interne peut accéder à des variables tout le temps, même si la fonction externe est fini.
  3. le navigateur garde en mémoire L'environnement lexical et toutes ses propriétés (variables) jusqu'à ce qu'il y ait une fonction interne qui le renvoie.

c'est ce qu'on appelle une fermeture.

72
répondu Arvand 2018-05-14 20:51:20

en tant que père d'un enfant de 6 ans, enseignant actuellement aux jeunes enfants (et un novice relatif au codage sans éducation formelle afin que les corrections seront nécessaires), je pense que la leçon serait le mieux coller à travers le jeu pratique. Si l'enfant de 6 ans est prêt à comprendre ce qu'est une fermeture, alors ils sont assez vieux pour avoir un aller eux-mêmes. Je suggère de coller le code dans jsfiddle.net, expliquant un peu, et les laissant 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

des DONNÉES: les Données est une collection de faits. Il peut s'agir de chiffres, de mots, de mesures, d'observations ou même de simples descriptions de choses. Vous ne pouvez pas le toucher, l'odorat ou le goût. Vous pouvez l'écrire, le parler et l'entendre. Vous pouvez l'utiliser pour créer toucher l'odorat et du goût à l'aide d'un ordinateur. Il peut être rendu utile par un ordinateur utilisant code.

CODE: Toute l'écriture ci-dessus est appelée code . Il est écrit en JavaScript.

JAVASCRIPT: JavaScript is a language. Comme l'anglais ou le français ou le chinois sont des langues. Il y a beaucoup de langues qui sont comprises 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 professeur 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" - donc 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 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 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. Il pourrait être une petite usine avec une seule machine à l'intérieur. Ou il pourrait contenir beaucoup d'autres petites usines, chacune avec beaucoup de machines faisant des travaux différents. Dans une vraie usine de vêtements vous pourriez avoir des rames de tissu et des bobines de fil entrant 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 le métal. Dans notre usine JavaScript, les données entrent et sortent.

Tout ce truc de données semble un peu ennuyeux, mais c'est vraiment très cool; nous pourrions avoir une fonction qui dit à un robot ce qu'il doit faire pour le dîner. Disons que je vous invite, toi et ton ami, chez moi. Tu préfères les cuisses de poulet, j'aime les saucisses, ton ami veut toujours ce que tu veux et mon ami ne pas manger de la viande.

Je n'ai pas le temps d'aller magasiner, donc la fonction doit savoir ce que nous avons dans le frigo pour prendre des décisions. Chaque ingrédient a un autre temps de cuisson 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 frigo, et la fonction pourrait contrôler le robot.

une fonction a normalement un nom, entre parenthèses et les accolades. Comme ceci:

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

notez que le code /*...*/ et // n'est pas lu par le navigateur.

nom: vous pouvez appeler une fonction à peu près n'importe quel mot que vous voulez. L'exemple "cookMeal" est typique pour joindre deux mots ensemble et donner au second une lettre majuscule au début - mais ce n'est pas nécessaire. Il ne peut pas y avoir de place, et ça ne peut pas être un numéro tout 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 postale dans la rue pour envoyer des paquets d'informations à l'usine. Parfois, la boîte postale peut être marquée par exemple cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) , auquel cas vous savez quelles données vous devez lui donner.

bretelles: "bretelles" qui ressemblent à ce {} sont les fenêtres teintées de notre usine. De l'intérieur de l' on voit à l'usine, mais de l'extérieur on ne voit pas à l'intérieur.

LE LONG EXEMPLE DE CODE AU-DESSUS DE

notre code commence par le mot fonction , donc nous savons que c'est un! Puis le nom de la fonction chanter - c'est ma propre description de ce que la fonction est d'environ. Puis les 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 en: (person) . Après cela, il y a un support comme celui-ci { . Cela marque le début de la fonction chanter() . Il a un partenaire qui marque la fin de sing () comme ce }

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

donc cette fonction pourrait avoir quelque chose à voir avec le chant, et pourrait avoir besoin de quelques données sur une personne. Il a des instructions à l'intérieur de faire quelque chose avec ces données.

maintenant, après la fonction sing () , près de la fin du code est la ligne

var person="an old lady";

VARIABLE: les lettres var signifient "variable". Une variable est comme une enveloppe. À l'extérieur, cette enveloppe porte la mention "personne". À l'intérieur, il contient une feuille de papier avec les informations de notre fonction des besoins, des lettres et des espaces réunis comme un morceau de ficelle (c'est appelé une ficelle) qui fait une phrase qui lit "une vieille dame". Notre enveloppe peut 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 toutes les accolades {} , et parce que vous pouvez voir à travers les fenêtres teintées quand 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 "à" un jeune homme", la personne continuera à être un jeune homme jusqu'à ce que vous décidez de le changer à nouveau et que toute autre fonction dans le code peut voir que c'est un jeune homme. Appuyez sur le bouton F12 ou regardez les options pour ouvrir la console de développement d'un navigateur et tapez "personne" à voyez quelle est cette valeur. Tapez person="a young man" pour le changer, puis tapez à nouveau" personne " 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

"Venez chanter , Venez vous faire personne !"

Lorsque le navigateur a chargé le JavaScript code une fois cette ligne atteinte, il va démarrer la fonction. J'ai mis la ligne à la fin pour s'assurer que le navigateur a toutes les informations dont il a besoin pour fonctionner.

fonctions définir des actions - la fonction principale est sur le chant. Il contient une variable appelée première partie 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 première partie dans la console, vous n'obtiendrez pas de réponse parce que 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 plus petites fonctions qui sont à l'intérieur de la grande sing() fonction. Les petites usines à l'intérieur de la grande usine. Ils ont chacun leur propre appareil, ce qui signifie que les variables à l'intérieur d'eux ne peuvent pas être vues de l'extérieur. C'est pourquoi les noms de la les variables ( créature et résultat ) peuvent être répétées 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 parce qu'elle est cachée par deux couches de fenêtres teintées.

les fermetures savent Toutes ce qu'est la sing () variable de la fonction appelée première partie , parce qu'ils peuvent voir à partir de leurs fenêtres teintées.

après les fermetures viennent les lignes

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

la fonction sing() appellera chacune de ces fonctions dans l'ordre où elles sont données. Alors le travail de la fonction sing () sera fait.

58
répondu grateful 2016-06-08 22:11:57

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

imaginez-vous-vous jouez avec vos petits frères et sœurs dans toute la maison, et vous vous déplacez avec vos jouets et apporté certains d'entre eux dans la chambre de votre frère aîné. Après un certain temps, votre frère est revenu de l'école et est allé dans sa chambre, et il s'est enfermé à l'intérieur, donc maintenant vous ne pouviez plus accéder aux jouets laissés là d'une manière directe. Mais tu pourrais frapper à la porte et demander ces jouets à ton frère. C'est ce qu'on appelle le fermeture de toy ; votre frère l'a inventé pour vous, et il est maintenant dans le scope extérieur .

comparer avec une situation où une porte a été verrouillée par le courant d'air et personne à l'intérieur (exécution de la fonction générale), puis un certain feu local se produire et brûler la salle (collecteur d'ordures:D), puis une nouvelle salle a été construit et maintenant vous pouvez laisser d'autres jouets là (nouvelle instance de fonction), mais jamais obtenir les mêmes jouets qui ont été laissés dans la première instance de pièce.

pour un enfant avancé, je dirais quelque chose comme ceci. Il n'est pas parfait, mais il vous fait sentir sur ce qu'il 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 voir, les jouets laissés dans la chambre sont toujours accessibles via le frère et peu importe si la chambre est verrouillée. Voici un jsbin pour jouer avec elle.

52
répondu dmi3y 2014-10-25 22:52:13

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 ce qu'est une donnée):

Les fonctions

peuvent renvoyer des données. Un type de données, vous pouvez le retour d'une fonction est une autre fonction. Lorsque cette nouvelle fonction est retournée, toutes les variables et arguments utilisés dans la fonction qui l'a créée ne disparaissent pas. Au lieu de cela, cette fonction de parent "se ferme."En d'autres termes, rien ne peut regarder à l'intérieur d'elle et de voir les variables, sauf pour le la fonction elle revenait. Cette nouvelle fonction a une capacité à regarder en arrière à l'intérieur de la fonction qui l'a créé et voir les données à l'intérieur de celui-ci.

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 de l'expliquer est en termes de portée:

chaque fois que vous créez une plus petite portée à l'intérieur d'une plus grande portée, la plus petite portée sera toujours en mesure de voir ce qui est dans la plus grande portée.

48
répondu Stupid Stupid 2014-10-25 23:02:19

une fonction en JavaScript n'est pas seulement une référence à un ensemble d'instructions (comme en langage C), mais elle inclut é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 pièces sont appelées fermetures. Chaque fonction en JavaScript peut être considérée comme une fermeture.

fermetures sont des fonctions avec un État. Il est quelque peu similaire à "ceci" dans le sens où "ceci" fournit également un État pour un fonction mais Fonction et" ceci"sont des objets séparés ("ceci" est juste un paramètre fantaisiste, et la seule façon de le lier de façon permanente à une fonction 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 la langue 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 sa fonction lexicalement imbriquée. les fonctions d'enclosage (les variables globales peuvent être supposées être des variables locales d'une certaine fonction racine), et chaque exécution d'une fonction crée de nouvelles instances de ses variables locales, il s'ensuit que chaque exécution d'une fonction retournant (ou la transférant autrement, comme l'enregistrer comme un 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ésentent son contexte d'exécution).

Also, il faut comprendre que les variables locales en JavaScript ne sont pas créées sur le cadre de la pile, mais sur le tas et détruites seulement lorsque personne ne les renvoie. Quand 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 courante 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 à certains codes externes).

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();
47
répondu srgstm 2016-05-05 16:04:06

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 le concept de fermeture en JavaScript cliquer pour moi.

une fermeture est une fonction qui a accès à la portée d'une autre Fonction (ses variables et fonctions). La manière la plus simple de créer une fermeture est d'utiliser une fonction à l'intérieur d'une fonction; la raison étant qu'en JavaScript une fonction a toujours accès à la portée de sa fonction contenant.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

alerte: singe

dans l'exemple ci-dessus, outerFunction s'appelle innerFunction. Notez comment outerVar est disponible à innerFunction, mis en évidence par son correctement alerter la valeur d'outerVar.

considérez maintenant ce qui suit:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

alerte: singe

referenceToInnerFunction est fixé à outerFunction(), qui renvoie simplement une référence à innerFunction. Quand referenceToInnerFunction est appelé, il renvoie outerVar. Encore une fois, comme ci-dessus, ceci démontre qu'innerFunction a accès à outerVar, une variable de outerFunction. De plus, il est intéressant de noter qu'il conserve cet accès même après que outerFunction a terminé l'exécution.

et voilà où les choses deviennent vraiment intéressantes. Si nous devions nous débarrasser de outerFunction, disons le mettre à null, vous pourriez penser que referenceToInnerFunction perdrait son accès à la valeur d'outerVar. Mais ce n'est pas le cas.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

alerte: singe Alerte: singe

mais comment cela se fait-il? Comment referenceToInnerFunction peut-elle encore connaître la valeur de outerVar maintenant que outerFunction a été définie à null?

la raison pour laquelle referencetournerfunction peut encore accéder à la valeur de outerVar est que lorsque la fermeture a été créée en plaçant innerFunction à l'intérieur de la fonction externe, innerFunction a ajouté une référence à la portée de la fonction externe (ses variables et fonctions) à sa chaîne de portée. Cela signifie qu'innerFunction a un pointeur ou une référence à toutes les variables d'outerFunction, y compris outerVar. Ainsi, même quand outerFunction a terminé l'exécution, ou même si elle est supprimée ou définie à null, les variables dans son champ d'application, comme outerVar, restent autour de la mémoire en raison de la référence exceptionnelle à eux sur la partie de la fonction inner qui a été retournée à la fonction referencetoinner. Pour vraiment libérer outerVar et le reste des variables d'outerFunction de la mémoire, vous devriez vous débarrasser de cette référence exceptionnelle à eux, disons en mettant referenceToInnerFunction à null ainsi.

//////////

deux autres choses à noter sur les fermetures. Premièrement, la fermeture aura toujours accès aux dernières valeurs de sa fonction contenant.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

alerte: gorille

Deuxièmement, lorsqu'une fermeture est créée, elle conserve une référence à toutes les variables et fonctions de sa fonction d'enclos; elle n'a pas à choisir. Et donc, les fermetures doivent être utilisées avec parcimonie, ou au moins avec soin, car elles peuvent être intensives en mémoire; un grand nombre de variables peuvent être conservées en mémoire longtemps après qu'une fonction contenant a terminé l'exécution.

46
répondu Michael Dziedzic 2015-04-29 15:37:06

je les pointerais simplement à la page Mozilla Closures . C'est la meilleure, la plus explication concise et simple des bases de fermeture et de l'usage pratique que j'ai trouvé. Il est fortement recommandé à quiconque apprend le JavaScript.

et oui, je le recommanderais même à un enfant de 6 ans -- si l'enfant de 6 ans apprend les fermetures, alors il est logique qu'ils soient prêts à comprendre l'explication concise et simple prévue à l'article.

43
répondu mjmoody383 2014-10-25 22:54:15