web-dev-qa-db-fra.com

Comment fonctionnent les fermetures de JavaScript?

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

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

7647
e-satis

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 le concept de base acquis. 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éée dans foo n'est pas détruite lorsque la fonction est fermée. Les variables deviennent simplement invisibles au monde extérieur. foo peut donc retourner astucieusement les fonctions bar et baz, et ils peuvent continuer à lire, écrire et communiquer les uns avec les autres au travers de cette famille de variables fermée ("la fermeture") que personne d'autre ne peut se mêler, pas même quelqu'un qui appelle à nouveau foo à l'avenir.

  • Une fermeture est un moyen de soutenir 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 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 dans une variable (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 car la fonction anonyme function() { console.log(text); } est déclarée à l'intérieur d'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 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 text qui contient la valeur 'Hello Bob' parce que les variables locales de sayHello2() ont été secrètement maintenues en vie lors d'une fermeture.

Le génie réside dans le fait 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 comment ils fonctionnent. 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 la sortie de la fonction externe!

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 variables gLogNumber, gIncreaseNumber, gSetNumber sont écrasées par 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 à chaque fois que la fonction externe est appelée. )

Exemple 5

Cet exemple montre que la fermeture contient les variables locales 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 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 alice se trouve dans la même portée (JavaScript le fait variable) ). De plus, sayAlice()() appelle directement 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 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 piège 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 au préalable.

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 familier avec les fonctions anonymes, pensez-y:

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’existe qu’une fermeture pour les variables locales pour buildList (qui sont result, i, list 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 de i et item dans cette fermeture (où i a la valeur 3 car la boucle avait terminé, et item a la valeur 'item2'). Notez que nous indexons à partir de 0, donc item a la valeur 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 la variable item est utilisée (via le mot clé let) à la place d'une déclaration de variable à fonction définie via le mot clé var. . Si cette modification est effectuée, chaque fonction anonyme du tableau result 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 à l'aide de let à la place de var, 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 distincte.

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;

Sommaire

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 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, dans eval, vous pouvez même créer de nouvelles variables locales à l'aide de eval('var foo = …').
  • Lorsque vous utilisez new Function(…) (le constructeur de la fonction ) dans une fonction, il 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 d’apporter des modifications dynamiques au code source (par exemple: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), cela ne fonctionnera pas si myFunction est une fermeture (bien entendu, vous ne penseriez même pas à faire une chaîne de code source. substitution à 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 en JavaScript diffèrent de celles que l'on trouve normalement dans les langages fonctionnels.

Liens

Merci

Si vous avez juste appris les fermetures (ici ou ailleurs!), Alors je suis intéressé par tout commentaire de votre part à propos des changements que vous pourriez suggérer et qui pourraient en résulter article plus clair. Envoyez 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 peut être trouvé dans le Internet Archive .

6963
Joel Anair

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 à la x définie comme un argument de foo, et il peut également accéder à tmp de foo.

Ce est une fermeture. Une fonction n'a pas à retourner pour s'appeler une fermeture. Le simple fait d'accéder à 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 faire référence à x et tmp, même s’il ne se trouve plus directement dans l’étendue.

Cependant, étant donné que tmp traîne 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 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 ceux-ci feront référence au mêmex 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ée, le nombre x est copié dans foo en tant qu'argument x.

D'autre part, JavaScript utilise toujours des références lorsqu'il traite des objets. Si, disons, vous avez appelé foo avec un objet, la fermeture qu'il renvoie sera 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 à 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 à bar, age.memb sera 2! Ce référencement constitue la base des fuites de mémoire avec les objets HTML.

3924
Ali

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

Comme l'a dit le vieil Albert: "Si vous ne pouvez pas expliquer cela à un enfant de six ans, vous ne le comprenez vraiment pas vous-même.".

Quelqu'un peut-il considérer que je suis 6 et étrangement intéressé par ce sujet?

Je suis à peu près sûr d'être l'une des seules personnes à avoir tenté de prendre la question initiale à la lettre. Depuis lors, la question a changé plusieurs fois et ma réponse peut maintenant sembler incroyablement stupide et déplacée. Espérons que l'idée générale de l'histoire reste amusante pour certains.


Je suis un grand fan d'analogie et de métaphore lorsque j'explique des concepts difficiles, alors laissez-moi m'essayer à une histoire.

Il était une fois:

Il y avait une princesse ...

function princess() {

Elle a vécu dans un monde merveilleux d'aventures. Elle a rencontré son prince charmant, parcouru son monde en Licorne, combattant des dragons, rencontré des animaux qui parlent et de nombreuses autres choses fantastiques.

    var adventures = [];

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

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

    /* ... */

Mais elle devrait toujours retourner dans son monde monotone de tâches ménagères et d'adultes.

    return {

Et elle leur racontait souvent sa dernière aventure incroyable en tant que princesse.

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

Mais tout ce qu'ils verraient, c'est une petite fille ...

var littleGirl = princess();

... raconter des histoires de magie et de fantaisie.

littleGirl.story();

Et même si les adultes connaissaient de vraies princesses, ils ne croiraient jamais aux licornes ni aux dragons, car ils ne pourraient jamais les voir. Les grandes personnes ont dit qu'elles n'existaient que dans l'imaginaire de la petite fille.

Mais nous connaissons la vraie vérité. que la petite fille avec la princesse à l'intérieur ...

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

2368
Jacob Swartwood

Si nous prenons la question au sérieux, nous devrions découvrir ce qu’un enfant typique de 6 ans est capable d’acquérir sur le plan cognitif, même s’il est vrai que celui qui s’intéresse à JavaScript n’est pas si typique.

Sur Développement de l'enfance: 5 à 7 ans il est écrit:

Votre enfant sera capable de suivre des instructions en deux étapes. Par exemple, si vous dites à votre enfant: "Va à la cuisine et apporte-moi un sac poubelle", ils pourront se rappeler 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 y a une fonction à l'intérieur de la cuisine appelée getTrashBag qui récupère un sac poubelle et le renvoie.

Nous pouvons coder ceci 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 expliquant pourquoi les fermetures sont intéressantes:

  • Chaque fois que makeKitchen() est appelée, une nouvelle fermeture est créée avec sa propre trashBags séparée.
  • La variable trashBags est locale à l'intérieur de chaque cuisine et n'est pas accessible à l'extérieur, mais la fonction interne de la propriété getTrashBag y a accès.
  • Chaque appel de fonction crée une fermeture, mais il ne serait pas nécessaire de garder la fermeture sauf si une fonction interne, qui a accès à l'intérieur de la fermeture, peut être appelée de l'extérieur de la fermeture. Renvoyer l'objet avec la fonction getTrashBag le fait ici.
727
dlaliberte

L'homme de paille

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

Solution assez évidente

// 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>

Cela fonctionnera à présent, mais cela empiétera sur la portée externe en ajoutant une variable dont le seul but est de garder une trace du nombre. Dans certains cas, cela serait préférable, car votre application externe pourrait avoir besoin d'accéder à ces informations. Mais dans ce cas, nous ne modifions que le comportement de chaque troisième clic. Il est donc préférable d’enfermer cette fonctionnalité dans le gestionnaire d’événements .

Considérez 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>

Notez 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'accéder indéfiniment à la portée dans laquelle elle a été créée. Pour l'appliquer de manière pratique, j'appelle immédiatement une fonction qui en renvoie une autre. fonction, et parce que la fonction que je retourne a accès à la variable de comptage interne (en raison du comportement de fermeture expliqué ci-dessus), il en résulte une portée privée à utiliser par la fonction résultante ... Pas si simple? Diluons-le ...

Une simple fermeture d'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 renvoyée sont disponibles pour la fonction renvoyée, mais elles ne le sont pas directement pour l'objet fonction renvoyé ...

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

Tu piges? Ainsi, dans notre exemple principal, la variable count est contenue dans la fermeture et toujours disponible pour le gestionnaire d'événements. Il conserve donc son état d'un clic à l'autre.

En outre, cet état de variable privée est entièrement accessible, à la fois pour les lectures et pour l'affectation à ses variables couvertes privées.

Voilà; vous encapsulez maintenant complètement ce comportement.

Article de blog complet (y compris les considérations sur jQuery)

566
jondavidjohn

Les fermetures sont difficiles à expliquer car elles sont utilisées pour faire fonctionner un comportement que tout le monde s'attend intuitivement à faire fonctionner de toute façon. Je trouve 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 non connaît 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 est de laisser plus5emporter son étendue (ou plutôt celle de son parent). De cette façon, x est bien défini et lié à la valeur 5.

476
Konrad Rudolph

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 ça. Lorsque vous aurez 27 ans, regardez le 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");
361
Max Tkachenko

Il s'agit d'une tentative pour dissiper plusieurs malentendus (possibles) sur les fermetures qui apparaissent dans certaines des autres réponses.

  • ne fermeture est non seulement créée lorsque vous retournez une fonction interne. En fait, la fonction englobante n'a pas besoin de revenir du tout afin que sa fermeture soit créée. 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 appelée immédiatement ou à tout moment ultérieurement. Par conséquent, la fermeture de la fonction englobante est probablement créée dès que la fonction englobante est appelée car 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 englobante.
  • ne fermeture ne référence pas une copie des anciennes valeurs des variables dans son champ d'application. Les variables elles-mêmes font partie de la fermeture et la valeur affichée lors de l’accès à l’une de ces variables est la dernière valeur au moment de son accès. C'est pourquoi les fonctions internes créées dans des boucles peuvent être délicates, car chacune d'entre elles a accès aux mêmes variables externes plutôt que de récupérer une copie des variables au moment de la création ou de l'appel de la fonction.
  • Les "variables" dans une fermeture incluent toutes les fonctions nommées déclarées dans la fonction. Ils incluent également les arguments de la fonction. Une fermeture a également accès aux variables de la fermeture qui la contient, jusqu'à la portée globale.
  • Les fermetures utilisent de la mémoire, mais elles ne causent pas de fuites de mémoire car JavaScript nettoie lui-même ses propres structures circulaires qui ne sont pas référencées. Des fuites de mémoire Internet Explorer impliquant des fermetures sont créées lorsqu'il ne parvient pas à déconnecter les valeurs d'attribut DOM faisant référence à des fermetures, conservant ainsi des références à des structures éventuellement circulaires.
360
dlaliberte

Un fermeture ressemble beaucoup à un objet. Il est instancié chaque fois que vous appelez une fonction.

La portée de fermeture en JavaScript est lexicale, ce qui signifie que tout ce qui est contenu dans la fonction à laquelle fermeture appartient a accès à toutes les variables qu'il contient.

Une variable est contenue dans le fermeture si vous

  1. l'assigner avec 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 le type fermeture.

A fermeture survit à l'exécution de la fonction qui l'a générée. Si d'autres fonctions le font en dehors de fermeture/portée dans lequel elles sont définies (par exemple, en tant que valeurs de retour), celles-ci continueront de référencer cela fermeture.

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
357
Florian Bösch

J'ai écrit un article de blog il y a quelque temps expliquant les fermetures. Voici ce que j'ai dit à propos des fermetures en termes de pourquoi vous en voudriez un.

Les fermetures sont un moyen de laisser une fonction avoir des variables privées persistantes , c'est-à-dire des variables qu'une seule fonction connaît, où elle peut garder une trace des informations. des temps précédents qu'il a été exécuté.

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

Message complet:

Alors, quelles sont ces choses de fermeture?

231
Nathan Long

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

Point clé: Chaque appel à make_calculator crée une nouvelle variable locale n, qui continue à être exploitable par les fonctions add et multiply longtemps. après que make_calculator soit revenu.

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

Fonctions internes telles que add et multiply, variables d'accès déclarées dans une fonction externe**, sont appelées des 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 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, un fait intéressant à connaître mais complètement sans rapport avec les fermetures. Il couvre également tous les points de la réponse acceptée , à l'exception des points (1) où les fonctions copient leurs arguments dans des variables locales (les arguments de la fonction nommée) et (2) que la copie de nombres crée un nouveau nombre, mais la copie d’une référence d’objet vous donne une autre référence au même objet. Celles-ci sont également bonnes à connaître mais encore une fois, elles n’ont aucun rapport avec les fermetures. C'est aussi 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 , c'est-à-dire que JavaScript rend difficile le branchement du courant valeur 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'à de l'espace de stockage, fournissant un flux constant de personnes qui comprennent les fermetures d'une manière (à savoir le "branchement") qui est: 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 cette réponse indique clairement.

212
Matt

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

Vous savez comment les adultes peuvent posséder une maison, et ils appellent à la maison? Quand une mère a un enfant, l'enfant ne possède pas vraiment quelque chose, non? Mais ses parents possèdent une maison, alors chaque fois que quelqu'un demande à l'enfant "Où est ta maison?", Il/elle peut répondre à "cette maison!" Et indiquer la maison de ses parents. Une "fermeture" est la capacité de l'enfant à toujours (même à l'étranger) de pouvoir dire qu'il a une maison, même si c'est vraiment le parent qui est propriétaire de la maison.

203
Magne

Pouvez-vous expliquer les fermetures à un enfant de 5 ans? *

Je pense toujours que 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

* Une question C #

195
Chris S

J'ai tendance à mieux apprendre en comparant BON/MAUVAIS. J'aime voir le code de travail suivi du code de non-travail que quelqu'un est susceptible de rencontrer. Je mets ensemble n 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 le code ci-dessus, createClosure(n) est invoqué à chaque itération de la boucle. Notez que j’ai nommé la variable n pour souligner qu’il s’agit d’une variable nouvelle créée dans une nouvelle portée de fonction et n’est pas la même que index qui est lié à la portée externe.

  • 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) renvoie une fonction qui renvoie le n dans cette étendue.

  • Dans chaque portée, n est lié à la valeur qu'il avait lorsque createClosure(n) a été invoquée, de sorte que la fonction imbriquée renvoyée renvoie toujours la valeur de n qu'elle avait quand createClosure(n). a été invoqué.

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 fonction createClosureArray() et celle-ci ne fait que renvoyer le tableau complet, ce qui semble à première vue plus intuitif.

  • Ce qui n’est peut-être pas évident, c’est que puisque createClosureArray() n’est appelé 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ée index est définie. La boucle s'exécute et ajoute des fonctions au tableau qui retournent 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 étendue dans la fonction createClosureArray(), index est uniquement liée à une valeur dans cette étendue. En d'autres termes, chaque fois que la boucle modifie 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 variable SAME index de la portée parente où elle a été définie à la place de 10 variables différentes de 10 étendues différentes, comme dans le premier exemple. Le résultat final est que toutes les 10 fonctions renvoient la même variable de la même portée.

  • Une fois la boucle terminée et la modification de index terminée, la valeur finale était 10, ainsi chaque fonction ajoutée au tableau renvoie la valeur de la variable unique index 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 FAITES MAUVAIS
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10

169
Chev

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, dans JavaScript , , chaque fonction est une fermeture . Il a toujours accès aux variables définies dans la portée environnante.

Puisque la construction définissant la portée en JavaScript est une fonction , pas un bloc de code comme dans beaucoup d'autres langages, ce que nous signifie généralement par fermeture en JavaScript est une fonction travaillant avec des variables non locales définies dans une fonction environnante déjà exécutée .

Les fermetures sont souvent utilisées pour créer des fonctions avec des données privées cachées (mais ce n'est pas toujours le cas).

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 appel. Chaque fonction générée aura son propre objet de base de données caché. Un autre exemple d'utilisation des fermetures consiste à ne pas renvoyer une fonction mais un objet contenant plusieurs fonctions à des fins différentes, chacune de ces fonctions ayant accès aux mêmes données.

161
mykhal

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
135
Nathan Whitehead

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. On pourrait l'appeler Chambre des secrets ou pile ou portée locale mais cela n'a pas vraiment d'importance. Nous savons qu'ils sont là, quelque part, cachés dans la mémoire.

Mais, dans JavaScript, il y a cette chose très spéciale que les fonctions qui sont créées à l'intérieur d'autres fonctions peuvent également connaître les variables locales de leurs parents et les conserver toute leur vie.

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 aussi une variable privée de sa fonction parent, il mourrait également à la fin du comportement du parent 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.

125
Tero Tolonen

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 portée 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 permet de faire peut être plus spectaculaire, voir les autres réponses.

102
floribon

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);
91
someisaac

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.

86
Rakesh Pai

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
85
Gerardo Lima

Vous dormez et vous invitez Dan. Vous dites à Dan d'apporter un contrôleur XBox.

Dan invite Paul. Dan demande à Paul d'apporter un contrôleur. Combien de contrôleurs ont été amenés à la fête?

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.");
81
StewShack

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 des boutons Vote-Up et Vote-Down de débordement de pile soient implémentés en tant que fermetures, voteUp_click et voteDown_click, ayant 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 StackOverflow, 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.

77
John Pick

L'auteur de Closures a assez bien expliqué les fermetures, en expliquant la raison pour laquelle nous en avons besoin et en expliquant également 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:

Enter image description here

Dans ce cas, l'interpréteur trouve la variable dans l'objet LexicalEnvironment extérieur.

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).

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 au LexicalEnvironment 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

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.

Enter image description here

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:

Enter image description here

Marquage des environnements lexicaux:

Enter image description here

Comme nous le voyons, this.say est une propriété de l’objet utilisateur, il continue donc à vivre après la fin de l’utilisateur.

Et si vous vous en souvenez bien, lorsque this.say est créé, il (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 à sac, comme d'habitude.

L'objectif est de s'assurer que si la fonction interne souhaite accéder à une variable externe à l'avenir, 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.

75
Arvand

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 sur 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 toucher les odeurs et les goûts à l'aide d'un ordinateur. Il peut être utile à un ordinateur utilisant un code.

CODE: Toute l'écriture ci-dessus s'appelle 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 il pourrait contenir de nombreuses autres petites usines, chacune avec plusieurs 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 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 que /*...*/ et // arrêtent la lecture du code par le navigateur.

NOM: Vous pouvez appeler une fonction à peu près tout le mot que vous voulez. 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 à la fabrique. Parfois, la boîte aux lettres peut être marquée par exemplecookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime), auquel cas vous savez quelles données vous devez lui fournir.

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 sing - c’est ma propre description de l’objet 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 dedans. Celui-ci a un mot dans: (person). Après cela, il y a un croisillon comme ceci {. 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 (), à la fin du code se trouve la ligne

var person="an old lady";

VARIABLE: Les lettres var signifient "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 pourrait contenir d'autres types d'éléments tels que des nombres (appelés entiers), des instructions (appelées fonctions), des listes (appelées tableaux). Comme 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", la personne restera jeune. jusqu'à ce que vous décidiez de le changer à nouveau et que n'importe quelle autre fonction du code puisse voir qu'il s'agit d'un jeune homme. appuyez sur la F12 Cliquez sur le bouton ou sur Paramètres pour ouvrir la console du développeur d’un navigateur et tapez "personne" pour connaître la valeur de cette valeur. Tapez person="a young man" pour le changer, puis tapez à nouveau "personne" pour vérifier 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 chante, Viens et reçois personne!"

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 des 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 teintées des accolades.

FERMETURES: Les fermetures sont les plus petites fonctions qui sont à l'intérieur de 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 (creature et result) peuvent être répétés dans les fermetures mais avec des valeurs différentes. Si vous tapez ces noms de variables dans la fenêtre de la console, vous n'obtiendrez pas sa valeur car elle est masquée par deux couches de fenêtres teintées.

Les fermetures savent toutes ce qu'est la variable de la fonction sing () appelée firstPart, car elles peuvent voir à 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é.

59
grateful

D'accord, parlant avec un enfant de 6 ans, j'utiliserais peut-être 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 quelques-uns 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. C'est ce qu'on appelle la fermeture de toy ; votre frère l'a inventé pour vous, et il est maintenant dans la portée externe .

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), 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 n jsbin pour jouer avec.

56
dmi3y

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 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 de l'expliquer est la 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 se trouve dans la portée plus grande.

50
Stupid Stupid

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 des fermetures. Chaque fonction en JavaScript peut être considérée comme une fermeture.

Les fermetures sont des fonctions avec un état. Il 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 à un objet. 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 réalité des variables locales dans la chaîne de ses fonctions englobant lexicalement (les variables globales peuvent être supposées être des variables locales de certaines fonctions 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 toujours être non nulles 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 lexicalement (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 à 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();
50
srgstm

Peut-être un peu au-delà de la plus précoce des enfants de six ans, mais quelques exemples qui ont contribué à rendre le concept de fermeture en JavaScript cliquez pour moi.

Une fermeture est une fonction qui a accès à la portée d'une autre fonction (ses variables et ses fonctions). Le moyen le plus simple de créer une fermeture consiste à utiliser une fonction dans une fonction. La raison en est qu’en JavaScript, une fonction a toujours accès à la portée de la fonction qui la contient.

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

outerFunction();

ALERTE: singe

Dans l'exemple ci-dessus, outerFunction est appelé, qui à son tour appelle innerFunction. Notez comment outerVar est disponible pour innerFunction, comme en témoigne son alerte correcte de la valeur de outerVar.

Considérons maintenant ce qui suit:

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

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

ALERTE: singe

referenceToInnerFunction est défini sur outerFunction (), qui renvoie simplement une référence à innerFunction. Lorsque referenceToInnerFunction est appelé, il renvoie outerVar. De nouveau, comme ci-dessus, cela démontre que 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 l'exécution de outerFunction.

Et voici où les choses deviennent vraiment intéressantes. Si nous devions nous débarrasser de outerFunction, si vous lui affectiez la valeur null, vous pourriez penser que referenceToInnerFunction perdrait son accès à la valeur de 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());

ALERT: singe ALERT: singe

Mais comment est-ce vrai? Comment referenceToInnerFunction peut-il toujours connaître la valeur de outerVar maintenant que outerFunction est défini sur null?

Si referenceToInnerFunction peut toujours accéder à la valeur de outerVar, c’est parce que, lorsque la fermeture a été créée pour la première fois en plaçant innerFunction à l’intérieur de outerFunction, innerFunction a ajouté une référence à la portée de outerFunction (ses variables et ses fonctions) à sa chaîne de portées. Cela signifie que innerFunction a un pointeur ou une référence à toutes les variables d’uterFunction, y compris outerVar. Ainsi, même lorsque outerFunction a fini de s'exécuter, ou même s'il est supprimé ou défini sur null, les variables de son étendue, telles que outerVar, restent en mémoire en raison de la référence exceptionnelle qui leur est renvoyée de la part de innerFunction qui a été renvoyée. referenceToInnerFunction. Pour libérer réellement la mémoire de outerVar et du reste des variables de outerFunction, vous devez vous débarrasser de cette référence exceptionnelle, par exemple en définissant referenceToInnerFunction sur null.

//////////

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

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 englobante; il ne faut pas choisir. Les fermetures doivent être utilisées avec parcimonie ou au moins avec précaution, car elles peuvent nécessiter beaucoup de mémoire. de nombreuses variables peuvent être conservées en mémoire longtemps après l'exécution d'une fonction contenant.

48
Michael Dziedzic

Je les dirigerais simplement vers = page Mozilla Closures . C'est la meilleure, la plus simple explication concise et simple des bases de la fermeture et de l'utilisation pratique que j'ai trouvée. Il est fortement recommandé à quiconque apprend le langage JavaScript.

Et oui, je le recommanderais même à un enfant de 6 ans - si un enfant de 6 ans en apprend sur les fermetures, il est logique qu'il soit prêt à comprendre le explication simple et concise fournie dans l'article.

46
mjmoody383

Je crois aux explications plus courtes, voyez donc l'image ci-dessous.

Enter image description here

function f1() ..> Boîte rouge clair

function f2() ..> Petite boîte rouge

Ici, nous avons deux fonctions, f1() et f2(). f2 () est interne à f1 (). f1 () a une variable, var x = 10.

Lors de l'appel de la fonction f1(), f2() peut accéder à la valeur de var x = 10.

Voici le code:

function f1() {
    var x=10;

    function f2() {
        console.log(x)
    }

    return f2

}
f1()

f1() invoquant ici:

Enter image description here

42
Dinesh Kanivu

Une fermeture est une fonction ayant accès à la portée parent, même après la fermeture de la fonction parent.

Donc, fondamentalement, une fermeture est une fonction d'une autre fonction. Nous pouvons dire comme une fonction enfant.

Une fermeture est une fonction interne qui a accès aux variables de la fonction externe (englobante) - chaîne d’étendue. La fermeture a trois chaînes de champs: elle a accès à son propre champ (variables définies entre ses accolades), elle a accès aux variables de la fonction extérieure et elle a accès aux variables globales.

La fonction interne a accès non seulement aux variables de la fonction externe, mais également à ses paramètres. Notez que la fonction interne ne peut pas appeler l'objet arguments de la fonction externe, même si elle peut appeler directement les paramètres de la fonction externe.

Vous créez une fermeture en ajoutant une fonction dans une autre fonction.

En outre, c'est une méthode très utile qui est utilisée dans de nombreux frameworks célèbres tels que Angular, Node.js et jQuery:

Les fermetures sont largement utilisées dans Node.js; Ce sont des chevaux de travail dans l’architecture asynchrone non bloquante de Node.js. Les fermetures sont également fréquemment utilisées dans jQuery et à peu près tous les morceaux de code JavaScript que vous lisez.

Mais à quoi ressemblent les fermetures dans un codage réel? Regardez cet exemple de code simple:

function showName(firstName, lastName) {
      var nameIntro = "Your name is ";
      // this inner function has access to the outer function's variables, including the parameter
      function makeFullName() {
          return nameIntro + firstName + " " + lastName;
      }
      return makeFullName();
  }

  console.log(showName("Michael", "Jackson")); // Your name is Michael Jackson

En outre, c’est une méthode classique de fermeture dans jQuery que tous les développeurs javascript et jQuery ont beaucoup utilisée:

$(function() {
    var selections = [];
    $(".niners").click(function() { // this closure has access to the selections variable
        selections.Push(this.prop("name")); // update the selections variable in the outer function's scope
    });
});

Mais pourquoi utilisons-nous des fermetures? quand on l'utilise dans une programmation réelle? Quelles sont les utilisations pratiques des fermetures? Voici une bonne explication et exemple de MDN:

Fermetures pratiques

Les fermetures sont utiles car elles vous permettent d'associer des données (l'environnement lexical) à une fonction qui agit sur ces données. Cela a des parallèles évidents avec la programmation orientée objet, où les objets nous permettent d’associer certaines données (propriétés de l’objet) à une ou plusieurs méthodes.

Par conséquent, vous pouvez utiliser une fermeture partout où vous pourriez normalement utiliser un objet avec une seule méthode.

Les situations dans lesquelles vous voudrez peut-être faire cela sont particulièrement courantes sur le Web. Une grande partie du code que nous écrivons dans le code JavaScript frontal est basé sur des événements. Nous définissons un comportement, puis nous l'attachons à un événement déclenché par l'utilisateur (comme un clic ou une pression sur une touche). Notre code est généralement associé en tant que rappel: une seule fonction qui est exécutée en réponse à l'événement.

Par exemple, supposons que nous souhaitions ajouter des boutons à une page pour ajuster la taille du texte. Une façon de procéder consiste à spécifier la taille de la police de l'élément body en pixels, puis à définir la taille des autres éléments de la page (tels que les en-têtes) à l'aide de l'unité relative em:

Lisez le code ci-dessous et lancez-le pour voir comment la fermeture nous aide à créer facilement des fonctions distinctes pour chaque section:

//javascript
function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/*css*/
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
<!--html><!-->
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

Pour un complément d'étude sur les fermetures, je vous recommande de visiter cette page par MDN: https://developer.mozilla.org/en/docs/Web/JavaScript/Closures

34
Alireza

Pour un enfant de six ans?

Vous et votre famille vivez dans la ville mythique d'Ann Ville. Vous avez un ami qui habite à côté, alors appelez-le et demandez-lui de sortir et de jouer. Vous composez:

000001 (jamiesHouse)

Après un mois, vous et votre famille quittez Ann Ville pour la ville suivante, mais vous et votre ami restez toujours en contact. Vous devez donc composer l'indicatif régional de la ville dans laquelle vit votre ami avant de composer le numéro. bon numéro:

001 000001 (annVille.jamiesHouse)

Un an après cela, vos parents déménagent dans un tout nouveau pays, mais vous et votre ami restez toujours en contact. Après avoir demandé à vos parents de vous permettre de passer des appels internationaux, vous composez le numéro suivant:

01 001 000001 (myOldCountry.annVille.jamiesHouse)

Étrangement, après avoir déménagé dans votre nouveau pays, vous et votre famille avez justement déménagé dans une nouvelle ville appelée Ann Ville ... et il vous est donc arrivé de vous lier d'amitié avec une nouvelle personne appelée Jamie ... appel...

000001 (jamiesHouse)

Effrayant...

Tellement fantasmagorique que vous en parlez à Jamie de votre vieux pays ... Vous en riez bien. Alors un jour, vous et votre famille prenez des vacances dans le vieux pays. Vous visitez votre vieille ville (Ann Ville) et allez rendre visite à Jamie ...

  • "Vraiment? Un autre Jamie? À Ann Ville? Dans ton nouveau pays !!?"
  • "Ouais ... Appelons-les ..."

02 001 000001 (myNewCountry.annVille.jamiesHouse)

Des avis?

De plus, j'ai beaucoup de questions sur la patience d'un enfant moderne de six ans ...

30
Charlie

En JavaScript, les fermetures sont géniales, où des variables ou des arguments sont disponibles pour les fonctions internes, et ils resteront actifs même après le retour de la fonction externe.

  function getFullName(a, b) {
  return a + b;
}

function makeFullName(fn) {

  return function(firstName) {

    return function(secondName) {

      return fn(firstName, secondName);
    }
  }
}

makeFullName(getFullName)("stack")("overflow"); // Stackoverflow
29
Shushanth Pallegar

Voici un scénario simple en temps réel. Lisez-le simplement et vous comprendrez comment nous avons utilisé la fermeture ici (voir comment le numéro de siège change).

Tous les autres exemples expliqués précédemment sont également très utiles pour comprendre le concept.

function movieBooking(movieName) {
    var bookedSeatCount = 0;
    return function(name) {
        ++bookedSeatCount ;
        alert( name + " - " + movieName + ", Seat - " + bookedSeatCount )
    };
};

var MI1 = movieBooking("Mission Impossible 1 ");
var MI2 = movieBooking("Mission Impossible 2 ");

MI1("Mayur");
// alert
// Mayur - Mission Impossible 1, Seat - 1

MI1("Raju");
// alert
// Raju - Mission Impossible 1, Seat - 2

MI2("Priyanka");
// alert
// Raja - Mission Impossible 2, Seat - 1
28
Mayur Randive

Les fermetures permettent aux programmeurs JavaScript d'écrire un meilleur code. Créatif, expressif et concis. Nous utilisons fréquemment des fermetures en JavaScript et, quelle que soit notre expérience en JavaScript, nous les rencontrons sans cesse. Les fermetures peuvent sembler complexes mais heureusement, une fois que vous aurez lu ceci, les fermetures seront beaucoup plus faciles à comprendre et donc plus attrayantes pour vos tâches de programmation JavaScript quotidiennes.

Vous devriez être familiarisé avec variable JavaScript scope avant de poursuivre la lecture, car pour comprendre les fermetures, vous devez comprendre la portée variable de JavaScript.

Qu'est-ce qu'une fermeture?

Une fermeture est une fonction interne qui a accès aux variables de la fonction externe (englobante) - chaîne d’étendue. La fermeture a trois chaînes de champs: elle a accès à son propre champ (variables définies entre ses accolades), elle a accès aux variables de la fonction extérieure et elle a accès aux variables globales.

La fonction interne a accès non seulement aux variables de la fonction externe, mais également à ses paramètres. Notez que la fonction interne ne peut pas appeler l'objet arguments de la fonction externe, même si elle peut appeler directement les paramètres de la fonction externe.

Vous créez une fermeture en ajoutant une fonction dans une autre fonction.

Un exemple de base de fermetures en JavaScript:

function showName (firstName, lastName) {

  var nameIntro = "Your name is ";
  // this inner function has access to the outer function's variables, including the parameter
  ​function makeFullName () {
            
​    return nameIntro + firstName + " " + lastName;
        
  }
​
​  return makeFullName ();

}

​
showName ("Michael", "Jackson"); // Your name is Michael Jackson


Les fermetures sont largement utilisées dans Node.js; Ce sont des chevaux de travail dans l’architecture asynchrone non bloquante de Node.js. Les fermetures sont également fréquemment utilisées dans jQuery et à peu près tous les morceaux de code JavaScript que vous lisez.

Un exemple classique de jQuery de fermetures:

$(function() {
​
​  var selections = []; 
  $(".niners").click(function() { // this closure has access to the selections variable​
    selections.Push (this.prop("name")); // update the selections variable in the outer function's scope​
  });
​});

Règles et effets secondaires des fermetures

1. Les fermetures ont accès à la variable de la fonction externe même après le retour de la fonction externe:

L’une des caractéristiques les plus importantes et délicates des fermetures est que la fonction interne a toujours accès aux variables de la fonction externe même après le retour de la fonction externe. Oui, vous avez bien lu. Lorsque les fonctions dans JavaScript s'exécutent, elles utilisent la même chaîne d'étendue que celle qui était en vigueur lors de leur création. Cela signifie que même après le retour de la fonction externe, celle-ci a toujours accès aux variables de la fonction externe. Par conséquent, vous pouvez appeler la fonction interne ultérieurement dans votre programme. Cet exemple montre:

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's variables, including the parameter​
   function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}
​
​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
​
​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson


2. Les fermetures stockent les références aux variables de la fonction externe:

Ils ne stockent pas la valeur réelle. Les fermetures deviennent plus intéressantes lorsque la valeur de la variable de la fonction externe change avant que la fermeture ne soit appelée. Et cette fonctionnalité puissante peut être exploitée de manière créative, comme cet exemple de variables privées présenté pour la première fois par Douglas Crockford:

function celebrityID () {
    var celebrityID = 999;
    // We are returning an object with some inner functions​
    // All the inner functions have access to the outer function's variables​
    return {
        getID: function ()  {
            // This inner function will return the UPDATED celebrityID variable​
            // It will return the current value of celebrityID, even after the changeTheID function changes it​
          return celebrityID;
        },
        setID: function (theNewID)  {
            // This inner function will change the outer function's variable anytime​
            celebrityID = theNewID;
        }
    }
​
}
​
​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable


3. Les fermetures ont mal tourné

Comme les fermetures ont accès aux valeurs mises à jour des variables de la fonction externe, elles peuvent également entraîner des bogues lorsque la variable de la fonction externe change avec une boucle for. Ainsi:

// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }
    
    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​
​var stalloneID = createIdForActionCelebs [0];

    console.log(stalloneID.id()); // 103


Plus peut être trouvé ici-

  1. http://javascript.info/tutorial/closures

  2. http://www.javascriptkit.com/javatutors/closures.shtml

26
Abrar Jahin

Voici la réponse la plus zen que je puisse donner:

Qu'attendriez-vous de ce code? Dis-moi dans un commentaire avant de l'exécuter. Je suis curieux!

function foo() {
  var i = 1;
  return function() {
    console.log(i++);
  }
}

var bar = foo();
bar();
bar();
bar();

var baz = foo();
baz();
baz();
baz();

Maintenant, ouvrez la console dans votre navigateur (Ctrl + Shift + I ou F12, espérons-le) et collez le code et appuyez sur Enter.

Si ce code imprimait ce que vous attendiez (débutants en JavaScript - ignorez le "non défini" à la fin), alors vous avez déjà compréhension sans mots. En mots, la variable i fait partie de la fonction interne instance fermeture.

Je l’exprime ainsi car, une fois que j’ai compris que ce code mettait des instances de la fonction interne de foo() dans bar et baz, puis que je les appelais via ces variables, rien d’autre ne m’étonnait.

Mais si je me trompe et que la sortie de la console vous a surpris, faites le moi savoir!

24
Andy

Plus je pense à la clôture plus je la vois comme un processus en deux étapes: init - action

init: pass first what's needed...
action: in order to achieve something for later execution.

Pour un enfant de 6 ans, je mettrais l'accent sur l'aspect pratique de la fermeture:

Daddy: Listen. Could you bring mum some milk (2).
Tom: No problem.
Daddy: Take a look at the map that Daddy has just made: mum is there and daddy is here.
Daddy: But get ready first. And bring the map with you (1), it may come in handy
Daddy: Then off you go (3). Ok?
Tom: A piece of cake!

Exemple : Apportez du lait à maman (= action). D'abord préparez-vous et apportez la carte (= init).

function getReady(map) {
    var cleverBoy = 'I examine the ' + map;
    return function(what, who) {
        return 'I bring ' + what + ' to ' + who + 'because + ' cleverBoy; //I can access the map
    }
}
var offYouGo = getReady('daddy-map');
offYouGo('milk', 'mum');

Parce que si vous apportez avec vous une information très importante (la carte), vous êtes suffisamment informé pour exécuter d'autres actions similaires:

offYouGo('potatoes', 'great mum');

Pour un développeur, je ferais un parallèle entre les fermetures et OOP . La phase d'initiation est similaire à la transmission d'arguments à un constructeur dans un langage traditionnel OO; la phase d'action est finalement la méthode que vous appelez pour obtenir ce que vous voulez. Et la méthode a accès à ces arguments init en utilisant un mécanisme appelé de fermeture .

Voir mon autre réponse illustrant le parallélisme entre OO et les fermetures:

Comment "correctement" créer un objet personnalisé en JavaScript?

22
roland

Étant donné la fonction suivante

function person(name, age){

    var name = name;
    var age = age;

    function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }

    return introduce;
}

var a = person("Jack",12);
var b = person("Matt",14);

Chaque fois que la fonction person est appelée une nouvelle fermeture est créée. Bien que les variables a et b aient la même fonction introduce, elles sont liées à des fermetures différentes. Et cette fermeture existera toujours même après que la fonction person aura terminé son exécution.

Enter image description here

a(); //My name is Jack, and I'm 12
b(); //My name is Matt, and I'm 14

Un résumé des fermetures pourrait être représenté à quelque chose comme ceci:

closure a = {
    name: "Jack",
    age: 12,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

closure b = {
    name: "Matt",
    age: 14,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

En supposant que vous sachiez comment fonctionne une class dans une autre langue, je ferai une analogie.

Pense comme

  • JavaScript function en tant que constructor
  • local variables comme instance properties
  • ces properties sont privés
  • inner functions comme instance methods

A chaque fois qu'un function est appelé

  • Un nouveau object contenant toutes les variables locales sera créé.
  • Les méthodes de cet objet ont accès à "properties" de cet objet d'instance.
22
Vitim.us

Même s'il existe sur Internet de nombreuses belles définitions de la fermeture de JavaScript, j'essaie de commencer à expliquer mon ami de six ans avec mes définitions préférées de la fermeture, ce qui m'a permis de mieux comprendre la fermeture.

Qu'est-ce qu'une fermeture?

Une fermeture est une fonction interne qui a accès aux variables de la fonction externe (englobante) - chaîne d’étendue. La fermeture a trois chaînes de champs: elle a accès à son propre champ (variables définies entre ses accolades), elle a accès aux variables de la fonction extérieure et elle a accès aux variables globales.

Une fermeture est la variable locale d'une fonction - maintenue en vie après le retour de la fonction.

Les fermetures sont des fonctions qui font référence à des variables indépendantes (libres). En d'autres termes, la fonction définie dans la fermeture "se souvient" de l'environnement dans lequel elle a été créée.

Les fermetures sont une extension du concept de portée. Avec les fermetures, les fonctions ont accès aux variables qui étaient disponibles dans l'étendue où la fonction a été créée.

Une fermeture est une image de pile qui n'est pas désallouée au retour de la fonction. (Comme si un "stack-frame" était mallocé au lieu d'être sur la pile!)

Des langages tels que Java permettent de déclarer des méthodes privées, ce qui signifie qu'elles ne peuvent être appelées que par d'autres méthodes de la même classe. JavaScript ne fournit pas un moyen natif de le faire, mais il est possible d'émuler des méthodes privées à l'aide de fermetures.

Une "fermeture" est une expression (généralement une fonction) pouvant avoir des variables libres ainsi qu'un environnement qui lie ces variables (qui "ferme" l'expression).

Les fermetures sont un mécanisme d'abstraction qui vous permet de séparer les problèmes de manière très nette.

Utilisations des fermetures:

Les fermetures sont utiles pour masquer la mise en œuvre des fonctionnalités tout en révélant l'interface.

Vous pouvez émuler le concept d'encapsulation en JavaScript à l'aide de fermetures.

Les fermetures sont largement utilisées dans jQuery et Node.js .

Bien que les littéraux d'objet soient certainement faciles à créer et pratiques pour stocker des données, les fermetures constituent souvent un meilleur choix pour créer des espaces de noms singleton statiques dans une grande application Web.

Exemple de fermetures:

En supposant que mon ami de 6 ans fasse connaissance avec l'addition très récemment dans son école primaire, j'ai pensé que cet exemple d'addition des deux chiffres serait le plus simple et le plus approprié pour que l'enfant de six ans apprenne la fermeture.

Exemple 1: La fermeture est obtenue ici en renvoyant une fonction.

function makeAdder(x) {
    return function(y) {
        return x + y;
    };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Exemple 2: La fermeture est obtenue ici en retournant un objet littéral.

function makeAdder(x) {
    return {
        add: function(y){
            return x + y;
        }
    }
}

var add5 = makeAdder(5);
console.log(add5.add(2));//7

var add10 = makeAdder(10);
console.log(add10.add(2));//12

Exemple 3: Fermetures dans jQuery

$(function(){
    var name="Closure is easy";
    $('div').click(function(){
        $('p').text(name);
    });
});

Liens utiles:

Merci aux liens ci-dessus qui m'aide à mieux comprendre et expliquer la fermeture.

21
Ravi

Pour comprendre les fermetures, vous devez entrer dans le programme et l'exécuter littéralement comme si vous étiez le temps d'exécution. Regardons ce morceau de code simple:

Enter image description here

JavaScript exécute le code en deux phases:

  • Phase de compilation // JavaScript n'est pas un langage purement interprété
  • Phase d'exécution

Lorsque JavaScript passe par la phase de compilation, il extrait les déclarations de variables et de fonctions. Ceci s'appelle le levage. Les fonctions rencontrées dans cette phase sont enregistrées en tant que blobs de texte dans la mémoire, également appelée lambda. Après la compilation, JavaScript entre en phase d'exécution où il attribue toutes les valeurs et exécute la fonction. Pour exécuter la fonction, il prépare le contexte d’exécution en affectant de la mémoire à partir du tas et en répétant la phase de compilation et d’exécution de la fonction. Cette zone mémoire est appelée étendue de la fonction. Il y a une portée globale au début de l'exécution. Les portées sont la clé pour comprendre les fermetures.

Dans cet exemple, dans un premier temps, la variable a est définie, puis f est définie dans la phase de compilation. Toutes les variables non déclarées sont enregistrées dans la portée globale. Dans la phase d'exécution, f est appelé avec un argument. La portée de f est attribuée et les phases de compilation et d'exécution sont répétées.

Les arguments sont également enregistrés dans cette portée locale pour f. Chaque fois qu'un contexte ou une portée d'exécution locale est créé, il contient un pointeur de référence sur sa portée parent. Tous les accès aux variables suivent cette chaîne d’étendue lexicale pour trouver sa valeur. Si une variable est introuvable dans la portée locale, elle suit la chaîne et la trouve dans la portée parente. C'est également la raison pour laquelle une variable locale remplace les variables de la portée parente. La portée parente est appelée la "fermeture" pour une portée ou une fonction locale.

Ici, lorsque la portée de g est configurée, un pointeur lexical sur la portée de ses parents de f. La portée de f est la fermeture de g. En JavaScript, s'il y a une référence à des fonctions, des objets ou des portées si vous pouvez les atteindre d'une manière ou d'une autre, les ordures ne seront pas récupérées. Ainsi, lorsque myG est en cours d'exécution, il a un pointeur sur la portée de f qui est sa fermeture. Cette zone de mémoire ne sera pas récupérée, même si f est revenu. C'est une fermeture en ce qui concerne l'exécution.

SO WHAT IS UNE FERMETURE?

  • C'est un lien implicite et permanent entre une fonction et sa chaîne de périmètre ...
  • Une définition de fonction (lambda) cachée [[scope]] référence.
  • Maintient la chaîne de l'oscilloscope (empêchant la récupération de place).
  • Il est utilisé et copié comme "référence d'environnement externe" chaque fois que la fonction est exécutée.

FERMETURE IMPLICITE

var data = "My Data!";
setTimeout(function() {
  console.log(data); // Prints "My Data!"
}, 3000);

FERMETURES EXPLICITES

function makeAdder(n) {
  var inc = n;
  var sum = 0;
  return function add() {
    sum = sum + inc;
    return sum;
  };
}

var adder3 = makeAdder(3);

Une conversation très intéressante sur les fermetures et plus encore est Arindam Paul - JavaScript VM internes, EventLoop, Async et ScopeChains.

21
Nikhil Ranjan

Une fermeture est une fonction dans une fonction qui a accès aux variables et paramètres de sa fonction "parent".

Exemple:

function showPostCard(Sender, Receiver) {

    var PostCardMessage = " Happy Spring!!! Love, ";

    function PreparePostCard() {
        return "Dear " + Receiver + PostCardMessage + Sender;
    }

    return PreparePostCard();
}
showPostCard("Granny", "Olivia");
21
enb081

Voici l'explication illustrée : Comment fonctionnent les fermetures de JavaScript en arrière-plan .

L'article explique comment les objets scope (ou LexicalEnvironments) sont alloués et utilisés de manière intuitive. Comme pour ce script simple:

"use strict";

var foo = 1;
var bar = 2;

function myFunc() {
  //-- Define local-to-function variables
  var a = 1;
  var b = 2;
  var foo = 3;
}

//-- And then, call it:
myFunc();

Lors de l'exécution du code de niveau supérieur, nous avons la disposition suivante des objets de portée:

Enter image description here

Et quand myFunc() est appelée, nous avons la chaîne de champs suivante:

Enter image description here

Comprendre la manière dont les objets de la portée sont créés, utilisés et supprimés est essentiel pour avoir une vue d’ensemble et comprendre comment les fermetures fonctionnent sous le capot.

Voir l'article susmentionné pour tous les détails.

21
Dmitry Frank

Image de la version pour cette réponse: [Résolu]

Oubliez la portée de tout et rappelez-vous: quand une variable a besoin de quelque part, javascript ne la détruira pas. La variable pointe toujours sur la valeur la plus récente.

Exemple 1:

enter image description here

Exemple 2:

enter image description here

Exemple 3: enter image description here

20
christian Nguyen

(Je ne prends pas en compte la chose de 6 ans.)

Dans un langage comme JavaScript, où vous pouvez passer des fonctions en tant que paramètres à d'autres fonctions (langages où les fonctions sont des citoyens de première classe ), vous vous trouverez souvent en train de faire quelque chose comme:

var name = 'Rafael';

var sayName = function() {
  console.log(name);
};

Vous voyez, sayName n'a pas la définition de la variable name, mais il utilise la valeur de name définie en dehors de sayName (dans une portée parente) ).

Supposons que vous transmettez sayName en tant que paramètre à une autre fonction, qui appellera sayName en tant que rappel:

functionThatTakesACallback(sayName);

Notez que:

  1. sayName sera appelé de l'intérieur de functionThatTakesACallback (supposons que, puisque je n'ai pas implémenté functionThatTakesACallback dans cet exemple).
  2. Lorsque vous appelez sayName, il enregistre la valeur de la variable name.
  3. functionThatTakesACallback ne définit pas de variable name (eh bien, c'est possible, mais cela n'a pas d'importance, supposons que ce ne soit pas le cas).

Donc, nous avons appelé sayName dans functionThatTakesACallback et nous référons à une variable name qui n'est pas définie dans functionThatTakesACallback.

Qu'est-ce qui se passe ensuite? Un ReferenceError: name is not defined?

Non! La valeur de name est capturée dans un fermeture. Vous pouvez considérer cette fermeture comme contexte associé à une fonction, qui contient les valeurs disponibles là où cette fonction a été définie.

Donc: Bien que name ne soit pas dans la portée où la fonction sayName sera appelée (dans functionThatTakesACallback), sayName peut accéder à la valeur pour name qui est capturé dans la fermeture associée à sayName.

-

Du livre JavaScript éloquent :

Un bon modèle mental consiste à penser que les valeurs de fonction contiennent à la fois le code dans leur corps et l'environnement dans lequel elles ont été créées. Lorsqu'il est appelé, le corps de la fonction voit son environnement d'origine, pas l'environnement dans lequel l'appel est effectué.

20
Rafael Eyng

Cette réponse est un résumé de cette vidéo youtube Javascript Closures . Donc, tous les crédits de cette vidéo.

Les fermetures ne sont que des fonctions avec état qui conservent les états de leurs variables privées.

Normalement, lorsque vous appelez une fonction, comme indiqué dans la figure ci-dessous. Les variables sont créées sur une pile (exécutée RAM mémoire) utilisée puis désallouée.

enter image description here

Mais à présent, il existe des situations dans lesquelles nous souhaitons conserver cet état de la fonction utilisé par les fermetures Javascript. Une fermeture est une fonction à l'intérieur d'une fonction avec un appel de retour comme indiqué dans le code ci-dessous.

enter image description here

Ainsi, le code de fermeture de la fonction de compteur ci-dessus ressemble à quelque chose comme indiqué ci-dessous.It une fonction à l'intérieur d'une fonction avec une instruction return.

function Counter() {
           var counter = 0;

           var Increment = function () {
               counter++;
               alert(counter);
           }
           return {
               Increment
           }
       }

Alors maintenant, si vous passez un appel, le compteur incrémentera, autrement dit, l'appel de fonction conserve des états.

var x = Counter(); // get the reference of the closure
x.Increment(); // Displays 1
x.Increment(); // Display 2 ( Maintains the private variables)

Mais maintenant, la plus grande question est l'utilisation d'une telle fonction stateful. Les fonctions avec état sont des blocs de construction permettant de mettre en œuvre le concept OOP tel que l'abstraction, l'encapsulation et la création de modules autonomes.

Ainsi, tout ce que vous voulez encapsulé peut être considéré comme privé et les éléments à exposer au public doivent être consignés dans une déclaration de retour. De plus, ces composants sont des objets isolés autonomes afin de ne pas polluer les variables globales.

Un objet qui suit OOP principes est autonome, suit l’abstraction, suit l’encapsulation, etc. Avec des fermetures en Javascript, cela est difficile à mettre en œuvre.

enter image description here

18
Shivprasad Koirala

D'une personnelle blog post :

Par défaut, JavaScript connaît deux types d'étendues: globale et locale.

var a = 1;

function b(x) {
    var c = 2;
    return x * c;
}

Dans le code ci-dessus, la variable a et la fonction b sont disponibles à partir de n’importe où dans le code (c’est-à-dire globalement). La variable c n'est disponible que dans l'étendue de la fonction b (c'est-à-dire locale). La plupart des développeurs de logiciels ne seront pas satisfaits de ce manque de flexibilité, en particulier dans les gros programmes.

Les fermetures de JavaScript aident à résoudre ce problème en liant une fonction à un contexte:

function a(x) {
    return function b(y) {
        return x + y;
    }
}

Ici, la fonction a renvoie une fonction appelée b. Puisque b est défini dans a, il a automatiquement accès à tout ce qui est défini dans a, c'est-à-dire x dans cet exemple. C'est pourquoi b peut retourner x + y sans déclarer x.

var c = a(3);

La variable c est affectée au résultat d'un appel à un paramètre 3, à savoir une instance de la fonction bx = 3. En d'autres termes, c est maintenant une fonction équivalente à:

var c = function b(y) {
    return 3 + y;
}

La fonction b rappelle que x = 3 dans son contexte. Donc:

var d = c(4);

attribuera la valeur 3 + 4 à d, soit 7.

Remarque : Si quelqu'un modifie la valeur de x (dites x = 22) après l'instance de la fonction b a été créé, cela sera également reflété dans b. Par conséquent, un appel ultérieur à c (4) renverrait 22 + 4, soit 26.

Les fermetures peuvent également être utilisées pour limiter la portée des variables et méthodes déclarées globalement:

(function () {
    var f = "Some message";
    alert(f);
})();

Ce qui précède est une fermeture où la fonction n'a pas de nom, pas d'argument et est appelée immédiatement. Le code en surbrillance, qui déclare une variable globale f, limite les portées de f à la fermeture.

Maintenant, il existe une mise en garde JavaScript commune dans laquelle les fermetures peuvent aider:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(x) { return x + i ; }
}

D'après ce qui précède, la plupart des gens supposeraient que le tableau a serait initialisé comme suit:

a[0] = function (x) { return x + 0 ; }
a[1] = function (x) { return x + 1 ; }
a[2] = function (x) { return x + 2 ; }

En réalité, voici comment a est initialisé, puisque la dernière valeur de i dans le contexte est 2:

a[0] = function (x) { return x + 2 ; }
a[1] = function (x) { return x + 2 ; }
a[2] = function (x) { return x + 2 ; }

La solution est:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(tmp) {
        return function (x) { return x + tmp ; }
    } (i);
}

L'argument/variable tmp contient une copie locale de la valeur changeante de i lors de la création d'instances de fonctions.

18
Jérôme Verstrynge

L'exemple suivant est une illustration simple d'une fermeture JavaScript. C'est la fonction de fermeture, qui retourne une fonction, avec accès à sa variable locale x,

function outer(x){
     return function inner(y){
         return x+y;
     }
}

Invoquez la fonction comme ceci:

var add10 = outer(10);
add10(20); // The result will be 30
add10(40); // The result will be 50

var add20 = outer(20);
add20(20); // The result will be 40
add20(40); // The result will be 60
18
Mohammed Safeer

Une fermeture est une chose que beaucoup de développeurs JavaScript utilisent tout le temps, mais nous la prenons pour acquise. Comment ça marche n'est pas si compliqué. Comprendre comment l’utiliser à bon escient est complexe .

Dans sa définition la plus simple (comme d'autres réponses l'ont souligné), une fermeture est fondamentalement une fonction définie dans une autre fonction. Et cette fonction interne a accès aux variables définies dans la portée de la fonction externe. La pratique la plus courante que vous observerez lors de l'utilisation de fermetures consiste à définir des variables et des fonctions dans la portée globale et à avoir accès à ces variables dans la portée de fonction de cette fonction.

var x = 1;
function myFN() {
  alert(x); //1, as opposed to undefined.
}
// Or
function a() {
   var x = 1;
   function b() {
       alert(x); //1, as opposed to undefined.
   }
   b();
}

Et alors?

Une fermeture n’est pas si spéciale pour un utilisateur JavaScript tant que vous n’avez pas pensé à ce que serait la vie sans eux. Dans d'autres langues, les variables utilisées dans une fonction sont nettoyées lorsque cette fonction est retournée. Dans ce qui précède, x aurait été un "pointeur nul", et vous auriez besoin d'établir un getter et un setter et de commencer à passer des références. Ne ressemble pas à JavaScript, n'est-ce pas? Merci la fermeture puissante.

Pourquoi devrais-je m'en soucier?

Vous n'avez pas vraiment besoin d'être au courant des fermetures pour les utiliser. Mais, comme d'autres l'ont également souligné, ils peuvent être à effet de levier pour créer de fausses variables privées. Jusqu'à ce que vous ayez besoin de variables privées, utilisez-les comme vous l'avez toujours fait.

17
Harry Robbins

J'ai trouvé très clair le chapitre 8, section 6, "Fermetures", de JavaScript: Le Guide définitif par David Flanagan, 6ème édition, O'Reilly, 2011. Je vais essayer de paraphraser.

  1. Lorsqu'une fonction est appelée, un nouvel objet est créé pour contenir les variables locales pour cet appel.

  2. La portée d'une fonction dépend de son emplacement de déclaration et non de son emplacement d'exécution.

Supposons maintenant une fonction interne déclarée dans une fonction externe et faisant référence à des variables de cette fonction externe. Supposons en outre que la fonction externe retourne la fonction interne en tant que fonction. Maintenant, il y a une référence externe à toutes les valeurs qui étaient dans la portée de la fonction interne (qui, selon nos hypothèses, inclut les valeurs de la fonction externe).

JavaScript préservera ces valeurs, car elles sont restées dans la portée de l'exécution en cours car elles ont été transmises à la fonction externe terminée. Toutes les fonctions sont des fermetures, mais les fermetures d'intérêt sont les fonctions internes qui, dans notre scénario supposé, préservent les valeurs des fonctions externes dans leur "Enclosure" (j'espère que j'utilise correctement le langage ici) lorsqu'elles (les fonctions internes) sont renvoyées. des fonctions extérieures. Je sais que cela ne répond pas à l'exigence de six ans, mais j'espère que cela restera utile.

17
Jim

Une fonction est exécutée dans la portée de l'objet/de la fonction dans lequel elle est définie. Ladite fonction peut accéder aux variables définies dans l'objet/la fonction où elle a été définie pendant son exécution.

Et prenez-le littéralement ... comme le code est écrit: P

16
moha297

Si vous voulez l'expliquer à un enfant de six ans, vous devez trouver quelque chose de beaucoup plus simple et sans code.

Dites simplement à l'enfant qu'il est "ouvert", ce qui signifie qu'il est capable d'entretenir des relations avec d'autres, ses amis. À un moment donné, il a déterminé des amis (on peut connaître le nom de ses amis), c'est une fermeture. Si vous prenez une photo de lui et de ses amis, il est "fermé" par rapport à sa capacité d'amitié. Mais en général, il est "ouvert". Pendant toute sa vie, il aura beaucoup d'amis différents. L'un de ces ensembles est une fermeture.

15

Je suis sûr que Einstein ne l'a pas dit avec une attente directe de notre part de choisir n'importe quelle idée de remue-méninges ésotérique et de survoler des enfants de six ans avec des tentatives vaines pour obtenir ces "fous" (et C'est encore pire pour eux-ennuyeux) des choses à leurs esprits enfantins :) Si j'avais six ans, je ne voudrais pas avoir de tels parents ou ne ferait pas l'amitié avec des philanthropes ennuyeux, désolé :)

Quoi qu'il en soit, pour les bébés, la fermeture est simplement un câlin , je suppose, quelle que soit la façon dont vous essayez d’expliquer :) Et lorsque vous serrez dans vos bras un de vos amis, vous partagez en quelque sorte tout ce que vous avez en ce moment. C'est un rite de passage, une fois que vous avez embrassé quelqu'un, vous lui montrez sa confiance et sa volonté de la laisser faire avec vous beaucoup de choses que vous ne permettez pas et que vous cacheriez des autres. C'est un acte d'amitié :).

Je ne sais vraiment pas comment l'expliquer aux bébés de 5 à 6 ans. Je ne pense pas non plus qu'ils apprécieront des extraits de code JavaScript tels que:

function Baby(){
    this.iTrustYou = true;
}

Baby.prototype.hug = function (baby) {
    var smiles = 0;

    if (baby.iTrustYou) {
        return function() {
            smiles++;
            alert(smiles);
        };
    }
};

var
   arman = new Baby("Arman"),
   morgan = new Baby("Morgana");

var hug = arman.hug(morgan);
hug();
hug();

Pour les enfants seulement:

La fermeture est un câlin

Bug est voler

KISS est très doux! :)

15
Arman McHitarian

Les fermetures sont un moyen par lequel les fonctions internes peuvent se référer aux variables présentes dans leur fonction englobante externe après que leurs fonctions parent se sont déjà terminées.

// A function that generates a new function for adding numbers.
function addGenerator( num ) {
    // Return a simple function for adding two numbers
    // with the first number borrowed from the generator
    return function( toAdd ) {
        return num + toAdd
    };
}

// addFive now contains a function that takes one argument,
// adds five to it, and returns the resulting number.
var addFive = addGenerator( 5 );
// We can see here that the result of the addFive function is 9,
// when passed an argument of 4.
alert( addFive( 4 ) == 9 );
14
ketan

Si vous le comprenez bien, vous pouvez l'expliquer simplement. Et la manière la plus simple consiste à l'abstraire du contexte. Code mis à part, même programmation mise à part. Un exemple de métaphore le fera mieux.

Imaginons qu'une fonction soit une pièce dont les murs sont en verre, mais ce sont des verres spéciaux, comme ceux d'une salle d'interrogatoire. De l'extérieur, ils sont opaques, de l'intérieur, ils sont transparents. Il peut s'agir de pièces situées à l'intérieur d'autres pièces et le seul moyen de contact est un téléphone.

Si vous appelez de l'extérieur, vous ne savez pas ce qu'il y a dedans, mais vous savez que les personnes à l'intérieur s'acquitteront d'une tâche si vous leur donnez certaines informations. Ils peuvent voir à l'extérieur, ils peuvent donc vous demander des choses qui se trouvent à l'extérieur et les modifier, mais vous ne pouvez pas changer ce qui est à l'intérieur de l'extérieur, vous ne voyez même pas (ne savez pas) ce que c'est à l'intérieur. Les personnes à l'intérieur de la pièce que vous appelez voient ce qu'il y a à l'extérieur, mais pas ce qu'elles sont à l'intérieur des pièces de cette pièce. Elles interagissent avec elles comme vous le faites de l'extérieur. Les personnes à l'intérieur des pièces les plus intérieures peuvent voir beaucoup de choses, mais les personnes des pièces les plus extérieures ne connaissent même pas l'existence des pièces les plus à l'intérieur.

Pour chaque appel dans une pièce intérieure, les personnes dans cette pièce gardent une trace des informations relatives à cet appel spécifique, et elles sont si douées pour le faire qu'elles ne confondent jamais un appel avec un autre.

Les pièces sont des fonctions, la visibilité est la portée, les personnes effectuant des tâches sont des instructions, les objets sont des objets, les appels téléphoniques sont des appels de fonction, les informations sur les appels téléphoniques sont des arguments, les enregistrements d'appels sont des instances de portée, la pièce la plus externe est l'objet global.

13
Juan Garcia

Une fermeture est une fonction ayant accès à la portée parent, même après la fermeture de la fonction parent.

var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();

add();
add();
add();
// The counter is now 3

Exemple expliqué:

  • La variable add reçoit la valeur de retour d'une fonction à invocation automatique.
  • La fonction d'invocation automatique ne s'exécute qu'une fois. Il met le compteur à zéro (0) et renvoie une expression de fonction.
  • De cette façon, ajouter devient une fonction. La partie "merveilleuse" est qu'il peut accéder au compteur dans la portée parente.
  • Cela s'appelle une fermeture JavaScript. Cela permet à une fonction d'avoir des variables "privées".
  • Le compteur est protégé par la portée de la fonction anonyme et ne peut être modifié qu'à l'aide de la fonction add.

Source

13
Premraj

Imaginez qu'il y ait un très grand parc dans votre ville où vous voyez un magicien appelé M. Coder commencer des parties de baseball dans différents coins du parc en utilisant sa baguette magique, appelée JavaScript.

Naturellement, chaque jeu de baseball a exactement les mêmes règles et chaque jeu a son propre tableau de score.

Naturellement, les scores d'un match de baseball sont complètement séparés des autres jeux.

Une fermeture est la façon spéciale dont Mr.Coder garde la notation de tous ses jeux de baseball magiques séparés.

13
b_dev

Pinocchio: Fermetures en 1883 (plus d'un siècle avant JavaScript)

Je pense que cela peut être mieux expliqué à un enfant de 6 ans avec une belle aventure ... La partie du Adventures of Pinocchio où Pinocchio est avalé par un chien de mer surdimensionné ...

var tellStoryOfPinocchio = function(original) {

  // Prepare for exciting things to happen
  var pinocchioFindsMisterGeppetto;
  var happyEnding;

  // The story starts where Pinocchio searches for his 'father'
  var pinocchio = {
    name: 'Pinocchio',
    location: 'in the sea',
    noseLength: 2
  };

  // Is it a dog... is it a fish...
  // The dogfish appears, however there is no such concept as the belly
  // of the monster, there is just a monster...
  var terribleDogfish = {
    swallowWhole: function(snack) {
      // The swallowing of Pinocchio introduces a new environment (for the
      // things happening inside it)...
      // The BELLY closure... with all of its guts and attributes
      var mysteriousLightLocation = 'at Gepetto\'s ship';

      // Yes: in my version of the story the monsters mouth is directly
      // connected to its belly... This might explain the low ratings
      // I had for biology...
      var mouthLocation = 'in the monsters mouth and then outside';

      var puppet = snack;


      puppet.location = 'inside the belly';
      alert(snack.name + ' is swallowed by the terrible dogfish...');

      // Being inside the belly, Pinocchio can now experience new adventures inside it
      pinocchioFindsMisterGeppetto = function() {
        // The event of Pinocchio finding Mister Geppetto happens inside the
        // belly and so it makes sence that it refers to the things inside
        // the belly (closure) like the mysterious light and of course the
        // hero Pinocchio himself!
        alert(puppet.name + ' sees a mysterious light (also in the belly of the dogfish) in the distance and swims to it to find Mister Geppetto! He survived on ship supplies for two years after being swallowed himself. ');
        puppet.location = mysteriousLightLocation;

        alert(puppet.name + ' tells Mister Geppetto he missed him every single day! ');
        puppet.noseLength++;
      }

      happyEnding = function() {
        // The escape of Pinocchio and Mister Geppetto happens inside the belly:
        // it refers to Pinocchio and the mouth of the beast.
        alert('After finding Mister Gepetto, ' + puppet.name + ' and Mister Gepetto travel to the mouth of the monster.');
        alert('The monster sleeps with its mouth open above the surface of the water. They escape through its mouth. ');
        puppet.location = mouthLocation;
        if (original) {
          alert(puppet.name + ' is eventually hanged for his innumerable faults. ');
        } else {
          alert(puppet.name + ' is eventually turned into a real boy and they all lived happily ever after...');
        }
      }
    }
  }

  alert('Once upon a time...');
  alert('Fast forward to the moment that Pinocchio is searching for his \'father\'...');
  alert('Pinocchio is ' + pinocchio.location + '.');
  terribleDogfish.swallowWhole(pinocchio);
  alert('Pinocchio is ' + pinocchio.location + '.');
  pinocchioFindsMisterGeppetto();
  alert('Pinocchio is ' + pinocchio.location + '.');
  happyEnding();
  alert('Pinocchio is ' + pinocchio.location + '.');

  if (pinocchio.noseLength > 2)
    console.log('Hmmm... apparently a little white lie was told. ');
}

tellStoryOfPinocchio(false);

 
13
Ron Deijkers

Une fois qu'une fonction est appelée, elle sort de la portée. Si cette fonction contient quelque chose comme une fonction de rappel, cette fonction de rappel est toujours dans la portée. Si la fonction de rappel fait référence à une variable locale dans l'environnement immédiat de la fonction parent, vous vous attendez naturellement à ce que cette variable soit inaccessible à la fonction de rappel et renvoie undefined.

Les fermetures garantissent que toute propriété référencée par la fonction de rappel est disponible pour être utilisée par cette fonction, même lorsque sa fonction parent a peut-être dépassé la portée.

13
goonerify

Vous devriez peut-être envisager une structure orientée objet plutôt que des fonctions internes. Par exemple:

var calculate = {
    number: 0,
    init: function (num) {
        this.number = num;
    },
    add: function (val) {
        this.number += val;
    },
    rem: function (val) {
        this.number -= val;
    }
};

Et lisez le résultat de la variable calcule.numéro, qui a de toute façon besoin de "retourner".

12
Psy Chip

Une fermeture est un bloc de code qui répond à trois critères:

  • Il peut être transmis comme une valeur et

  • exécuté sur demande par quiconque a cette valeur, à quel moment

  • il peut faire référence à des variables du contexte dans lequel il a été créé (c'est-à-dire qu'il est fermé en ce qui concerne l'accès aux variables, au sens mathématique du mot "fermé").

(Le mot "clôture" a en fait une signification imprécise, et certaines personnes ne pensent pas que le critère n ° 1 fait partie de la définition. Je pense que oui.)

Les fermetures sont un pilier des langages fonctionnels, mais elles sont également présentes dans de nombreux autres langages (par exemple, les classes internes anonymes de Java). Vous pouvez faire des trucs sympas avec eux: ils permettent une exécution différée et quelques astuces de style élégantes.

Par: Paul Cantrell, @ http://innig.net/software/Ruby/closures-in-Ruby

12
Magne

Les fermetures sont simples

Vous ne devriez probablement pas parler des fermetures à un enfant de six ans, mais si vous le faites, vous pourriez dire que cette fermeture donne la possibilité d'accéder à une variable déclarée dans une autre étendue de fonction.

enter image description here

function getA() {
  var a = [];

  // this action happens later,
  // after the function returned
  // the `a` value
  setTimeout(function() {
    a.splice(0, 0, 1, 2, 3, 4, 5);
  });

  return a;
}

var a = getA();
out('What is `a` length?');
out('`a` length is ' + a.length);

setTimeout(function() {
  out('No wait...');
  out('`a` length is ' + a.length);
  out('OK :|')
});
<pre id="output"></pre>

<script>
  function out(k) {
    document.getElementById('output').innerHTML += '> ' + k + '\n';
  }
</script>
12
Eugene Tiurin

Une fermeture est créée lorsque la fonction interne est en quelque sorte mise à la disposition d'une étendue en dehors de la fonction externe.

Exemple:

var outer = function(params){ //Outer function defines a variable called params
    var inner = function(){ // Inner function has access to the params variable of the outer function
        return params;
    }
    return inner; //Return inner function exposing it to outer scope
},
myFunc = outer("myParams");
myFunc(); //Returns "myParams"
11

Les fermetures sont une fonctionnalité quelque peu avancée et souvent mal comprise du langage JavaScript. En termes simples, les fermetures sont des objets contenant une fonction et une référence à l'environnement dans lequel la fonction a été créée. Cependant, pour bien comprendre les fermetures, il faut d'abord comprendre deux autres caractéristiques du langage JavaScript: les fonctions de première classe et les fonctions internes.

Fonctions de première classe

Dans les langages de programmation, les fonctions sont considérées comme des citoyens de premier ordre si elles peuvent être manipulées comme tout autre type de données. Par exemple, des fonctions de première classe peuvent être construites au moment de l'exécution et attribuées à des variables. Ils peuvent également être passés et retournés par d'autres fonctions. En plus de répondre aux critères mentionnés précédemment, les fonctions JavaScript ont également leurs propres propriétés et méthodes. L'exemple suivant montre certaines des capacités des fonctions de première classe. Dans l'exemple, deux fonctions sont créées et attribuées aux variables "foo" et "bar". La fonction stockée dans "foo" affiche une boîte de dialogue, tandis que "bar" renvoie simplement l’argument qui lui est transmis. La dernière ligne de l'exemple fait plusieurs choses. Premièrement, la fonction stockée dans "bar" est appelée avec "foo" comme argument. "Bar" renvoie ensuite la référence à la fonction "foo". Enfin, la référence "foo" renvoyée est appelée, ce qui provoque l'affichage de "Hello World!".

var foo = function() {
  alert("Hello World!");
};

var bar = function(arg) {
  return arg;
};

bar(foo)();

Fonctions internes

Les fonctions internes, également appelées fonctions imbriquées, sont des fonctions définies à l'intérieur d'une autre fonction (appelée fonction externe). Chaque fois que la fonction externe est appelée, une instance de la fonction interne est créée. L'exemple suivant montre comment les fonctions internes sont utilisées. Dans ce cas, add () est la fonction externe. À l'intérieur de add (), la fonction interne doAdd () est définie et appelée.

function add(value1, value2) {
  function doAdd(operand1, operand2) {
    return operand1 + operand2;
  }

  return doAdd(value1, value2);
}

var foo = add(1, 2);
// foo equals 3

Une caractéristique importante des fonctions internes est qu’elles ont un accès implicite à la portée de la fonction externe. Cela signifie que la fonction interne peut utiliser les variables, arguments, etc. de la fonction externe. Dans l'exemple précédent, les arguments "valeur1" et "valeur2" de add () ont été passés à doAdd () sous la forme "- opérande1 ”et arguments“ opérande2 ”. Cependant, cela est inutile car doAdd () a un accès direct à “valeur1” et à “valeur2”. L'exemple précédent a été récrit ci-dessous pour montrer comment doAdd () peut utiliser "valeur1" et "valeur2".

function add(value1, value2) {
  function doAdd() {
    return value1 + value2;
  }

  return doAdd();
}

var foo = add(1, 2);
// foo equals 3

Création de fermetures

Une fermeture est créée lorsqu'une fonction interne est rendue accessible depuis l'extérieur de la fonction qui l'a créée. Cela se produit généralement lorsqu'une fonction externe retourne une fonction interne. Lorsque cela se produit, la fonction interne conserve une référence à l'environnement dans lequel elle a été créée. Cela signifie qu'il se souvient de toutes les variables (et de leurs valeurs) qui étaient dans la portée à ce moment-là. L'exemple suivant montre comment une fermeture est créée et utilisée.

function add(value1) {
  return function doAdd(value2) {
    return value1 + value2;
  };
}

var increment = add(1);
var foo = increment(2);
// foo equals 3

Il y a un certain nombre de choses à noter à propos de cet exemple.

La fonction add () renvoie sa fonction interne doAdd (). En renvoyant une référence à une fonction interne, une fermeture est créée. "Valeur1" est une variable locale de add () et une variable non locale de doAdd (). Les variables non locales font référence à des variables qui ne sont ni dans la portée locale ni dans la portée globale. “Valeur2” est une variable locale de doAdd (). Lorsque add (1) est appelé, une fermeture est créée et stockée par incrément. Dans l’environnement de référence de la fermeture, "valeur1" est lié à la valeur un. Les variables liées sont également dites fermées. C'est de là que vient la fermeture du nom. Lorsque increment (2) est appelé, la fermeture est entrée. Cela signifie que doAdd () est appelé, avec la variable "valeur1" contenant la valeur un. On peut essentiellement penser que la fermeture crée la fonction suivante.

function increment(value2) {
  return 1 + value2;
}

Quand utiliser les fermetures

Les fermetures peuvent être utilisées pour accomplir beaucoup de choses. Ils sont très utiles pour des tâches telles que la configuration de fonctions de rappel avec des paramètres. Cette section couvre deux scénarios dans lesquels les fermetures peuvent vous simplifier la vie.

Travailler avec des minuteries

Les fermetures sont utiles lorsqu'elles sont utilisées conjointement avec les fonctions setTimeout () et setInterval (). Pour être plus précis, les fermetures vous permettent de passer des arguments aux fonctions de rappel de setTimeout () et setInterval (). Par exemple, le code suivant imprime la chaîne "un message" une fois par seconde en appelant showMessage ().

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      window.setInterval(showMessage, 1000, "some message<br />");
    });

    function showMessage(message) {
      document.getElementById("message").innerHTML += message;
    }
  </script>
</head>
<body>
  <span id="message"></span>
</body>
</html>

Malheureusement, Internet Explorer ne prend pas en charge le passage d'arguments de rappel via setInterval (). Au lieu d’afficher "un message", Internet Explorer affiche "non défini" (puisqu’aucune valeur n’est transmise à showMessage ()). Pour contourner ce problème, vous pouvez créer une fermeture qui lie l'argument "message" à la valeur souhaitée. La fermeture peut ensuite être utilisée comme fonction de rappel pour setInterval (). Pour illustrer ce concept, le code JavaScript de l'exemple précédent a été récrit ci-dessous pour utiliser une fermeture.

window.addEventListener("load", function() {
  var showMessage = getClosure("some message<br />");

  window.setInterval(showMessage, 1000);
});

function getClosure(message) {
  function showMessage() {
    document.getElementById("message").innerHTML += message;
  }

  return showMessage;
}

Emulation de données privées

De nombreux langages orientés objet supportent le concept de données de membre privé. Cependant, JavaScript n'est pas un langage purement orienté objet et ne prend pas en charge les données privées. Mais, il est possible d'émuler des données privées à l'aide de fermetures. Rappelez-vous qu'une fermeture contient une référence à l'environnement dans lequel elle a été créée, ce qui est maintenant hors de portée. Comme les variables de l'environnement de référencement ne sont accessibles qu'à partir de la fonction de fermeture, il s'agit essentiellement de données privées.

L'exemple suivant montre un constructeur pour une classe Person simple. Lorsque chaque personne est créée, un nom lui est attribué via l'argument “nom”. En interne, la Personne enregistre son nom dans la variable “_ name”. Suivant de bonnes pratiques de programmation orientée objet, la méthode getName () est également fournie pour extraire le nom.

function Person(name) {
  this._name = name;

  this.getName = function() {
    return this._name;
  };
}

Il y a toujours un problème majeur avec la classe Personne. Comme JavaScript ne prend pas en charge les données privées, rien n'empêche quelqu'un d'autre de modifier le nom. Par exemple, le code suivant crée une personne nommée Colin, puis change son nom en Tom.

var person = new Person("Colin");

person._name = "Tom";
// person.getName() now returns "Tom"

Personnellement, je n’aimerais pas que tout le monde puisse venir et changer légalement mon nom. Afin d'empêcher que cela ne se produise, une fermeture peut être utilisée pour rendre la variable "_name" privée. Le constructeur Personne a été réécrit ci-dessous à l'aide d'une fermeture. Notez que "_name" est maintenant une variable locale du constructeur Person au lieu d’une propriété d’objet. Une fermeture est formée parce que la fonction externe, Person () expose une fonction interne en créant la méthode publique getName ().

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Désormais, lorsque getName () est appelé, il est garanti que la valeur renvoyée au constructeur a été renvoyée. Il est toujours possible pour quelqu'un d'ajouter une nouvelle propriété "_name" à l'objet, mais le fonctionnement interne de l'objet ne sera pas affecté tant qu'il fait référence à la variable liée par la fermeture. Le code suivant montre que la variable “_name” est bien privée.

var person = new Person("Colin");

person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"

Quand ne pas utiliser les fermetures

Il est important de comprendre comment fonctionnent les fermetures et quand les utiliser. Il est également important de comprendre quand ils ne sont pas le bon outil pour le travail à accomplir. L'utilisation abusive de fermetures peut entraîner une exécution lente des scripts et une consommation de mémoire inutile. Et comme les fermetures sont si simples à créer, il est possible d’en abuser sans même le savoir. Cette section couvre plusieurs scénarios dans lesquels les fermetures doivent être utilisées avec prudence.

Dans les boucles

La création de fermetures dans des boucles peut avoir des résultats trompeurs. Un exemple de ceci est montré ci-dessous. Dans cet exemple, trois boutons sont créés. Lorsque l'utilisateur clique sur "bouton1", une alerte indiquant "Bouton cliqué 1" doit s'afficher. Des messages similaires doivent être affichés pour “button2” et “button3”. Cependant, lorsque ce code est exécuté, tous les boutons affichent "Bouton cliqué 4". En effet, au moment où un des boutons est cliqué, l'exécution de la boucle est terminée et la variable de boucle a atteint sa valeur finale de quatre.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      for (var i = 1; i < 4; i++) {
        var button = document.getElementById("button" + i);

        button.addEventListener("click", function() {
          alert("Clicked button " + i);
        });
      }
    });
  </script>
