La fermeture est un concept de programmation fonctionnelle apparu dans les années 1960. Le premier langage à implémenter la fermeture était Scheme, qui est un dialecte de LISP. Depuis lors, les fonctionnalités de fermeture ont été largement adoptées par d’autres langages.
La définition stricte d'une fermeture est "un ensemble composé d'une fonction (environnement) et de ses variables libres incluses". Cette définition est un peu obscure pour tout le monde, alors expliquons-la d'abord à travers des exemples et une explication moins stricte est une fermeture, puis donne des exemples de quelques utilisations classiques des fermetures.
Qu'est-ce que la fermeture
En termes simples, chaque fonction en JavaScript est une fermeture, mais en général, les fonctions imbriquées peuvent mieux refléter
Pour montrer les caractéristiques de fermeture, veuillez consulter l'exemple suivant :
var generateClosure = function() { var count = 0; var get = function() { count ++; return count; }; return get; }; var counter = generateClosure(); console.log(counter()); // 输出 1 console.log(counter()); // 输出 2 console.log(counter()); // 输出 3
Dans ce code, il y a un nombre de variables locales dans la fonction generateClosure(), avec une valeur initiale de 0. Il existe également une fonction appelée get, qui augmente la variable count dans sa portée parent, la fonction generateClosure(), de 1 et renvoie la valeur de count. La valeur de retour de generateClosure() est la fonction get. En externe, nous avons appelé la fonction generateClosure() via la variable counter et obtenu sa valeur de retour, qui est la fonction get. Ensuite, nous avons appelé à plusieurs reprises counter() et nous avons constaté que la valeur renvoyée était incrémentée de 1 à chaque fois.
Jetons un coup d'œil aux caractéristiques de l'exemple ci-dessus. Selon la compréhension habituelle de la programmation impérative, count est une variable à l'intérieur de la fonction generateClosure. Son cycle de vie est la période pendant laquelle generateClosure est appelé. la variable count L'espace appliqué sera libéré. Le problème est qu'après la fin de l'appel à generateClosure(), counter() fait référence à la variable count "déjà publiée", et non seulement aucune erreur ne se produit, mais count est modifié et renvoyé à chaque fois que counter() est appelé. Que se passe-t-il?
C’est exactement la caractéristique de ce qu’on appelle la fermeture. Lorsqu'une fonction renvoie une fonction définie en son sein, une fermeture est générée. La fermeture inclut non seulement la fonction renvoyée, mais également l'environnement dans lequel la fonction est définie. Dans l'exemple ci-dessus, lorsque la fonction interne get de la fonction generateClosure() est référencée par une variable externe counter, les variables locales de counter et generateClosure() sont une fermeture. Si ce n'est pas assez clair, l'exemple suivant peut vous aider
Vous comprenez :
var generateClosure = function() { var count = 0; var get = function() { count ++; return count; }; return get; }; var counter1 = generateClosure(); var counter2 = generateClosure(); console.log(counter1()); // 输出 1 console.log(counter2()); // 输出 1 console.log(counter1()); // 输出 2 console.log(counter1()); // 输出 3 console.log(counter2()); // 输出 2
L'exemple ci-dessus explique comment les fermetures sont générées : counter1 et counter2 appellent respectivement la fonction generateClosure(), générant deux instances de fermetures, et les variables de comptage qu'ils référencent en interne appartiennent à leurs environnements d'exploitation respectifs. Nous pouvons comprendre que lorsque generateClosure() renvoie la fonction get, les variables internes de la fonction generateClosure() auxquelles get peut faire référence (c'est-à-dire la variable count) sont également renvoyées en privé, et une copie est générée dans la mémoire, et then generateClosure( ) Les deux instances de la fonction renvoyée, counter1 et counter2, sont indépendantes l'une de l'autre.
Le but de la fermeture
1. Fonction de rappel imbriquée
Les fermetures ont deux utilisations principales, l'une consiste à implémenter des fonctions de rappel imbriquées et l'autre à masquer les détails d'un objet. Examinons d'abord l'exemple de code suivant pour comprendre les fonctions de rappel imbriquées. Le code suivant utilise MongoDB dans Node.js pour implémenter une fonction simple d'ajout d'utilisateurs :
exports.add_user = function(user_info, callback) { var uid = parseInt(user_info['uid']); mongodb.open(function(err, db) { if (err) {callback(err); return;} db.collection('users', function(err, collection) { if (err) {callback(err); return;} collection.ensureIndex("uid", function(err) { if (err) {callback(err); return;} collection.ensureIndex("username", function(err) { if (err) {callback(err); return;} collection.findOne({uid: uid}, function(err) { if (err) {callback(err); return;} if (doc) { callback('occupied'); } else { var user = { uid: uid, user: user_info, }; collection.insert(user, function(err) { callback(err); }); } }); }); }); }); }); };
Si vous n'êtes pas familier avec Node.js ou MongoDB, cela n'a pas d'importance. Vous n'avez pas besoin de comprendre les détails, comprenez simplement la logique générale. Ce code utilise des couches d'imbrication de fermetures, et chaque couche d'imbrication est une fonction de rappel. La fonction de rappel ne sera pas exécutée immédiatement, mais sera rappelée par la fonction demandée après le traitement de la requête correspondante. Nous pouvons voir qu'il existe une référence au rappel dans chaque couche d'imbrication, et la couche la plus interne utilise également la variable uid définie par la couche externe. En raison de l'existence du mécanisme de fermeture, même si la fonction externe a été exécutée, les variables appliquées dans sa portée ne seront pas libérées, car la fonction interne peut toujours référencer ces variables, réalisant ainsi parfaitement le rappel asynchrone imbriqué.
2. Implémenter les membres privés
Nous savons que les objets JavaScript n'ont pas de propriétés privées, ce qui signifie que chaque propriété de l'objet est exposée au monde extérieur. Cela peut engendrer des risques de sécurité, par exemple si l'utilisateur de l'objet modifie directement un attribut, entraînant la destruction de la cohérence des données internes de l'objet, etc. JavaScript utilise la convention pour placer un trait de soulignement avant toutes les propriétés privées (telles que _myPrivateProp) pour indiquer que cette propriété est privée et que les objets externes ne doivent pas la lire ou l'écrire directement. Mais il ne s’agit que d’un accord informel. En supposant que l’utilisateur de l’objet ne le fasse pas, existe-t-il un mécanisme plus strict ? La réponse est oui, cela peut être réalisé grâce à des fermetures. Reprenons l'exemple précédent :
var generateClosure = function() { var count = 0; var get = function() { count ++; return count; }; return get; }; var counter = generateClosure(); console.log(counter()); // 输出 1 console.log(counter()); // 输出 2 console.log(counter()); // 输出 3
Kita dapat melihat bahawa hanya memanggil counter() boleh mengakses pembolehubah kiraan dalam penutupan dan menambahnya sebanyak 1 mengikut peraturan Tidak mustahil untuk mencari pembolehubah kiraan dengan cara lain. Diilhamkan oleh contoh mudah ini, kita boleh merangkum objek dengan penutupan dan hanya mengembalikan objek "aksesor" untuk menyembunyikan butiran.
Di atas ialah keseluruhan kandungan artikel ini, saya harap ia dapat membantu semua orang mempelajari dan memahami penutupan JavaScript dengan lebih baik.