À quoi sert une fermeture en JavaScript?


280

J'essaie de mon mieux pour envelopper ma tête autour de la fermeture JavaScript.

J'obtiens qu'en retournant une fonction interne, il aura accès à n'importe quelle variable définie dans son parent immédiat.

Où cela me serait-il utile? Peut-être que je n'ai pas encore tout à fait compris. La plupart des exemples que j'ai vus en ligne ne fournissent aucun code du monde réel, juste de vagues exemples.

Quelqu'un peut-il me montrer une utilisation réelle d'une fermeture?

Est-ce celui-ci, par exemple?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
+1 pour avoir fait de votre mieux :-) Les fermetures peuvent sembler vraiment intimidantes au départ, je sais qu'elles étaient pour moi. Une fois que vous aurez compris, vous serez instantanément un meilleur codeur.
Andy E

7
Je viens d'écrire un article de blog sur les fermetures en JavaScript que vous pourriez trouver helfpul.
Skilldrick

@Skilldrick. lien est mort ... et j'ai aussi trouvé cet exemple pratique très utile. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Réponses:


241

J'ai utilisé des fermetures pour faire des choses comme:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

Comme vous pouvez le voir, aest maintenant un objet, avec une méthode publicfunction( a.publicfunction()) qui appelle privatefunction, qui n'existe qu'à l'intérieur de la fermeture. Vous ne pouvez PAS appeler privatefunctiondirectement (ie a.privatefunction()), juste publicfunction().

C'est un exemple minimal mais peut-être pouvez-vous en voir les utilisations? Nous l'avons utilisé pour appliquer des méthodes publiques / privées.


26
Ah, si c'est une fermeture, alors j'ai utilisé des fermetures sans le savoir! Je mets souvent des fonctions dans un autre comme ça, puis j'expose tout ce dont j'ai besoin en retournant un objet littéral comme dans votre exemple.
alex

1
Oui, comme vous le voyez, vous conservez le contexte de la fonction car l'objet que vous renvoyez référence des variables (et des fonctions) à l'intérieur. Vous les utilisez donc, vous ne le saviez tout simplement pas.
Francisco Soto

9
Techniquement, chaque fonction que vous créez en Javascript sur un navigateur est une fermeture car l'objet fenêtre y est lié.
Adam Gent

9
Je sais que c'est une vieille question, mais pour moi, cela ne fournit toujours pas de réponse adéquate. Pourquoi ne pas simplement appeler la fonction directement? Pourquoi avez-vous besoin d'une fonction privée?
qodeninja

5
Parce que même si l'exemple n'a qu'une fonction, il peut également avoir des variables qui ne sont pas accessibles de l'extérieur. Dites: var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 etc.
Francisco Soto

212

Supposons que vous souhaitiez compter le nombre de fois où l'utilisateur a cliqué sur un bouton d'une page Web.
Pour cela, vous déclenchez une fonction sur onclickévénement de bouton pour mettre à jour le décompte de la variable

<button onclick="updateClickCount()">click me</button>  

Maintenant, il pourrait y avoir de nombreuses approches comme:

1) Vous pouvez utiliser une variable globale et une fonction pour augmenter le compteur :

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

Mais, l'écueil est que tout script sur la page peut changer le compteur, sans appelerupdateClickCount() .


2) Maintenant, vous pourriez penser à déclarer la variable à l'intérieur de la fonction:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

Mais salut! Chaque fois que la updateClickCount()fonction est appelée, le compteur est remis à 1.


3) Penser aux fonctions imbriquées ?

Les fonctions imbriquées ont accès à l'étendue "au-dessus" d'elles.
Dans cet exemple, la fonction interne updateClickCount()a accès à la variable compteur dans la fonction parentcountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

Cela aurait pu résoudre le contre-dilemme, si vous pouviez accéder à la updateClickCount()fonction de l'extérieur et que vous deviez également trouver un moyen de l'exécuter counter = 0une seule fois pas à chaque fois.