</head>
<body>
  <input type="button" id="button1" value="One" />
  <input type="button" id="button2" value="Two" />
  <input type="button" id="button3" value="Three" />
</body>
</html>

Pour résoudre ce problème, la fermeture doit être découplée de la variable de boucle réelle. Cela peut être fait en appelant une nouvelle fonction, qui crée à son tour un nouvel environnement de référencement. L'exemple suivant montre comment cela est fait. La variable de boucle est transmise à la fonction getHandler (). getHandler () renvoie ensuite une fermeture indépendante de la boucle "for" d'origine.

function getHandler(i) {
  return function handler() {
    alert("Clicked button " + i);
  };
}
window.addEventListener("load", function() {
  for (var i = 1; i < 4; i++) {
    var button = document.getElementById("button" + i);
    button.addEventListener("click", getHandler(i));
  }
});

Utilisation inutile dans les constructeurs

Les fonctions de constructeur sont une autre source commune d’utilisation abusive de la fermeture. Nous avons vu comment les fermetures peuvent être utilisées pour émuler des données privées. Cependant, il est excessif d’implémenter des méthodes en tant que fermetures s’ils n’accèdent pas réellement aux données privées. L’exemple suivant reprend la classe Person, mais cette fois-ci ajoute une méthode sayHello () qui n’utilise pas les données privées.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.sayHello = function() {
    alert("Hello!");
  };
}

