7625 votes

Comment faire JavaScript fermetures de travail?

Comme le vieil Albert Einstein a dit:

Si vous ne pouvez pas expliquer à un enfant de six ans, vous n'avez vraiment pas à comprendre vous-même.

Eh bien, j'ai essayé d'expliquer JavaScript fermetures à 27 ans vieil ami et complètement échoué.

Comment voulez-vous expliquer à quelqu'un avec une connaissance des concepts qui font de fermeture (par exemple, les fonctions, les variables et les autres), mais ne comprennent pas les fermetures d'eux-mêmes?

J'ai vu le Schéma de l'exemple donné dans le Débordement de la Pile, et il n'a pas aidé.

4110voto

Ali Points 553

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) {
    alert(x + y + (++tmp)); // will alert 16
  }
  bar(10);
}
foo(2);

Ce sera toujours en alerte 16, car bar pouvez accéder à l' x qui a été défini comme un argument à l' foo, et il peut également accéder tmp de foo.

C' est une fermeture. Une fonction n'a pas de retour afin d'être appelé à la fermeture. Tout simplement l'accès aux variables hors de votre portée lexicale crée une fermeture.

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp)); // will also alert 16
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);

La fonction ci-dessus également d'alerter 16, car bar pouvez toujours vous y référer x et tmp, même si elle n'est plus directement à l'intérieur de la portée.

Cependant, depuis tmp est toujours traîner à l'intérieur d' bar's la fermeture, c'est aussi d'être augmentés. Il sera incrémenté chaque fois que vous appelez bar.

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

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

Il est possible de créer plus d'une fonction de clôture, soit par le retour d'une liste d'entre eux ou par l'établissement à des variables globales. Tous ces éléments vont se référer à la même x et le même tmp, ils ne font pas leurs propres copies.

Ici, le nombre x est un nombre littéral. Comme avec d'autres littéraux en JavaScript, lorsqu' foo est appelé, le nombre x est copié en foo comme argument x.

D'autre part, JavaScript utilise toujours des références lorsque vous traitez avec des Objets. Si par exemple, vous avez appelé foo avec un Objet, la fermeture il rendements de référence que l'Objet d'origine!

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(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 à l' bar(10) incrémente x.memb. Ce peut-être pas prévu, c'est qu' x est tout simplement référence au même objet que l' age variable! Après quelques appels à bar, age.memb 2! Ce référencement est la base pour les fuites de mémoire avec des objets HTML.

2569voto

Jacob Swartwood Points 3994

Je suis un grand fan de l'analogie et de la métaphore pour expliquer des concepts difficiles, alors laissez-moi tenter ma main avec une histoire.

Once upon a time:

Il y avait une princesse...

function princess() {

Elle vivait dans un monde merveilleux et plein d'aventures. Elle a rencontré son Prince Charmant, roulé autour de son monde sur une licorne, combattu des dragons, rencontré des animaux qui parlent, et beaucoup d'autres fantastiques choses.

    var adventures = [];

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

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

Mais elle aurait toujours de revenir en arrière à son terne monde de corvées et les adultes.

    return {

Et elle allait souvent de leur dire de sa dernière aventure incroyable comme une princesse.

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

Mais tout ce qu'ils voient est une petite fille...

var littleGirl = princess();

...de raconter des histoires à propos de la magie et de la fantaisie.

littleGirl.story();

Et même si les adultes savaient de vraies princesses, ils n'auraient jamais croire en l'licornes ou des dragons car ils ne pourraient jamais les voir. Les adultes ont dit qu'ils n'existaient à l'intérieur de la petite fille à l'imagination.

Mais nous savons la vérité; que la petite fille de la princesse à l'intérieur...

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

799voto

dlaliberte Points 1947

Prendre la question au sérieux, nous devons savoir ce qu'est un typique de 6 ans est capable de le plan cognitif, certes, celui qui est intéressé dans le JavaScript n'est pas typique.

Sur le Développement de l'enfant: 5 à 7 Ans, il dit:

Votre enfant sera en mesure de suivre les deux étapes les directions. Par exemple, si vous dites à votre enfant "Aller à la cuisine et me faire un sac-poubelle", ils seront en mesure de se rappeler que la 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é getTrashBag qui obtient un sac poubelle et le renvoie.

Nous pouvons à présent code en JavaScript comme ceci:

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

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

var kitchen = makeKitchen();

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

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

  • Chaque fois makeKitchen() est appelé, une nouvelle fermeture est créé avec sa propre trashBags.
  • Le trashBags variable est locale à l'intérieur de chaque cuisine, et n'est pas accessible de l'extérieur, mais l'intérieur de la fonction sur la getTrashBag bien n'y avoir accès.
  • Chaque appel de fonction crée une fermeture, mais il n'y aurait pas besoin de garder la clôture autour de moins qu'une fonction interne, qui a accès à l'intérieur de la clôture, peut être appelé de l'extérieur de la clôture. Retour sur l'objet avec le getTrashBag fonction n'a qu'ici.

613voto

jondavidjohn Points 28769

L'Homme De Paille

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

De Façon Assez Évidente Solution

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

element.onclick = function() {
    // increment outside counter
    counter++;

    if (counter === 3) {
        // do something every third time
        alert("Third time's the charm!");
        // reset counter
        counter = 0;
    }
};

Maintenant, cela va fonctionner, mais il n'empiéter en extérieur, portée par l'ajout d'une variable, dont le seul but est de garder une trace du comte. Dans certaines situations, il serait préférable que votre extérieur d'application peut avoir besoin d'accéder à cette information. Mais dans ce cas nous sommes seulement un changement tous les trois sur le comportement, il est donc préférable de placer cette fonctionnalité dans le gestionnaire d'événement.

Envisager cette option

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

element.onclick = (function() {
    // init the count to 0
    var count = 0;

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

        if (count === 3) {
            // do something every third time
            alert("Third time's the charm!");
            //reset counter
            count = 0;
        }
    };
})();

Avis un peu les choses ici

Dans l'exemple ci-dessus, je suis l'aide de la fermeture comportement de JavaScript. Ce comportement permet à n'importe quelle fonction pour avoir accès à de l'étendue dans laquelle il a été créé, indéfiniment. Mettre en pratique ce, j'ai immédiatement appeler une fonction qui renvoie une autre fonction, et parce que la fonction je suis de retour a accès à l'intérieur de la variable count (en raison de la fermeture de comportement expliqué ci-dessus), il en résulte une portée privée pour l'utilisation de la fonction... Pas si simple? Permet de diluer...

Une simple fermeture de ligne

//         ____________Immediately executed (self 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 à l'extérieur de la fonction renvoyée sont disponibles pour le retour de la fonction, mais ne sont pas directement disponibles pour le retour de la fonction de l'objet....

func();  // alerts "value"
func.a;  // undefined

Pour l'obtenir? donc, dans notre premier exemple, la variable compteur est contianed à l'intérieur de la clôture et toujours disponible pour le gestionnaire d'événements, de sorte qu'il conserve son état de cliquez pour cliquez.

Aussi cette variable privée de l'état est pleinement accessible, à la fois la lecture et l'affectation à il est privé d'étendue variables.

Voilà, vous êtes maintenant pleinement de l'encapsulation de ce comportement

Article complet (y compris jQuery)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X