4) Fermeture à la rescousse! (fonction auto-invoquante) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

La fonction auto-invoquante ne s'exécute qu'une seule fois. Il met counterà zéro (0) et renvoie une expression de fonction.

Cette façon updateClickCountdevient une fonction. La partie "merveilleuse" est qu'elle peut accéder au compteur dans la portée parent.

C'est ce qu'on appelle une fermeture JavaScript . Il permet à une fonction d'avoir des variables " privées ".

Le counterest protégé par l'étendue de la fonction anonyme et ne peut être modifié qu'à l'aide de la fonction add!

Exemple plus vivant sur la fermeture:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


Référence: https://www.w3schools.com/js/js_function_closures.asp


50
C'est la première réponse qui m'a fait dire "Oh, c'est pourquoi j'utiliserais des fermetures!"
Devil's Advocate

6
tu
as

15
Je viens de lire la page w3schools sur les fermetures, puis je suis venu ici pour plus d'informations. C'est la même chose que la page w3schools
tyelford

1
@JerryGoyal pourriez-vous le faire fonctionner avec 2 boutons séparés? Je ne peux pas comprendre comment sans recourir à 2 variables (copies de la fonction), ce qui semble supprimer certains des principaux avantages / commodités.
Tyler Collier

2
Bonne réponse. Notez cependant qu'une fermeture n'a pas besoin d'être une fonction auto-invoquante, mais elle peut l' être. Lorsqu'une fermeture est auto-invoquée (c'est-à-dire immédiatement appelée en ajoutant () après la fonction), cela signifie que la valeur de retour est immédiatement calculée, plutôt que la fonction renvoyée et la valeur de retour calculée plus tard une fois la fonction invoquée. Une fermeture peut en fait être n'importe quelle fonction au sein d'une autre fonction, et sa principale caractéristique est qu'elle a accès à la portée de la fonction parent, y compris ses variables et méthodes.
Chris Halcrow

69

L'exemple que vous donnez est excellent. Les fermetures sont un mécanisme d'abstraction qui vous permet de séparer les problèmes très proprement. Votre exemple est un cas de séparation de l'instrumentation (comptage des appels) de la sémantique (une API de rapport d'erreurs). D'autres utilisations incluent:

  1. Passer un comportement paramétré dans un algorithme (programmation classique d'ordre supérieur):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. Simuler une programmation orientée objet:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. Implémentation d'un contrôle de flux exotique, tel que la gestion des événements de jQuery et les API AJAX.


3
( int?) La dernière fois que j'ai vérifié, JavaScript était un langage de type canard. Vous pensiez peut-être à Java?
Hello71

1
@ Hello71: Je pensais à JavaScript, mais les vieilles habitudes ont la vie dure. Bonne prise.
Marcelo Cantos

2
@MarceloCantos il semble que vous ayez oublié une virgule dans la mise en œuvre du compteur. J'ai modifié votre message pour le corriger. J'espère que ça va :)
Natan Streppel

2
@Streppel: Bonne prise! Je suis plus qu'heureux que vous amélioriez mon code. :-)
Marcelo Cantos

essayer de comprendre # 1 ... Comment appelleriez-vous proximité_sort?
Dave2081

26

Je sais que je suis super en retard pour répondre à cette question, mais cela pourrait aider quiconque cherche toujours la réponse en 2018.

Fermetures Javascript peuvent être utilisés pour mettre en œuvre des gaz et debounce fonctionnalité dans votre application.

Limitation :

La limitation limite le nombre maximal de fois qu'une fonction peut être appelée dans le temps. Comme dans «exécuter cette fonction au plus une fois toutes les 100 millisecondes».

Code:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Anti - rebond :

Le rebouncing limite une fonction à ne plus être appelée tant qu'un certain laps de temps ne s'est pas écoulé sans qu'elle soit appelée. Comme dans "n'exécuter cette fonction que si 100 millisecondes se sont écoulées sans qu'elle soit appelée".