Chaque fois qu'une personne est instanciée, le temps passé à créer la méthode sayHello () est utilisé. Si plusieurs objets Personne sont créés, cela devient une perte de temps. Une meilleure approche consisterait à ajouter sayHello () au prototype Person. En ajoutant au prototype, tous les objets Personne peuvent partager la même méthode. Cela fait gagner du temps au constructeur en évitant de créer une fermeture pour chaque instance. L'exemple précédent est récrit ci-dessous avec la fermeture superflue déplacée dans le prototype.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Person.prototype.sayHello = function() {
  alert("Hello!");
};

Ce qu'il faut retenir

  • Les fermetures contiennent une fonction et une référence à l'environnement dans lequel la fonction a été créée.
  • Une fermeture est formée lorsqu'une fonction externe expose une fonction interne. Les fermetures peuvent être utilisées pour passer facilement des paramètres aux fonctions de rappel.
  • Les données privées peuvent être émulées à l'aide de fermetures. Ceci est courant dans la programmation orientée objet et la conception d'espaces de noms.
  • Les fermetures ne doivent pas être trop utilisées dans les constructeurs. L'ajout au prototype est une meilleure idée.

Lien

11
Durgesh Pandey

Considérant que la question consiste à l'expliquer simplement comme s'il s'agissait d'un âgé de 6 ans, ma réponse serait:

