Cet article explorera en profondeur trois concepts cruciaux dans le développement de JavaScript moderne: fermetures, fonctions de rappel et expressions des fonctions d'exécution immédiate (iife). Nous avons appris en détail la portée et l'amélioration variables, et maintenant, terminons notre parcours d'exploration.
Points de base
Clôture
Dans JavaScript, une fermeture est toute fonction qui conserve des références à sa variable de portée parent, même si la fonction parent a renvoyé .
En fait, n'importe quelle fonction peut être considérée comme une fermeture, car comme nous l'avons appris dans la section de portée variable de la première partie de ce tutoriel, une fonction peut être référencée ou consultée:
point 1: Vous pouvez vous référer à des variables définies en dehors de la fonction actuelle.
Dans cet exemple de code, la fonction
se réfère au paramètrefunction setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } printLocation(); } setLocation("Paris"); // 输出:You are in Paris, France
de la fonction enfermée (parent) printLocation()
. En conséquence, lorsque setLocation()
est appelé, country
produit avec succès "Vous êtes à Paris, en France" en utilisant les anciennes variables et paramètres. city
setLocation()
Point 2: printLocation()
Les fonctions internes peuvent se référer à des variables définies dans les fonctions externes même après le retour de la fonction externe.
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } printLocation(); } setLocation("Paris"); // 输出:You are in Paris, France
Ceci est presque le même que le premier exemple, sauf que cette fois printLocation()
renvoie dans la fonction en dehors de la fonction setLocation()
, plutôt que d'appeler immédiatement. Par conséquent, la valeur de currentLocation
est la fonction interne printLocation()
.
si nous rappelons comme ça currentLocation
- alert(currentLocation);
- nous obtiendrons la sortie suivante:
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } return printLocation; } var currentLocation = setLocation("Paris"); currentLocation(); // 输出:You are in Paris, France
Comme nous l'avons vu, printLocation()
est exécuté en dehors de sa portée lexicale. setLocation()
semble disparaître, mais printLocation()
peut toujours accéder et "se souvenir" de ses variables (country
) et des paramètres (city
).
La fermeture (fonction intérieure) est capable de se souvenir de la portée autour de lui (fonction externe) même si elle s'exécute en dehors de sa portée lexicale. Vous pouvez donc l'appeler plus tard à tout moment dans votre programme.
point 3: Les fonctions internes stockent des variables qui sont des fonctions externes par référence, plutôt que par des valeurs.
function printLocation () { console.log("You are in " + city + ", " + country); }
Ici cityLocation()
Renvoie un objet contenant deux fermetures - get()
et set()
- se référant à des variables externes city
. get()
obtient la valeur actuelle de city
et set()
le met à jour. Lorsque le deuxième appel myLocation.get()
, il publie une valeur (actuelle) mise à jour de city
- "Sydney" - au lieu du "Paris" par défaut.
Par conséquent, les fermetures peuvent à la fois lire et mettre à jour leurs variables stockées, et ces mises à jour sont visibles pour toute fermeture qui y accède. Cela signifie que la fermeture stocke une référence à sa variable externe au lieu de copier sa valeur. C'est un point très important, car ne pas savoir que cela peut conduire à certaines erreurs logiques difficiles à trouver - comme nous le verrons dans la section "Exécuter les expressions de fonction immédiatement (iife)". Une caractéristique intéressante de
La fermeture est que les variables de la fermeture sont automatiquement cachées. Les fermetures stockent les données dans leurs variables fermées sans fournir un moyen d'y accéder directement. La seule façon de modifier ces variables est d'y accéder indirectement. Par exemple, dans le dernier extrait de code, nous voyons que nous ne pouvons modifier indirectement la variable en utilisant get()
et set()
fermetures. city
Comme vous pouvez le voir, il n'y a rien de mystérieux ou de profond autour de la fermeture - juste trois points simples à retenir.
Fonction de rappel
En JavaScript, les fonctions sont des citoyens de première classe. L'une des conséquences de ce fait est que les fonctions peuvent être adoptées comme arguments à d'autres fonctions ou renvoyées par d'autres fonctions.Les fonctions qui prennent d'autres fonctions comme paramètres ou fonctions de retour comme leurs résultats sont appelées fonctions d'ordre supérieur, et les fonctions transmises comme paramètres sont appelées fonctions de rappel. Il s'appelle un "rappel" car à un moment donné, il sera "rappel" par une fonction d'ordre supérieur.
Les fonctions de rappel ont de nombreuses utilisations quotidiennes. L'un d'eux est lorsque nous utilisons les méthodes setTimeout()
et setInterval()
de l'objet de fenêtre du navigateur - ces méthodes acceptent et exécutent la fonction de rappel:
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } printLocation(); } setLocation("Paris"); // 输出:You are in Paris, France
Un autre exemple est lorsque nous joignons un écouteur d'événements à un élément sur la page. Ce faisant, nous fournissons en fait un pointeur de la fonction de rappel, qui sera appelé lorsque l'événement se produira.
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } return printLocation; } var currentLocation = setLocation("Paris"); currentLocation(); // 输出:You are in Paris, France
La façon la plus simple de comprendre comment les fonctions d'ordre supérieur et les fonctions de rappel fonctionnent est de créer vos propres fonctions d'ordre supérieur et fonctions de rappel. Alors, créons un maintenant:
function printLocation () { console.log("You are in " + city + ", " + country); }
Ici, nous créons une fonction fullName()
, qui accepte trois paramètres - deux pour le premier et le nom de famille, et un pour la fonction de rappel. Ensuite, après l'instruction console.log()
, nous plaçons un appel de fonction qui déclenchera la fonction de rappel réelle - la fonction fullName()
définie ci-dessous greeting()
. Enfin, nous appelons fullName()
où greeting()
est passé comme une variable - sans crochets - parce que nous ne voulons pas qu'il soit exécuté immédiatement, mais nous voulons simplement le pointer pour une utilisation ultérieure par fullName()
.
Nous passons des définitions de fonction, pas des appels de fonction. Cela empêche l'exécution de la fonction de rappel immédiatement, ce qui est incompatible avec la philosophie derrière la fonction de rappel. Passé comme définitions de fonction, ils peuvent être exécutés à tout moment et à tout moment de la fonction de contenu. De plus, parce que les fonctions de rappel se comportent comme si elles étaient réellement placées à l'intérieur de la fonction, ce sont en fait des fermetures: ils peuvent accéder aux variables et aux paramètres contenant la fonction, et même à accéder aux variables dans la portée globale.
La fonction de rappel peut être une fonction existante (comme indiqué dans l'exemple précédent) ou une fonction anonyme.
Les fonctions de rappelfunction cityLocation() { var city = "Paris"; return { get: function() { console.log(city); }, set: function(newCity) { city = newCity; } }; } var myLocation = cityLocation(); myLocation.get(); // 输出:Paris myLocation.set('Sydney'); myLocation.get(); // 输出:Sydney
Supposons que nous ayons besoin de deux fonctions - l'une qui imprime les informations de l'article publié et l'autre qui imprime les informations sur le message envoyé. Nous les avons créés, mais nous avons remarqué qu'une partie de notre logique est répétée dans les deux fonctions. Nous savons qu'avoir le même morceau de code en un seul endroit n'est pas nécessaire et difficile à entretenir. Alors, quelle est la solution? Illustrons-le dans l'exemple suivant:
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } printLocation(); } setLocation("Paris"); // 输出:You are in Paris, France
Ce que nous faisons ici est de mettre les modèles de code en double (console.log(item)
et var date = new Date()
) dans une fonction générale distincte (publish()
) et conserve uniquement des données spécifiques dans d'autres fonctions - celles-ci sont maintenant une fonction de rappel. De cette façon, en utilisant la même fonction, nous pouvons imprimer des informations pertinentes sur diverses choses connexes - messages, articles, livres, magazines, etc. La seule chose que vous devez faire est de créer une fonction de rappel spéciale pour chaque type et de la transmettre comme argument à la fonction publish()
.
Exécuter les expressions de fonction immédiatement (iife)
Exécuter immédiatement l'expression de la fonction, ou iife (prononcé "ify"), est une expression de fonction (nommée ou anonyme) qui est exécutée immédiatement après sa création.
Il existe deux variantes de syntaxe légèrement différentes de ce mode:
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } return printLocation; } var currentLocation = setLocation("Paris"); currentLocation(); // 输出:You are in Paris, France
Pour convertir une fonction régulière en iife, vous devez effectuer deux étapes:
trois autres choses à retenir:
Tout d'abord, si vous attribuez une fonction à une variable, vous n'avez pas besoin de renfermer la fonction entière entre parenthèses, car c'est déjà une expression:
function printLocation () { console.log("You are in " + city + ", " + country); }
Deuxièmement, l'Iife se termine par un point-virgule, sinon votre code peut ne pas fonctionner correctement.
Troisièmement, vous pouvez transmettre des paramètres à iife (c'est une fonction après tout), comme indiqué dans l'exemple suivant:
function cityLocation() { var city = "Paris"; return { get: function() { console.log(city); }, set: function(newCity) { city = newCity; } }; } var myLocation = cityLocation(); myLocation.get(); // 输出:Paris myLocation.set('Sydney'); myLocation.get(); // 输出:Sydney
passer un objet global en tant que paramètre à iife est un modèle commun pour y accéder à l'intérieur d'une fonction sans utiliser un objet window
, ce qui rend le code indépendant de l'environnement du navigateur. Le code suivant crée une variable global
qui fera référence à l'objet global quelle que soit la plate-forme que vous utilisez:
function showMessage(message) { setTimeout(function() { alert(message); }, 3000); } showMessage('Function called 3 seconds ago');
Ce code fonctionne dans le navigateur (l'objet global est window
) ou dans l'environnement Node.js (nous utilisons une variable spéciale global
pour se référer à l'objet global).
L'un des grands avantages de l'Iife est que lorsque vous l'utilisez, vous n'avez pas à vous soucier de contaminer l'espace global avec des variables temporaires. Toutes les variables que vous définissez à l'intérieur de l'Iife seront locales. Vérifions-le:
<!-- HTML --> <button id="btn">Click me</button> <!-- JavaScript --> function showMessage() { alert('Woohoo!'); } var el = document.getElementById("btn"); el.addEventListener("click", showMessage);
Dans cet exemple, la première instruction console.log()
fonctionne bien, mais la deuxième instruction échoue parce que les variables today
et currentTime
deviennent des variables locales dues à iife.
Nous savons déjà que les fermetures conservent des références aux variables externes, afin qu'elles renvoient les valeurs les plus récentes / mises à jour. Alors, quelle est votre sortie de l'exemple suivant?
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } printLocation(); } setLocation("Paris"); // 输出:You are in Paris, France
Vous pouvez vous attendre à ce que le nom du fruit soit imprimé un par un à un intervalle de secondes. Cependant, en fait, la sortie est quatre fois "non définie". Alors, quel est le problème?
Le problème est que dans l'instruction, la valeur de console.log()
est égale à 4 pour chaque itération de la boucle. Et, comme nous n'avons rien à indexer 4 dans le tableau i
, la sortie est "non définie". (N'oubliez pas que dans JavaScript, l'index du tableau commence à 0.) Lorsque fruits
est égal à 4, la boucle se termine. i
. Nous le faisons en désactivant la méthode i
dans iife et en définissant une variable privée pour maintenir la copie actuelle de setTimeout()
. i
function setLocation(city) { var country = "France"; function printLocation() { console.log("You are in " + city + ", " + country); } return printLocation; } var currentLocation = setLocation("Paris"); currentLocation(); // 输出:You are in Paris, France
function printLocation () { console.log("You are in " + city + ", " + country); }
Conclusion
Le but de ce tutoriel est d'introduire ces concepts de base aussi clairement et concise que possible - en tant que simple ensemble de principes ou de règles. Une bonne compréhension d'eux est la clé pour devenir un développeur JavaScript réussi et efficace.Pour expliquer le sujet présenté ici plus en détail et en profondeur, je vous suggère de lire "Vous ne savez pas JS: Scopes et fermetures" de Kyle Simpson.
(Le contenu ultérieur, à savoir la partie FAQ, a été omis en raison de la durée de l'article. Si nécessaire, veuillez poser des questions spécifiques.)
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!