Code:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

Comme vous pouvez le voir, les fermetures ont aidé à implémenter deux belles fonctionnalités que chaque application Web devrait avoir pour offrir une fonctionnalité d'interface utilisateur fluide.

J'espère que cela aidera quelqu'un.


18

Oui, c'est un bon exemple de fermeture utile. L'appel à warnUser crée la calledCountvariable dans sa portée et renvoie une fonction anonyme qui est stockée dans la warnForTampervariable. Parce qu'il y a toujours une fermeture utilisant la variable appeléeCompte, elle n'est pas supprimée à la sortie de la fonction, donc chaque appel à la warnForTamper()va augmenter la variable de portée et alerter la valeur.

Le problème le plus courant que je vois sur StackOverflow est celui où quelqu'un veut "retarder" l'utilisation d'une variable qui est augmentée à chaque boucle, mais parce que la variable est limitée, chaque référence à la variable se ferait après la fin de la boucle, entraînant la état final de la variable:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

Cela entraînerait que chaque alerte affiche la même valeur de i, la valeur à laquelle elle a été augmentée à la fin de la boucle. La solution consiste à créer une nouvelle fermeture, une portée distincte pour la variable. Cela peut être fait en utilisant une fonction anonyme exécutée instantanément, qui reçoit la variable et stocke son état comme argument:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

Intéressant -1, je suppose que ce n'est pas "une utilisation pratique pour une fermeture en javascript"?
Andy E

1
J'ai trouvé une certaine utilité à le lire, j'ai donc attribué un +1 avant le downvote.
alex

1
@alex: merci, j'ai remarqué le vote positif. Je me suis presque habitué aux votes anonymes ici à SO. Cela ne m'énerve que parce que j'aimerais vraiment savoir si j'ai dit quelque chose d'inexact ou de mal, et ils ont tendance à vous faire croire que vous venez d'être dévalorisé par un autre répondeur qui souhaite une meilleure visibilité pour sa propre réponse. Heureusement, je ne suis pas du genre vengeur ;-)
Andy E

1
Je pense que c'est plus un travail autour de l'étendue des blocs cassés JavaScripts. Vous devriez juste pouvoir ajouter var j = i; avant le premier setTimeout et obtenez l'alerte pour utiliser ce j. Une autre solution consiste à utiliser «avec» comme ceci: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Value of j'étais "+ i +" lorsque cette minuterie a été réglée ")}, 100);}}
davidbuttar

1
@AndyE Funny n'est peut-être pas le bon mot. Je viens de remarquer que souvent les gens utilisent des fonctions auto-appelantes pour expliquer les fermetures, comme beaucoup de réponses sur cette page. Mais la fonction de rappel dans setTimeout est également une fermeture; cela peut être considéré comme "une utilisation pratique" car vous pouvez accéder à d'autres variables locales à partir du rappel. Lorsque j'ai découvert les fermetures, je me suis rendu compte que cela était utile - que les fermetures sont partout, pas seulement dans les modèles JavaScript d'arcade.
antoine

14