"Lorsque vous déclarez une fonction en JavaScript, elle a toujours accès à toutes les variables et fonctions disponibles dans la ligne précédant cette déclaration. La fonction et toutes les variables externes et fonctions auxquelles elle a accès appeler une fermeture. "

10
Raul Martins

Les fonctions ne contenant aucune variable libre sont appelées des fonctions pures.

Les fonctions contenant une ou plusieurs variables libres sont appelées fermetures.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo
  // foo is free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

10
soundyogi

MDN explique le mieux, je pense:

Les fermetures sont des fonctions qui font référence à des variables indépendantes (libres). En d'autres termes, la fonction définie dans la fermeture "se souvient" de l'environnement dans lequel elle a été créée.

Une fermeture a toujours une fonction externe et une fonction interne. La fonction interne est l'endroit où tout le travail se produit, et la fonction externe est simplement l'environnement qui préserve l'étendue où la fonction interne a été créée. De cette manière, la fonction interne d'une fermeture "se souvient" de l'environnement/de la portée dans laquelle elle a été créée. L'exemple le plus classique est une fonction de compteur:

var closure = function() {
  var count = 0;
  return function() {
    count++;
    console.log(count);
  };
};

var counter = closure();

counter() // returns 1
counter() // returns 2
counter() // returns 3

