Les fermetures sont souvent considérées comme un art mystérieux au pays de JavaScript. Une fois maîtrisés, ils vous permettent d’écrire du code JavaScript vraiment étonnant. Cet article vous familiarisera avec la magie des fermetures de JavaScript.
L'une des vérités clés de JavaScript est que tout est un objet. Cela inclut bien sûr des fonctions.
Une fermeture n'est rien d'autre qu'un objet de fonction avec une portée associée dans laquelle les variables de la fonction sont résolues.
Les bouchons portent leur nom à cause de la façon dont ils Fermer sur leur contenu. Considérez le code JavaScript suivant:
topping = "anchovi"; function pizzaParty (numSlices) var topping = "pepperoni", innerFunction = function () var topping = "jambon"; console.log ("… mais mettez" + topping + "sur" + numSlices + "slices"); ; console.log ("Cette pizza est tout au sujet de" + garniture); fonction intérieure (); pizzaParty (3);
Si vous ouvrez votre console favorite et dirigez ce méchant garçon, vous recevrez un message délicieux: "Cette pizza est une histoire de pepperoni ... Mais mettez du jambon sur 3 tranches." Cet exemple illustre quelques concepts clés de JavaScript indispensables à la maîtrise des fermetures..
Combien d'objets de fonction sont dans le code ci-dessus? Bien… nous avons notre soirée pizza
fonction, et imbriquée dans cette fonction est fonction intérieure
. Les maths n'ont pas toujours été mon point fort, mais 1 + 1 = 2
dans mon livre. Chaque objet de fonction a son propre ensemble de variables, qui sont résolues dans les fonctions de chaque fonction. portée.
Les fermetures ne peuvent être entièrement comprises sans une base solide. Le mécanisme de portée de JavaScript est ce qui permet à chaque fonction d'avoir sa propre Garniture
variable, et sans cela nous pourrions avoir trop de pepperoni, trop peu de jambon, ou * halètement *… quelques anchois à notre soirée pizza. Utilisons une illustration rapide pour mieux illustrer cette idée.
Les fonctions sont exécutées à l'aide de la portée qui était en vigueur lors de la définition de la fonction. Cela n’a rien à voir avec la portée en vigueur lorsque la fonction est appelée.
Les flèches vertes indiquent que l'accessibilité fonctionne de l'extérieur vers l'intérieur. Les variables définies dans l'étendue en dehors d'une fonction sont accessibles de l'intérieur..
Si nous devions omettre le Garniture
variable de l'intérieur du soirée pizza
fonction, alors nous aurions un message comme "Cette pizza est tout au sujet de l'anchovi", mais depuis soirée pizza
a un Garniture
variable dans sa propre portée; ces ventouses salées ne s'approcheront jamais de notre soirée pizza.
De même, le numSlices
paramètre accessible de l'intérieur fonction intérieure
car il est défini dans le champ d'application ci-dessus - dans ce cas, le champ soirée pizza
.
Les flèches rouges indiquent que les variables entrant dans le champ d'une fonction ne sont jamais accessibles en dehors de cette fonction. C'est le cas uniquement lorsqu'une variable remplit l'une des conditions suivantes:
var
le mot clé est utilisé. Omettant le var
Lorsque vous définissez une variable, le mot-clé amène JavaScript à définir la variable nommée la plus proche dans les fonctions externes jusqu’à la portée globale. Donc, en utilisant notre exemple, le jambon Garniture
dans fonction intérieure
ne peut pas être consulté à partir de soirée pizza
, et le pepperoni Garniture
dans soirée pizza
ne peut pas être consulté dans la portée globale où habite anchovi.
Portée lexicale signifie que les fonctions sont exécutées à l'aide de la portée variable en vigueur au moment défini. Cela n'a rien à voir avec la portée en vigueur lorsque la fonction est appelée. Ce fait est crucial pour libérer le pouvoir des fermetures.
Maintenant que nous comprenons ce qu'est une fermeture et ce que cela signifie pour les fermetures, passons à quelques cas d'utilisation classiques..
Les fermetures sont la moyen de dissimuler votre code à l’œil du public. Avec les fermetures, vous pouvez facilement avoir des membres privés protégés du monde extérieur:
(function (exports) function myPrivateMultiplyFunction (num, num2) return num * num2; // équivalent à window.multiply = fonction (num1, num2) … exports.multiply = fonction (num1, num2) console.log (myPrivateMultiplyFunction (num1, num2));) (fenêtre);
Avec les fermetures, vous pouvez facilement avoir des membres privés protégés du monde extérieur.
Faisons le décomposer. Notre objet de fonction de niveau supérieur est une fonction anonyme:
(fonction (exports) ) (fenêtre);
Nous appelons cette fonction anonyme tout de suite. Nous passons le contexte global (la fenêtre
dans ce cas) pour pouvoir "exporter" une fonction publique, mais masquer tout le reste. Parce que la fonction myPrivateMultiplyFunction
est une fonction imbriquée, elle n’existe que dans le cadre de notre fermeture; afin que nous puissions l'utiliser n'importe où dans cette portée, et seulement dans cette portée.
JavaScript tiendra une référence à notre fonction privée pour une utilisation à l'intérieur de la fonction multiply, mais myPrivateMultiplyFunction
ne peut pas être consulté en dehors de la fermeture. Essayons ceci:
multiplier (2,6) // => 12 myPrivateMultiplyFunction (2,6) // => ReferenceError: myPrivateMultiplyFunction n'est pas défini
La fermeture nous a permis de définir une fonction à usage privé, tout en nous permettant de contrôler ce que le reste du monde voit. Que peuvent faire les fermetures??
Les fermetures sont assez utiles pour générer du code. Fatigué de vous souvenir de tous ces codes de clés embêtants pour les événements de clavier? Une technique courante consiste à utiliser une carte de clé:
var KeyMap = "Entrée": 13, "Shift": 16, "Onglet": 9, "LeftArrow": 37;
Ensuite, dans notre événement de clavier, nous voulons vérifier si une certaine touche a été enfoncée:
var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = fonction (e) code var = e.keyCode || e.which // tarif habituel pour obtenir la touche enfoncée if (code === KeyMap.Enter) console.log (txtInput.value);
L'exemple ci-dessus n'est pas le pire, mais nous pouvons utiliser la méta-programmation et les fermetures pour créer une solution encore meilleure. En utilisant notre existant KeyMap
objet, nous pouvons générer des fonctions utiles:
for (clé var dans KeyMap) // objet d'accès avec accesseur de tableau pour définir le nom de la fonction "dyanamic" KeyMap ["est" + clé] = (fonction (comparer) return fonction (ev) var code = ev.keyCode | | ev.which; code retour === comparer;) (KeyMap [key]);
Les fermetures sont si puissantes car elles peuvent capturer la variable locale et les liaisons de paramètres de la fonction dans laquelle elles sont définies.
Cette boucle génère un est
fonction pour chaque clé KeyMap
, et notre txtInput.onkeypress
la fonction devient un peu plus lisible:
var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = fonction (e) if (KeyMap.isEnter (e)) console.log (txtInput.value);
La magie commence ici:
KeyMap ["est" + key] = (fonction (comparer) ) (KeyMap [clé]); // invoque immédiatement et passe la valeur actuelle à KeyMap [key]
Comme nous passons en boucle sur les clés KeyMap
, nous passons la valeur référencée par cette clé à la fonction externe anonyme et l'invoquons immédiatement. Ceci lie cette valeur à la comparer
paramètre de cette fonction.
La fermeture qui nous intéresse est celle que nous revenons de l'intérieur de la fonction anonyme:
fonction de retour (ev) var code = ev.keyCode || ev.which; code retour === comparer;
N'oubliez pas que les fonctions sont exécutées avec la portée qui était en place lors de leur définition. le comparer
paramètre est lié à la KeyMap
valeur qui était en place lors d'une itération de boucle, notre fermeture imbriquée est donc capable de la capturer. Nous prenons un instantané dans le temps de la portée qui était en vigueur à ce moment-là.
Les fonctions que nous avons créées nous permettent d’ignorer la configuration du code
variable à chaque fois que nous voulons vérifier le code de la clé, et nous avons maintenant des fonctions pratiques et lisibles à utiliser.
À ce stade, il devrait être relativement facile de voir que les fermetures sont essentielles à l’écriture de JavaScript de qualité supérieure. Appliquons ce que nous savons sur les fermetures à l’augmentation de l’un des types natifs de JavaScript (gasp!). Avec notre focus sur les objets de fonction, augmentons le natif Une fonction
type:
Function.prototype.cached = function () var self = this, // "this" fait référence à la fonction d'origine cache = ; // notre fonction de retour de mémoire cache locale au périmètre lexique (args) if (args dans la mémoire cache) return cache [args]; cache de retour [args] = self (args); ; ;
Ce petit bijou permet à n’importe quelle fonction de créer sa propre version en cache. Vous pouvez voir que la fonction retourne une fonction elle-même, donc cette amélioration peut être appliquée et utilisée comme suit:
Math.sin = Math.sin.cached (); Math.sin (1) // => 0.8414709848078965 Math.sin (1) // => 0.8414709848078965 cette fois extrait de la mémoire cache
Notez les compétences de fermeture qui entrent en jeu. Nous avons un local cache
variable qui est gardée privée et protégée du monde extérieur. Cela empêchera toute falsification qui pourrait invalider notre cache.
La fermeture renvoyée a accès aux liaisons de la fonction externe, ce qui signifie que nous sommes en mesure de renvoyer une fonction avec un accès complet au cache à l'intérieur, ainsi que la fonction d'origine! Cette petite fonction peut faire des merveilles pour la performance. Cette extension particulière est configurée pour gérer un argument, mais j'aimerais voir votre coup de poignard à une fonction de cache à plusieurs arguments.
En prime, examinons quelques utilisations des fermetures dans la nature.
Parfois, le fameux jQuery $
l’usine n’est pas disponible (pensez à WordPress) et nous voulons l’utiliser comme nous le faisons habituellement. Plutôt que d’atteindre jQuery.noConflict
, nous pouvons utiliser une fermeture pour permettre aux fonctions internes d'accéder à notre $
liaison de paramètre.
(function ($) $ (document) .ready (function () // comme d'habitude…);) (jQuery);
Sur les grands projets Backbone.js, il peut s'avérer utile de garder vos modèles d'application privés, puis d'exposer une API publique sur la vue de votre application principale. En utilisant une fermeture, vous pouvez facilement obtenir cette confidentialité.
(fonction (exportations) var Product = Backbone.Model.extend (urlRoot: '/ products',); var ProductList = Backbone.Collection.extend (url: '/ products', modèle: Product); var Products = new ProductList; var ShoppingCartView = Backbone.View.extend (addProduct: function (produit, opté) rendu CartItems.create (produit, opté);, removeProduct: fonction (produit, opté) Produits.remove (produit , opts);, getProduct: function (productId) return Products.get (productId);, getProducts: function () return Products.models;); // exporte uniquement la vue principale de l'application exports.ShoppingCart = nouveau ShoppingCartView;) (fenêtre);
Un récapitulatif rapide de ce que nous avons appris:
Merci beaucoup d'avoir lu! N'hésitez pas à poser des questions. Maintenant profitons de la soirée pizza!