Dans le langage JavaScript (ou n'importe quel ECMAScript), en particulier, les fermetures sont utiles pour masquer l'implémentation des fonctionnalités tout en révélant l'interface.

Par exemple, imaginez que vous écrivez une classe de méthodes utilitaires de date et que vous souhaitez autoriser les utilisateurs à rechercher des noms de semaine par index, mais que vous ne voulez pas qu'ils puissent modifier le tableau de noms que vous utilisez sous le capot.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Notez que le daystableau pourrait simplement être stocké en tant que propriété de l' dateUtilobjet, mais il serait alors visible pour les utilisateurs du script et ils pourraient même le modifier s'ils le voulaient, sans même avoir besoin de votre code source. Cependant, comme il est entouré par la fonction anonyme qui renvoie la fonction de recherche de date, il n'est accessible que par la fonction de recherche, il est donc désormais inviolable.


2
Cela peut sembler stupide, mais ne pourraient-ils pas simplement ouvrir le fichier JavaScript lui-même et voir votre implémentation?
itsmichaelwang

1
@Zapurdead: oui, ils pouvaient bien sûr voir l'implémentation mais ils ne pouvaient pas changer l'implémentation (accidentellement ou intentionnellement) sans modifier directement votre code source. Je suppose que vous pouvez le comparer aux membres protégés en Java.
maerics

6

Il y a une section sur les fermetures pratiques sur le Mozilla Developer Network .


En regardant à travers cela, je ne vois pas comment c'est "pratique" comme si je supprimais le tout, return function ()...le code fonctionne toujours bien. La fermeture n'est pas nécessaire
un perce-oreille

@James_Parsons Vous ne pouviez pas alors les affecter aux gestionnaires d'événements comme ils l'avaient fait dans l'exemple.
alex

5

Une autre utilisation courante des fermetures consiste à lier thisune méthode à un objet spécifique, ce qui permet de l'appeler ailleurs (comme un gestionnaire d'événements).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Chaque fois qu'un événement mousemove se déclenche, watcher.follow(evt)est appelé.

Les fermetures sont également une partie essentielle des fonctions d'ordre supérieur, permettant le modèle très courant de réécriture de plusieurs fonctions similaires en une seule fonction d'ordre supérieur en paramétrant les parties dissemblables. À titre d'exemple abstrait,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

devient

fooer = function (x) {
    return function (...) {A x B}
}

où A et B ne sont pas des unités syntaxiques mais des chaînes de code source (pas des littéraux de chaîne).

Voir " Rationaliser mon javascript avec une fonction " pour un exemple concret.


5

Ici, j'ai une salutation que je veux dire plusieurs fois. Si je crée une fermeture, je peux simplement appeler cette fonction pour enregistrer le message d'accueil. Si je ne crée pas la fermeture, je dois passer mon nom à chaque fois.

Sans fermeture ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

Avec une fermeture ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
Je ne suis pas sûr mais toujours sans fermeture, vous pouvez appeler comme var grretBilly = salutation ("Billy", "Bob"); et appelez grretBilly (); Il ferait toujours la même chose ?? bien que vous créez une fermeture ou non, c'est un problème différent, mais le passage du nom à chaque fois n'est pas un problème ici.
user2906608

4

Si vous êtes à l'aise avec le concept d'instanciation d'une classe dans le sens orienté objet (c'est-à-dire pour créer un objet de cette classe), vous êtes sur le point de comprendre les fermetures.

Pensez-y de cette façon: lorsque vous instanciez deux objets Personne, vous savez que la variable de membre de classe "Nom" n'est pas partagée entre les instances; chaque objet a sa propre «copie». De même, lorsque vous créez une fermeture, la variable libre («appeléCompte» dans votre exemple ci-dessus) est liée à «l'instance» de la fonction.

Je pense que votre saut conceptuel est légèrement entravé par le fait que chaque fonction / fermeture renvoyée par la fonction warnUser (à part: c'est une fonction d'ordre supérieur ) la fermeture lie 'appeléCompte' avec la même valeur initiale (0), alors que souvent lors de la création de fermetures il est plus utile de passer différents initialiseurs dans la fonction d'ordre supérieur, un peu comme passer des valeurs différentes au constructeur d'une classe.

Donc, supposez que lorsque 'calledCount' atteint une certaine valeur, vous souhaitez mettre fin à la session de l'utilisateur; vous pouvez vouloir des valeurs différentes pour cela selon que la demande provient du réseau local ou du grand mauvais Internet (oui, c'est un exemple artificiel). Pour ce faire, vous pouvez passer différentes valeurs initiales pour appeléCompte dans warnUser (c'est-à-dire -3 ou 0?).

Une partie du problème avec la littérature est la nomenclature utilisée pour les décrire ("portée lexicale", "variables libres"). Ne vous y trompez pas, les fermetures sont plus simples qu'il n'y paraît ... prima facie ;-)