Dans le code ci-dessus, count est préservé par la fonction externe (fonction d'environnement), de sorte que chaque fois que vous appelez counter(), la fonction interne (fonction de travail) puisse l'incrémenter.

9
Brandon Kent

J'aime la définition de fermeture de Kyle Simpson:

La fermeture se produit lorsqu'une fonction est capable de se souvenir et d'accéder à sa portée lexicale même lorsque cette fonction s'exécute en dehors de sa portée lexicale.

La portée lexicale est le moment où une portée interne peut accéder à sa portée externe.

Voici un exemple modifié qu'il fournit dans sa série de livres intitulée "Vous ne savez pas JS: Scopes & Closures".

function foo() {
  var a = 2;

  function bar() {
    console.log( a );
  }
  return bar;
}

function test() {
  var bz = foo();
  bz();
}

// prints 2. Here function bar referred by var bz is outside 
// its lexical scope but it can still access it
test(); 
9
TastyCode

Voici comment un débutant se met la tête dans la tête Des fermetures, comme une fonction, sont enveloppées dans un corps de fonctions également appelé Fermetures.

Définition tirée du livre Speaking JavaScript "Une fermeture est une fonction et la connexion à l'étendue dans laquelle la fonction a été créée" - Dr.Axel Rauschmayer

Alors à quoi cela pourrait-il ressembler? Voici un exemple

function newCounter() {
  var counter = 0;
   return function increment() {
    counter += 1;
   }
}

var counter1 = newCounter();
var counter2 = newCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3

newCounter se ferme plus tard incrémentation , counter peut être référencé et accédé par incrémenter .

counter1 et counter2 gardent une trace de leur propre valeur.

Simple mais, espérons-le, une perspective claire de ce qu’est une clôture autour de toutes ces bonnes et avancées réponses.

8
devlighted

La fermeture se produit lorsqu'une fonction est fermée d'une manière telle qu'elle a été définie dans un espace de noms immuable par le temps où la fonction est appelée.

En JavaScript, cela se produit lorsque vous:

  • Définir une fonction dans l'autre fonction
  • La fonction interne est appelée après le retour de la fonction externe
// 'name' is resolved in the namespace created for one invocation of bindMessage
// the processor cannot enter this namespace by the time displayMessage is called
function bindMessage(name, div) {

    function displayMessage() {
        alert('This is ' + name);
    }

    $(div).click(displayMessage);
}
8
Pawel Furmaniak

Pour un enfant de six ans ...

Savez-vous ce que sont les objets?

Les objets sont des choses qui ont des propriétés et font des choses.

L'un des aspects les plus importants des fermetures est qu'elles vous permettent de créer des objets en JavaScript. Les objets en JavaScript ne sont que des fonctions et des fermetures qui permettent à JavaScript de stocker la valeur de la propriété pour l'objet une fois qu'il a été créé.

Les objets sont très utiles et gardent tout ce qui est bien organisé. Différents objets peuvent effectuer différentes tâches et travailler ensemble peut faire des choses compliquées.

Heureusement que JavaScript a des fermetures pour fabriquer des objets, sinon tout deviendrait un cauchemar désordonné.

8
ejectamenta

Le meilleur moyen consiste à expliquer progressivement ces concepts:

Variables

console.log(x);
// undefined

Ici, undefined est la façon dont JavaScript utilise pour dire "Je ne sais pas ce que x signifie".

Les variables sont comme des balises.

Vous pouvez dire que la balise x pointe sur la valeur 42:

var x = 42;
console.log(x);
// 42

Maintenant, JavaScript sait ce que x signifie.

Vous pouvez également réaffecter une variable.

Faites en sorte que la balise x pointe sur une valeur différente:

x = 43;
console.log(x);
// 43

Maintenant, x signifie autre chose.

Portée

Lorsque vous créez une fonction, celle-ci possède sa propre "zone" pour les variables.

function A() {
  var x = 42;
}

console.log(x);

// undefined

De l'extérieur de la boîte, vous ne pouvez pas voir ce qu'il y a à l'intérieur.

Mais à l'intérieur de la boîte, vous pouvez voir ce qu'il y a en dehors de cette boîte:

var x = 42;

function A() {
  console.log(x);
}

// 42

Dans la fonction A, vous avez "accès à la portée" à x.

Maintenant, si vous avez deux cases côte à côte:

function A() {
  var x = 42;
}

function B() {
  console.log(x);
}

// undefined

Dans la fonction B, vous n'avez pas accès aux variables de la fonction A.

Mais si vous définissez la fonction B à l'intérieur de la fonction A:

function A() {

  var x = 42;

  function B() {
    console.log(x);
  }

}

// 42

Vous avez maintenant "accès à la portée".

Fonctions

En JavaScript, vous exécutez une fonction en l'appelant:

function A() {
  console.log(42);
}

Comme ça:

A();

// 42

Fonctionne comme une valeur

En JavaScript, vous pouvez pointer une balise sur une fonction, tout comme un nombre:

var a = function() {
  console.log(42);
};

La variable a signifie maintenant une fonction, vous pouvez l'exécuter.

a();
// 42

Vous pouvez également transmettre cette variable autour de:

setTimeout(a, 1000);

En une seconde (1000 millisecondes), la fonction a pointe vers est appelée:

// 42

Portée de la fermeture

Désormais, lorsque vous définissez des fonctions, celles-ci ont accès à leurs portées externes.

Lorsque vous transmettez des fonctions en tant que valeurs, il serait gênant de perdre cet accès.

En JavaScript, les fonctions conservent leur accès aux variables de la portée externe. Même quand ils sont distribués pour être courus ailleurs.

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  // but you want to run `b` later, rather than right away
  setTimeout(b, 1000);

}

Qu'est-ce qui se passe maintenant?

// 'Hello!'

Ou considérez ceci:

var c;

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  c = b;

}

// now we are out side of function `a`
// call `a` so the code inside `a` runs
a(); 

// now `c` has a value that is a function
// because what happened when `a` ran

// when you run `c`
c();

// 'Hello!'

Vous pouvez toujours accéder aux variables dans l'étendue de la fermeture.

Même si a a fini de fonctionner, et maintenant vous utilisez c en dehors de a.

Ce qui vient de se passer ici s'appelle ' fermeture ' en JavaScript.

7
David Rosson

La fermeture ne sont pas difficiles à comprendre. Cela ne dépend que du point de vue.

Personnellement, j'aime les utiliser dans les cas de la vie quotidienne.

function createCar()
{
    var rawMaterial = [/* lots of object */];
    function transformation(rawMaterials)
    {
       /* lots of changement here */
       return transformedMaterial;
    }
    var transformedMaterial = transformation(rawMaterial);
    function assemblage(transformedMaterial)
    {
        /*Assemblage of parts*/
        return car;
    }
    return assemblage(transformedMaterial);
}

Nous n'avons qu'à suivre certaines étapes dans des cas particuliers. Quant à la transformation des matériaux n'est utile que lorsque vous avez les pièces.