3

Ici, j'ai un exemple simple de concept de fermeture que nous pouvons utiliser dans notre site de commerce électronique ou bien d'autres. J'ajoute mon lien jsfiddle avec l'exemple. il contient une petite liste de produits de 3 articles et un compteur de panier.

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

Utilisation des fermetures:

Les fermetures sont l'une des fonctionnalités les plus puissantes de JavaScript. JavaScript permet l'imbrication des fonctions et accorde à la fonction interne un accès complet à toutes les variables et fonctions définies à l'intérieur de la fonction externe (et à toutes les autres variables et fonctions auxquelles la fonction externe a accès). Cependant, la fonction externe n'a pas accès aux variables et fonctions définies à l'intérieur de la fonction interne. Cela fournit une sorte de sécurité pour les variables de la fonction interne. De plus, puisque la fonction interne a accès à la portée de la fonction externe, les variables et les fonctions définies dans la fonction externe vivront plus longtemps que la fonction externe elle-même, si la fonction interne parvient à survivre au-delà de la durée de vie de la fonction externe.

Exemple :

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

Dans le code ci-dessus, la variable de nom de la fonction externe est accessible aux fonctions internes, et il n'y a pas d'autre moyen d'accéder aux variables internes sauf via les fonctions internes. Les variables internes de la fonction interne agissent comme des magasins sûrs pour les fonctions internes. Ils contiennent des données "persistantes", mais sécurisées, avec lesquelles les fonctions internes peuvent fonctionner. Les fonctions n'ont même pas besoin d'être attribuées à une variable ou d'avoir un nom. lire ici pour plus de détails


2

J'aime l' exemple de l' usine de fonctions de Mozilla .