7
Alexis

Il était une fois un homme des cavernes

function caveman {

qui avait un rock très spécial,

var rock = "diamond";

Vous ne pouviez pas obtenir le rocher vous-même car il se trouvait dans la grotte privée de l'homme des cavernes. Seul l'homme des cavernes savait comment trouver et obtenir le rocher.

return {
    getRock: function() {
        return rock;
    }
};
}

Heureusement, c'était un homme des cavernes amical, et si vous vouliez attendre son retour, il l'obtiendrait volontiers.

var friend = caveman();
var rock = friend.getRock();

Homme des cavernes assez intelligent.

7
NinjaBeetle

Commençons à partir d’ici, comme défini dans MDN: Les fermetures sont des fonctions qui font référence à des variables indépendantes (libres) (variables qui sont: utilisé localement, mais défini dans une portée englobante). En d'autres termes, ces fonctions "se souviennent" de l'environnement dans lequel elles ont été créées.

Portée lexicale
Considérer ce qui suit:

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

init () crée une variable locale appelée nom et une fonction appelée displayName (). La fonction displayName () est une fonction interne définie dans init () et disponible uniquement dans le corps de la fonction init (). La fonction displayName () n'a pas de variable locale propre. Cependant, étant donné que les fonctions internes ont accès aux variables des fonctions externes, displayName () peut accéder au nom de variable déclaré dans la fonction parent, init ().

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

Exécutez le code et notez que l'instruction alert () de la fonction displayName () affiche correctement la valeur de la variable name, qui est déclarée dans sa fonction parent. Il s'agit d'un exemple de portée lexicale, qui décrit comment un analyseur syntaxique résout les noms de variables lorsque des fonctions sont imbriquées. Le mot "lexical" fait référence au fait que la portée lexicale utilise l'emplacement où une variable est déclarée dans le code source pour déterminer où cette variable est disponible. Les fonctions imbriquées ont accès aux variables déclarées dans leur étendue externe.

Fermeture
Considérons maintenant l’exemple suivant:

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

L'exécution de ce code a exactement le même effet que l'exemple précédent de la fonction init () ci-dessus: cette fois, la chaîne "Mozilla" sera affichée dans une zone d'alerte JavaScript. Ce qui est différent - et intéressant - est que la fonction interne displayName () est renvoyée par la fonction externe avant d’être exécutée.

À première vue, il peut sembler peu intuitif que ce code fonctionne toujours. Dans certains langages de programmation, les variables locales d'une fonction n'existent que pour la durée de son exécution. Une fois que makeFunc () a fini de s’exécuter, vous pouvez vous attendre à ce que la variable de nom ne soit plus accessible. Cependant, comme le code fonctionne toujours comme prévu, ce n'est évidemment pas le cas en JavaScript.

La raison en est que les fonctions dans les fermetures de formulaire JavaScript. Une fermeture est la combinaison d'une fonction et de l'environnement lexical dans lequel cette fonction a été déclarée. Cet environnement est constitué de toutes les variables locales qui étaient dans la portée au moment de la création de la fermeture. Dans ce cas, myFunc est une référence à l'instance de la fonction displayName créée lors de l'exécution de makeFunc. L'instance de displayName conserve une référence à son environnement lexical, dans lequel le nom de variable existe. Pour cette raison, lorsque myFunc est appelé, le nom de la variable reste disponible et "Mozilla" est transmis à alert.

Voici un exemple un peu plus intéressant - une fonction makeAdder:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Dans cet exemple, nous avons défini une fonction makeAdder (x), qui prend un seul argument, x, et retourne une nouvelle fonction. La fonction renvoyée prend un seul argument, y, et renvoie la somme de x et y.

MakeAdder est essentiellement une fabrique de fonctions - elle crée des fonctions qui peuvent ajouter une valeur spécifique à leur argument. Dans l'exemple ci-dessus, nous utilisons notre fabrique de fonctions pour créer deux nouvelles fonctions: une qui ajoute 5 à son argument et l'autre qui en ajoute 10.

add5 et add10 sont les deux fermetures. Ils partagent la même définition de corps de fonction, mais stockent différents environnements lexicaux. Dans l'environnement lexical de add5, x est égal à 5, tandis que dans l'environnement lexical pour add10, x est égal à 10.

Fermetures pratiques

Les fermetures sont utiles car elles vous permettent d'associer des données (l'environnement lexical) à une fonction qui agit sur ces données. Cela a des parallèles évidents avec la programmation orientée objet, où les objets nous permettent d’associer certaines données (propriétés de l’objet) à une ou plusieurs méthodes.

Par conséquent, vous pouvez utiliser une fermeture partout où vous pourriez normalement utiliser un objet avec une seule méthode.

Les situations dans lesquelles vous voudrez peut-être faire cela sont particulièrement courantes sur le Web. Une grande partie du code que nous écrivons dans le code JavaScript frontal est basé sur des événements. Nous définissons un comportement, puis nous l'attachons à un événement déclenché par l'utilisateur (comme un clic ou une pression sur une touche). Notre code est généralement associé en tant que rappel: une seule fonction qui est exécutée en réponse à l'événement.

Par exemple, supposons que nous souhaitions ajouter des boutons à une page pour ajuster la taille du texte. Une façon de procéder consiste à spécifier la taille de la police de l'élément body en pixels, puis à définir la taille des autres éléments de la page (tels que les en-têtes) à l'aide de l'unité relative em:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

Nos boutons de taille de texte interactifs peuvent modifier la propriété font-size de l'élément body et les ajustements seront repris par d'autres éléments de la page grâce aux unités relatives. Voici le JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12, size14 et size16 sont maintenant des fonctions qui redimensionneront le corps du texte à 12, 14 et 16 pixels, respectivement. Nous pouvons les attacher à des boutons (ici des liens) comme suit:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>


function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

pour en savoir plus sur les fermetures, visitez le lien sur MDN

6
Alireza

J'ai déjà lu tous ces documents auparavant et ils sont tous très instructifs. Certains sont sur le point d'obtenir une explication simple pour ensuite devenir complexes ou rester abstraits, allant à l'encontre du but recherché et ne montrant pas une utilisation très simple du monde réel.

Bien que vous parcouriez tous les exemples et explications pour avoir une bonne idée de ce que sont les fermetures au moyen de commentaires et de codes, je n’étais toujours pas satisfait d’une illustration très simple qui m’a aidé à obtenir une utilité des fermetures sans s’avérer si complexe. Ma femme veut apprendre à coder et je me suis dit que je devais être capable de montrer ici non seulement quoi, mais pourquoi et comment.

Je ne suis pas sûr qu'un enfant de six ans l'aura, mais je pense que ce serait peut-être un peu plus près de démontrer un cas simple, concrètement, qui pourrait être utile et facile à comprendre.

L'un des meilleurs (ou le plus simple des plus simples) est la réédition de l'exemple de Morris 'Closures for Dummies.

Prenons le concept "SayHi2Bob" juste un peu plus loin montre les deux choses fondamentales que vous pouvez glaner en lisant toutes les réponses:

  1. Les fermetures ont accès aux variables de la fonction contenant.
  2. Les fermetures persistent dans leur propre espace mémoire (et sont donc utiles pour tous les types d'instanciations oop-y)

Prouvant et démontrant cela moi-même, je fis un peu de violon:

http://jsfiddle.net/9ZMyr/2/

function sayHello(name) {
  var text = 'Hello ' + name; // Local variable
  console.log(text);
  var sayAlert = function () {
      alert(text);
  }
  return sayAlert;
}

sayHello(); 
/* This will write 'Hello undefined' to the console (in Chrome anyway), 
but will not alert though since it returns a function handle to nothing). 
Since no handle or reference is created, I imagine a good js engine would 
destroy/dispose of the internal sayAlert function once it completes. */

// Create a handle/reference/instance of sayHello() using the name 'Bob'
sayHelloBob = sayHello('Bob');
sayHelloBob();

// Create another handle or reference to sayHello with a different name
sayHelloGerry = sayHello('Gerry');
sayHelloGerry();

/* Now calling them again demonstrates that each handle or reference contains its own 
unique local variable memory space. They remain in memory 'forever' 
(or until your computer/browser explode) */
sayHelloBob();
sayHelloGerry();

Cela démontre les deux concepts de base que vous devriez avoir sur les fermetures.

En termes simples, pour expliquer pourquoi cela est utile, j’ai une fonction de base à laquelle je peux faire des références ou des descripteurs contenant des données uniques qui persistent dans cette référence de mémoire. Je n'ai pas à réécrire la fonction à chaque fois que je veux dire le nom de quelqu'un. J'ai encapsulé cette routine et l'ai rendue réutilisable.

Pour moi, cela conduit au moins aux concepts de base des constructeurs, des pratiques oop, des singletons par rapport aux instances instanciées avec leurs propres données, etc.

Si vous démarrez un néophyte avec cela, vous pouvez passer à des appels plus complexes fondés sur la propriété d'objet/membre et, espérons-le, sur les concepts.

6
williambq

Je pense qu’il est intéressant de prendre du recul et d’examiner une notion plus générale de "fermeture" - le soi-disant "opérateur de jointure".

En mathématiques, un opérateur "rejoindre" est une fonction d'un ensemble partiellement ordonné qui renvoie le plus petit objet supérieur ou égal à ses arguments. Dans les symboles, rejoignez [a, b] = d tel que d> = a et d> = b, mais il n’existe pas de e tel que d> e> = a ou d> e> = b.

Donc, la jointure vous donne la plus petite chose "plus grande" que les parties.

Notez maintenant que les étendues JavaScript sont une structure partiellement ordonnée. Il y a donc une notion raisonnable de jointure. En particulier, une jointure d'étendues est la plus petite étendue plus grande que l'étendue d'origine. Cette portée est appelée le fermeture.

Donc, une fermeture pour les variables a, b, c est la plus petite portée (dans le réseau des portées de votre programme!) Qui met a, b et c dans la portée.

6
nomen

Le cas d'utilisation le plus simple auquel je puisse penser est d'expliquer fermetures de JavaScript est le modèle de module. Dans le modèle de module, vous définissez une fonction et appelez-la immédiatement après dans ce que l'on appelle une expression de fonction immédiatement appelée (IIFE). Tout ce que vous écrivez dans cette fonction a une portée privée car il est défini à l'intérieur de la fermeture, vous permettant ainsi de "simuler" la confidentialité en JavaScript. Ainsi:

 var Closure = (function () {
    // This is a closure
    // Any methods, variables and properties you define here are "private"
    // and can't be accessed from outside the function.

    //This is a private variable
    var foo = "";

    //This is a private method
    var method = function(){

    }
})();

Si, par contre, vous souhaitez rendre une ou plusieurs variables ou méthodes visibles en dehors de la fermeture, vous pouvez les renvoyer à l'intérieur d'un littéral d'objet. Ainsi:

var Closure = (function () {
  // This is a closure
  // Any methods, variables and properties you define here are "private"
  // and can't be accessed from outside the function.

  //This is a private variable
  var foo = "";

  //This is a private method
  var method = function(){

  }

  //The method will be accessible from outside the closure
  return {
    method: method
  }

})();

Closure.method();

J'espère que ça aide. Cordialement,

6
Javier La Banca

Le mot fermeture se réfère simplement à pouvoir accéder à objets (six ans: choses) qui sont fermé (six ans) : privé) dans un fonction (six ans: boîte). Même si la fonction (six ans: boîte) est en dehors de portée (six ans: envoyée au loin).

6
cube

Une fermeture crée essentiellement deux choses: - une fonction - une portée privée à laquelle seule cette fonction peut accéder

C'est comme mettre un revêtement sur une fonction.

Donc, pour un enfant de 6 ans, cela pourrait s'expliquer par une analogie. Disons que je construis un robot. Ce robot peut faire beaucoup de choses. Parmi ces choses, je l'ai programmé pour compter le nombre d'oiseaux qu'il voit dans le ciel. Chaque fois qu'il a vu 25 oiseaux, il devrait me dire combien d'oiseaux il a vus depuis le début.

Je ne sais pas combien d'oiseaux il a vus à moins qu'il ne me l'ait dit. Seulement il sait. C'est la portée privée. C'est fondamentalement la mémoire du robot. Disons que je lui ai donné 4 Go.

Me dire combien d'oiseaux il a vus est la fonction restituée. J'ai aussi créé ça.

Cette analogie est un peu moche, mais je suppose que l’on pourrait l’améliorer.

6
Taye

Mon point de vue des fermetures:

Les fermetures peuvent être comparées à un livre, avec un signet, sur une étagère.

Supposons que vous ayez lu un livre et que vous aimiez une page du livre. Vous insérez un signet sur cette page pour le suivre.

Maintenant, une fois que vous avez fini de lire le livre, vous n’avez plus besoin de ce livre, sauf que vous voulez avoir accès à cette page. Vous auriez pu simplement couper la page, mais vous perdriez alors le contexte de l'histoire. Donc, vous remettez le livre dans votre bibliothèque avec le marque-page.

Ceci est similaire à une fermeture. Le livre est la fonction externe et la page est votre fonction interne, qui est renvoyée à partir de la fonction externe. Le signet est la référence à votre page et le contexte de l’histoire est la portée lexicale que vous devez conserver. La bibliothèque est la pile de fonctions, qui ne peut pas être nettoyée des anciens livres, tant que vous ne tenez pas la page.

Exemple de code:

function book() {
   var pages = [....]; //array of pages in your book
   var bookMarkedPage = 20; //bookmarked page number
   function getPage(){
       return pages[bookMarkedPage];
   }
   return getPage;
}

var myBook = book(),
    myPage = myBook.getPage();

Lorsque vous exécutez la fonction book(), vous allouez de la mémoire dans la pile pour l'exécution de la fonction. Mais puisqu'il renvoie une fonction, la mémoire ne peut pas être libérée, car la fonction interne a accès aux variables du contexte. en dehors, dans ce cas, "pages" et "bookMarkedPage".

Donc, appeler efficacement book() renvoie une référence à une fermeture, c'est-à-dire non seulement une fonction, mais une référence au livre et à son contexte, c'est-à-dire une référence à la fonction getPage , état des pages et des variables bookMarkedPage .

Quelques points à considérer:

Point 1: La bibliothèque, tout comme la pile de fonctions a un espace limité, utilisez-la à bon escient.

Point 2: Réfléchissez au fait de savoir si vous devez conserver l'intégralité du livre lorsque vous souhaitez simplement suivre une seule page. Vous pouvez libérer une partie de la mémoire en ne stockant pas toutes les pages du livre lorsque la fermeture est renvoyée.

Ceci est ma perspective des fermetures. J'espère que cela aidera, et si quelqu'un pense que cela n'est pas correct, faites-le-moi savoir, car je suis très intéressé à en comprendre encore plus sur les portées et les fermetures!

5
poushy

Aussi ... Peut-être devrions-nous laisser un peu de temps à votre ami de 27 ans , parce que tout le concept de "fermetures" vraiment est (!) ... voodoo!

Je veux dire par là: (a) vous ne vous attendez pas, intuitivement, à l'attendre ... ET ... (b) quand quelqu'un prend le temps de vous l'expliquer, vous ne vous attendez certainement pas à ce qu'il ( fonctionne!

L'intuition vous dit que "cela doit être absurde ... sûrement cela doit entraîner une sorte d'erreur de syntaxe ou quelque chose!" Comment diable (!) pourriez-vous, en fait, "tirer une fonction du" milieu de "partout où elle est," telle que [encore!] pourrait-il réellement avoir un accès en lecture/écriture au contexte de "where-it - was - at ?!"

Lorsque vous réalisez finalement qu'une telle chose est possible, , alors ... bien sûr ... n'importe qui après-le -fact la réaction serait: "whoa-aaa (!) ... kew-el-lll ... (!!!)"

Mais il faudra d'abord "surmonter un grand obstacle contre-intuitif". L'intuition vous donne de nombreuses attentes tout à fait plausibles qu'une telle chose soit "bien sûr, , absolument absurde et donc tout à fait impossible."

Comme je le disais: "c'est du vaudou".

4
Mike Robinson

La réponse la plus simple, la plus courte et la plus facile à comprendre:

Une fermeture est un bloc de code où chaque ligne peut référencer le même ensemble de variables avec les mêmes noms de variables.

Si "ceci" signifie quelque chose de différent d’ailleurs, alors vous savez que ce sont deux fermetures différentes.

4
Nick Manning

La fermeture peut être des variables ou des fonctions privées et publiques.

var ClusureDemo = function() {
    //privare variables
    var localVa1, localVa2;

    //private functions
    var setVaOne = function(newVa) {
        localVa1 = newVa;
    },
    setVaTwo = function(newVa) {
        localVa2 = newVa;
    },
    getVaOne = function() {
        return localVa1;
    },
    getVaTwo = function() {
        return localVa2;
    };

    return {
        //public variables and functions
        outVaOne : localVa1,
        outVaTwo : localVa2,
        setVaOne : setVaOne,
        setVaTwo : setVaTwo,
        getVaOne : getVaOne,
        getVaTwo : getVaTwo
    };
};

//Test Demo
var app = new ClusureDemo();
app.outVaOne = 'Hello Variable One';
app.outVaTwo = 'Hello Variable Two';
app.setVaOne(app.outVaOne);
app.setVaTwo(app.outVaTwo);

alert(app.getVaOne());
alert(app.getVaTwo());

démo

2
Pao Im

Une fermeture est une fonction qui a accès aux informations de l'environnement dans lequel elle a été définie.

Pour certains, l'information est la valeur dans l'environnement au moment de la création. Pour d'autres, les informations sont les variables de l'environnement au moment de la création.

Si l'environnement lexical auquel fait référence la fermeture appartient à une fonction qui a été fermée, alors (dans le cas d'une clôture faisant référence aux variables de l'environnement), ces variables lexicales continueront d'exister pour référence par la fermeture.

Une fermeture peut être considérée comme un cas particulier de variables globales - avec une copie privée créée uniquement pour la fonction.

Ou bien, on peut penser à une méthode où l'environnement est une instance spécifique d'un objet dont les propriétés sont les variables de l'environnement.

La première (fermeture en tant qu'environnement) est similaire à la dernière, où la copie d'environnement est une variable de contexte transmise à chaque fonction de la première, et les variables d'instance forment une variable de contexte dans la dernière.

Ainsi, une fermeture est un moyen d'appeler une fonction sans avoir à spécifier explicitement le contexte en tant que paramètre ou en tant qu'objet dans une invocation de méthode.

var closure = createclosure(varForClosure);
closure(param1);  // closure has access to whatever createclosure gave it access to,
                  // including the parameter storing varForClosure.

contre

var contextvar = varForClosure; // use a struct for storing more than one..
contextclosure(contextvar, param1);

contre

var contextobj = new contextclass(varForClosure);
contextobj->objclosure(param1);

Pour le code maintenable, je recommande la méthode orientée objet. Toutefois, pour un ensemble de tâches rapide et simple (par exemple, la création d'un rappel), une fermeture peut devenir naturelle et plus claire, en particulier dans le contexte de fonctions lamda ou anonymes.

2
Gerard ONeill

Une fermeture est simplement lorsqu'une fonction a accès à son étendue externe même après que l'exécution de la fonction de l'étendue a été terminée. Exemple:

function multiplier(n) {
    function multiply(x) {
          return n*x;
    }
    return mutliply;
}

var 10xmultiplier = multiplier(10);
var x = 10xmultiplier(5); // x= 50

nous pouvons voir que même après que le multiplicateur ait fini de s'exécuter, la fonction interne multiply a toujours accès à la valeur de x qui est 10 dans cet exemple.

Une utilisation très courante des fermetures est le currying (le même exemple ci-dessus) où nous épices notre fonction progressivement avec des paramètres au lieu de fournir tous les arguments à la fois.

Nous pouvons y parvenir parce que Javascript (en plus de la POO prototypique) permet de programmer de manière fonctionnelle où des fonctions d'ordre supérieur peuvent prendre d'autres fonctions en tant qu'arguments (fonctions de classe fisrt). programmation fonctionnelle dans wikipedia

Je vous recommande fortement de lire ce livre de Kyle Simpson: 2 une partie de la série de livres est consacrée aux fermetures et elle s’appelle portée et fermetures. vous ne savez pas js: lecture libre sur github

2
zak.http