function makeAdder(x) {

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

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
C'est le type d'exemple qui n'aide pas les gens à comprendre les fermetures ou à quoi elles servent, à mon avis. Combien de fois avez-vous déjà écrit une fermeture pour renvoyer une fonction pour ajouter des nombres, autrement qu'à titre d'exemple?
Mohamad

2

Le modèle de module JavaScript utilise des fermetures. Son joli motif vous permet d'avoir quelque chose comme des vars "publics" et "privés".

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

Ce fil m'a énormément aidé à mieux comprendre le fonctionnement des fermetures. Depuis, j'ai fait mes propres expériences et j'ai trouvé ce code assez simple qui peut aider d'autres personnes à voir comment les fermetures peuvent être utilisées de manière pratique et comment utiliser la fermeture à différents niveaux pour maintenir des variables similaires à statiques et / ou des variables globales sans risque de les écraser ou de les confondre avec des variables globales. Cela permet de garder une trace des clics sur les boutons, à la fois au niveau local pour chaque bouton individuel et au niveau global, en comptant chaque clic sur le bouton, contribuant à un chiffre unique. Remarque Je n'ai utilisé aucune variable globale pour ce faire, ce qui est en quelque sorte le point de l'exercice - avoir un gestionnaire qui peut être appliqué à n'importe quel bouton qui contribue également à quelque chose à l'échelle mondiale.

S'il vous plaît, experts, faites-moi savoir si j'ai commis des mauvaises pratiques ici! J'apprends toujours ce truc moi-même.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

Référence: Utilisation pratique des fermetures

Dans la pratique, les fermetures peuvent créer des conceptions élégantes, permettant la personnalisation de divers calculs, appels différés, rappels, création d'une portée encapsulée, etc.

Un exemple de la méthode de tri des tableaux qui accepte comme argument la fonction condition de tri:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

Mappage des fonctionnelles comme méthode de mappage des tableaux qui mappe un nouveau tableau par la condition de l'argument fonctionnel:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

Il est souvent pratique d'implémenter des fonctions de recherche en utilisant des arguments fonctionnels définissant des conditions de recherche presque illimitées:

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

Nous pouvons également noter l'application de fonctionnelles comme, par exemple, une méthode forEach qui applique une fonction à un tableau d'éléments:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

Une fonction est appliquée aux arguments (à une liste d'arguments - en application et aux arguments positionnés - en appel):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Appels différés:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

Fonctions de rappel:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Création d'une portée encapsulée dans le but de masquer des objets auxiliaires:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

Une grande partie du code que nous écrivons en JavaScript frontal est basé sur des événements - nous définissons un comportement, puis l'associons à un événement déclenché par l'utilisateur (tel qu'un clic ou une pression de touche). Notre code est généralement attaché comme un rappel: une seule fonction qui est exécutée en réponse à l'événement. size12, size14 et size16 sont désormais des fonctions qui redimensionneront le corps du texte à 12, 14 et 16 pixels, respectivement. Nous pouvons les attacher à des boutons (dans ce cas des liens) comme suit:

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;

Violon


Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la manière et / ou la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
Donald Duck

1
cet exemple, il me semble, pourrait être implémenté sans fermeture via une fonction standard. J'essaie de trouver un exemple de quelque chose qui NE POURRAIT PAS être mis en œuvre sans fermeture
Zach Smith

0

Les fermetures sont un moyen utile de créer , une séquence incrémentée à la demande:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

Les différences sont résumées comme suit:

Fonctions anonymes Fonctions définies

Ne peut pas être utilisé comme méthode Peut être utilisé comme méthode d'un objet

Existe uniquement dans l'étendue dans laquelle il est défini Existe dans l'objet dans lequel il est défini

Ne peut être appelé que dans la portée dans laquelle il est défini Peut être appelé à tout moment dans le code

Peut être réaffecté une nouvelle valeur ou supprimé Ne peut pas être supprimé ou modifié

Références


0

Dans l'échantillon donné, la valeur de la variable incluse «compteur» est protégée et ne peut être modifiée qu'en utilisant les fonctions données (incrémenter, décrémenter). parce que c'est dans une fermeture,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

Expliquer l'utilisation pratique d'une fermeture en JavaScript ---

Lorsque nous créons une fonction à l'intérieur d'une autre fonction, nous créons une fermeture. Les fermetures sont puissantes car elles sont capables de lire et de manipuler les données de ses fonctions externes. Chaque fois qu'une fonction est invoquée, une nouvelle étendue est créée pour cet appel. La variable locale déclarée à l'intérieur de la fonction appartient à cette portée et n'est accessible qu'à partir de cette fonction. Lorsque la fonction a terminé l'exécution, la portée est généralement détruite.

Un exemple simple d'une telle fonction est le suivant:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

Dans l'exemple ci-dessus, la fonction buildName () déclare un message d'accueil de variable locale et le renvoie. Chaque appel de fonction crée une nouvelle portée avec une nouvelle variable locale. Une fois la fonction exécutée, nous n'avons plus aucun moyen de faire à nouveau référence à cette étendue, c'est donc une récupération de place.

Mais qu'en est-il lorsque nous avons un lien avec cette portée?

Regardons la fonction suivante:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

La fonction sayName () de cet exemple est une fermeture. La fonction sayName () a sa propre portée locale (avec la variable welcome) et a également accès à la portée de la fonction externe (englobante). Dans ce cas, la variable salutation de buildName ().

Une fois l'exécution de buildName terminée, la portée n'est pas détruite dans ce cas. La fonction sayMyName () y a toujours accès, elle ne sera donc pas récupérée. Cependant, il n'y a pas d'autre moyen d'accéder aux données depuis le périmètre externe, sauf la fermeture. La fermeture sert de passerelle entre le contexte mondial et la portée extérieure.


0

J'essaie d'apprendre les clousures et je pense que l'exemple que j'ai créé est un cas d'utilisation pratique. Vous pouvez exécuter l'extrait de code et voir le résultat dans la console. nous avons deux utilisateurs distincts qui ont des données distinctes. Chacun d'eux peut voir l'état réel et le mettre à jour.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.