Nous souhaitons rendre ce projet open source disponible pour les gens du monde entier.
Aidez-nous à traduire le contenu de ce tutoriel dans votre langue!
Variable scope, closure
JavaScript est un langage orienté vers le fonctionnel. Cela nous donne beaucoup de liberté. Une fonction peut être créée dynamiquement, passée en argument à une autre fonction et appelée ultérieurement à partir d’un code totalement différent.
Nous savons déjà qu’une fonction peut accéder à des variables en dehors de celle-ci (variables externes).
Mais que se passe-t-il si les variables externes changent depuis la création d’une fonction ? La fonction obtiendra-t-elle des valeurs plus récentes ou les anciennes ?
Et si une fonction est transmise en tant que paramètre et appelée depuis un autre endroit du code, aura-t-elle accès aux variables externes au nouvel endroit ?
Développons maintenant nos connaissances pour inclure des scénarios plus complexes.
let/constEn JavaScript, il y a 3 façons de déclarer une variable : let, const (les modernes) et var (le vestige du passé).
- Dans cet article, nous utiliserons des variables
letdans les exemples. - Les variables, déclarées avec
const, se comportent de la même manière, donc cet article concerne égalementconst. - L’ancien
vara quelques différences notables, elles seront traitées dans l’article L'ancien "var".
Code blocks
Si une variable est déclarée à l’intérieur d’un bloc de code {...}, elle n’est visible qu’à l’intérieur de ce bloc.
Par exemple :
Nous pouvons l’utiliser pour isoler un morceau de code qui fait sa propre tâche, avec des variables qui lui appartiennent uniquement :
Pour if, for, while et ainsi de suite, les variables déclarées dans {...} ne sont également visibles qu’à l’intérieur :
Ici, après la fin du if, l’alert ci-dessous ne verra pas phrase, d’où l’erreur.
C’est super, car cela nous permet de créer des variables locales, spécifiques à une branche if.
La même chose vaut pour les boucles for et while :
Visuellement, let i est à l’extérieur de {...}. Mais la construction de for est spéciale ici : la variable déclarée à l’intérieur est considérée comme faisant partie du bloc.
Fonctions imbriquées
Une fonction est appelée “imbriquée” lorsqu’elle est créée dans une autre fonction.
Il est possible de faire cela facilement avec JavaScript.
Nous pouvons l’utiliser pour organiser notre code, comme ceci :
function sayHiBye(firstName, lastName) {
// helper nested function to use below
function getFullName() {
return firstName + " " + lastName;
}
alert( "Hello, " + getFullName() );
alert( "Bye, " + getFullName() );
}
Ici, la fonction imbriquée getFullName() est faite pour plus de commodité. Elle peut accéder aux variables externes et peut donc renvoyer le nom complet. Les fonctions imbriquées sont assez courantes dans JavaScript.
Ce qui est beaucoup plus intéressant, une fonction imbriquée peut être retournée : soit en tant que propriété d’un nouvel objet, soit en tant que résultat par elle-même. Elle peut ensuite être utilisée ailleurs. Peu importe où, elle a toujours accès aux mêmes variables externes.
Ci-dessous, makeCounter crée la fonction “counter” qui renvoie le nombre suivant à chaque appel :
Bien que simples, des variantes légèrement modifiées de ce code ont des utilisations pratiques, par exemple un générateur de nombres aléatoires pour générer des valeurs aléatoires pour des tests automatisés.
Comment cela marche-t-il ? Si nous créons plusieurs compteurs, seront-ils indépendants ? Que se passe-t-il avec les variables ici ?
La compréhension de ce genre de choses est excellente pour la connaissance globale de JavaScript et bénéfique pour les scénarios plus complexes. Allons donc un peu en profondeur.
Lexical Environment
L’explication technique approfondie reste à venir.
Bien que je souhaiterai éviter les détails de bas niveau du langage, toute compréhension sans eux serait manquante et incomplète, alors préparez-vous.
Pour plus de clarté, l’explication est divisée en plusieurs étapes.
Étape 1. Variables
En JavaScript, chaque fonction en cours d’exécution, bloc de code {...} et le script dans son ensemble ont un objet associé interne (caché) connu sous le nom de Environnement Lexical.
L’objet environnement lexical se compose de deux parties :
- Environment Record – un objet qui stocke toutes les variables locales comme ses propriétés (et quelques autres informations comme la valeur de
this). - Une référence à l’environnement lexical externe, celui associé au code externe.
Une “variable” est juste une propriété de l’objet interne spécial Environment Record. “Pour obtenir ou modifier une variable” signifie “pour obtenir ou modifier une propriété de cet objet”.
Dans ce code simple sans fonctions, il n’y a qu’un seul environnement lexical :
Il s’agit de l’environnement Lexical dit global, associé à l’ensemble du script.
Sur l’image ci-dessus, le rectangle signifie Environment Record (stockage de variable) et la flèche signifie la référence externe. L’environnement lexical global n’a pas de référence externe, c’est pourquoi la flèche pointe vers null.
À mesure que le code commence à s’exécuter et se poursuit, l’environnement lexical change.
Voici un peu plus de code :
Les rectangles sur le côté droit montrent comment l’environnement lexical global change pendant l’exécution :
- Lorsque le script démarre, l’environnement lexical est prérempli avec toutes les variables déclarées.
- Initialement, elles sont à l’état “non initialisé”. C’est un état interne spécial, cela signifie que le moteur connaît la variable, mais elle ne peut pas être référencée tant qu’elle n’a pas été déclarée avec
let. C’est presque la même chose que si la variable n’existait pas.
- Initialement, elles sont à l’état “non initialisé”. C’est un état interne spécial, cela signifie que le moteur connaît la variable, mais elle ne peut pas être référencée tant qu’elle n’a pas été déclarée avec
- Ensuite, la définition de
let phraseapparaît. Il n’y a pas encore d’affectation, donc sa valeur estundefined. Nous pouvons utiliser la variable depuis ce moment. phrasese voit attribuer une valeur.phrasechange de valeur.
Tout semble simple pour l’instant, non ?
- Une variable est la propriété d’un objet interne spécial, associé au bloc/fonction/script en cours d’exécution.
- Travailler avec des variables, c’est travailler avec les propriétés de cet objet.
“L’environnement lexical” est un objet de spécification : il n’existe que “théoriquement” dans la spécification du langage pour décrire comment les choses fonctionnent. nous ne pouvons pas obtenir cet objet dans notre code et le manipuler directement.
Les moteurs JavaScript peuvent également l’optimiser, supprimer les variables inutilisées pour économiser de la mémoire et effectuer d’autres opérations internes, tant que le comportement visible reste conforme à la description.
Step 2. Fonctions Declarations
Une fonction est également une valeur, comme une variable.
La différence est qu’une fonction déclaration est instantanément et complètement initialisée.
Lorsqu’un environnement lexical est créé, une fonction déclaration devient immédiatement une fonction prête à l’emploi (contrairement à let, qui est inutilisable jusqu’à la déclaration).
C’est pourquoi nous pouvons utiliser une fonction, déclarée comme fonction déclaration, avant même la déclaration elle-même.
Par exemple, voici l’état initial de l’environnement lexical global lorsque nous ajoutons une fonction :
Naturellement, ce comportement ne s’applique qu’aux fonctions déclarations, pas aux fonctions expressions où nous attribuons une fonction à une variable, telle que let say = function(name)....
Step 3. Environnement lexical intérieur et extérieur
Lorsqu’une fonction s’exécute, au début de l’appel, un nouvel environnement lexical est créé automatiquement pour stocker les variables locales et les paramètres de l’appel.
Par exemple, pour say("John"), cela ressemble à ceci (l’exécution est à la ligne, marquée d’une flèche) :
Pendant l’appel de la fonction, nous avons deux environnements lexicaux : l’intérieur (pour l’appel de la fonction) et l’extérieur (global) :
- L’environnement lexical interne correspond à l’exécution actuelle de
say. Il a une seule propriété :name, l’argument de la fonction. Nous avons appelésay("John"), donc la valeur denameest"John". - L’environnement lexical externe est l’environnement lexical global. Il a la variable
phraseet la fonction elle-même.
L’environnement lexical intérieur a une référence à l’environnement outer (extérieur).
Lorsque le code veut accéder à une variable – l’environnement lexical interne est recherché en premier, puis celui externe, puis le plus externe et ainsi de suite jusqu’à celui global.
Si une variable n’est trouvée nulle part, c’est une erreur en mode strict (sans use strict une affectation à une variable non existante crée une nouvelle variable globale, pour la compatibilité avec l’ancien code).
Dans cet exemple, la recherche se déroule comme ceci :
- Pour la variable
name, l’alertà l’intérieur desayla trouve immédiatement dans l’environnement lexical interne. - Lorsqu’elle veut accéder à
phrase, il n’y a pas dephraselocalement, elle suit donc la référence à l’environnement lexical externe et la trouve là.
Step 4. Retourner une fonction
Revenons à l’exemple makeCounter.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
Au début de chaque appel makeCounter(), un nouvel objet environnement lexical est créé, pour stocker les variables pour cette exécution makeCounter.
Nous avons donc deux environnements lexicaux imbriqués, comme dans l’exemple ci-dessus :
Ce qui est différent, c’est que, pendant l’exécution de makeCounter(), une minuscule fonction imbriquée est créée à partir d’une seule ligne : return count++. Nous ne l’exécutons pas encore, nous créons seulement.
Toutes les fonctions se souviennent de l’environnement lexical dans lequel elles ont été créées. Techniquement, il n’y a pas de magie ici : toutes les fonctions ont la propriété cachée nommée [[Environment]], qui garde la référence à l’environnement lexical où la fonction a été créée :
Ainsi, counter.[[Environment]] a la référence à l’environnement lexical {count: 0}. C’est ainsi que la fonction se souvient de l’endroit où elle a été créée, quel que soit son nom. La référence [[Environnement]] est définie une fois pour toutes au moment de la création de la fonction.
Plus tard, lorsque counter() est appelé, un nouvel environnement lexical est créé pour l’appel, et sa référence externe à l’environnement lexical est tirée de counter.[[Environnement]] :
Maintenant, lorsque le code à l’intérieur de counter() recherche la variable count, il recherche d’abord son propre environnement lexical (vide, car il n’y a pas de variables locales), puis l’environnement lexical de l’appel externe makeCounter(), où il la trouve et la change.
Une variable est mise à jour dans l’environnement lexical où elle se trouve.
Voici l’état après l’exécution :
Si nous appelons counter() plusieurs fois, la variable count sera augmentée à 2, 3 et ainsi de suite, au même endroit.
Il existe un terme général de programmation “closure”, que les développeurs devraient généralement connaître.
Une closure est une fonction qui se souvient de ses variables externes et peut y accéder. Dans certains langages, ce n’est pas possible, ou une fonction doit être écrite d’une manière spécifique pour y arriver. Mais comme expliqué ci-dessus, en JavaScript, toutes les fonctions sont naturellement des fermetures (il n’y a qu’une seule exception, à couvrir dans La syntaxe "new Function").
C’est-à-dire : elles se souviennent automatiquement de l’endroit où elles ont été créées en utilisant une propriété cachée [[Environnement]], puis leur code peut accéder aux variables externes.
Lors d’un entretien d’embauche, un développeur frontend reçoit assez souvent une question du genre “qu’est-ce qu’une closure ?”. Une réponse valide serait une définition de la closure ainsi qu’une explication sur le fait que toutes les fonctions en JavaScript sont des closures, et peut-être quelques mots de plus sur les détails techniques : la propriété [[Environment]] et comment fonctionnent les environnements lexicaux.
Garbage collection
Habituellement, un environnement lexical est supprimé de la mémoire avec toutes les variables une fois l’appel de fonction terminé. C’est parce qu’il n’y a plus aucune référence à cela. Comme tout objet JavaScript, il n’est conservé en mémoire que lorsqu’il est accessible.
Cependant, s’il y a une fonction imbriquée qui est toujours accessible après la fin d’une fonction, alors elle a la propriété [[Environment]] qui fait référence à l’environnement lexical.
Dans ce cas, l’environnement lexical est toujours accessible même après la fin de la fonction, il reste donc en vie.
Par exemple :
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // g.[[Environment]] stocke une référence à l'environnement lexical
// de l'appel f() correspondant
Veuillez noter que si f() est appelé plusieurs fois et que les fonctions résultantes sont sauvegardées, tous les objets correspondants de l’environnement lexical seront également conservés en mémoire. Dans le code ci-dessous, ils sont tous les trois :
function f() {
let value = math.random();
return function() { alert(value); };
}
// 3 fonctions dans un tableau, chacune d'entre elles étant liée à l'environnement lexical
// à partir de l'exécution de f() correspondante
let arr = [f(), f(), f()];
Un objet environnement lexical meurt lorsqu’il devient inaccessible (comme tout autre objet). En d’autres termes, il n’existe que s’il existe au moins une fonction imbriquée qui le référence.
Dans le code ci-dessous, une fois que la fonction imbriquée est supprimée, son environnement lexical englobant (et donc la value) est nettoyé de la mémoire :
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // tant que la fonction g existe, la valeur reste en mémoire
g = null; // … et maintenant la mémoire est nettoyée
Optimisations réelles
Comme nous l’avons vu, en théorie, lorsqu’une fonction est vivante, toutes les variables externes sont également conservées.
Mais dans la pratique, les moteurs JavaScript tentent d’optimiser cela. Ils analysent l’utilisation des variables et s’il est évident d’après le code qu’une variable externe n’est pas utilisée – elle est supprimée.
Un effet secondaire important dans V8 (Chrome, Edge, Opera) est qu’une telle variable ne sera plus disponible lors du débogage.
Essayez d’exécuter l’exemple ci-dessous sous Chrome avec les outils de développement ouverts.
Quand il se met en pause, dans la console, tapez alert(value).
Comme vous avez pu le constater, cette variable n’existe pas ! En théorie, elle devrait être accessible, mais le moteur l’a optimisée.
Cela peut conduire à des problèmes de débogage amusants (voire fastidieux). L’un d’eux – nous pouvons voir une variable externe portant le même nom au lieu de celle attendue :
Cette fonctionnalité du V8 est bonne à savoir. Si vous déboguez avec Chrome/Edge/Opera, tôt ou tard vous la rencontrerez.
Ce n’est pas un bogue dans le débogueur, mais plutôt une caractéristique spéciale de V8. Peut-être que cela sera changé un jour. Vous pouvez toujours le vérifier en exécutant les exemples sur cette page.
Exercices
La fonction sayHi utilise un nom de variable externe. Lorsque la fonction s’exécute, quelle valeur va-t-elle utiliser ?
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
sayHi(); // qu'affichera-t-elle : "John" ou "Pete" ?
De telles situations sont courantes à la fois dans le développement côté navigateur et côté serveur. Une fonction peut être programmée pour s’exécuter plus tard qu’elle n’est créée, par exemple après une action de l’utilisateur ou une demande réseau.
Donc, la question est : reprend-elle les derniers changements ?
La réponse est : Pete.
Une fonction obtient des variables externes telles qu’elles sont maintenant, elle utilise les valeurs les plus récentes.
Les anciennes valeurs de variable ne sont enregistrées nulle part. Lorsqu’une fonction veut une variable, elle prend la valeur actuelle de son propre environnement lexical ou de l’environnement externe.
La fonction makeWorker ci-dessous crée une autre fonction et la renvoie. Cette nouvelle fonction peut être appelée ailleurs.
Aura-t-elle accès aux variables externes depuis son lieu de création, ou depuis le lieu d’invocation, ou les deux ?
function makeWorker() {
let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// créons une fonction
let work = makeWorker();
// appelons-la
work(); // que va-t-elle afficher ?
Quelle valeur va-t-elle afficher ? “Pete” ou “John” ?
La réponse est : Pete.
La fonction work() dans le code ci-dessous obtient name du lieu de son origine via la référence d’environnement lexical externe :
Donc, le résultat est "Pete" ici.
Mais s’il n’y avait pas de let name dans makeWorker(), alors la recherche irait à l’extérieur et prendrait la variable globale comme nous pouvons le voir dans la chaîne ci-dessus. Dans ce cas, le résultat serait "John".
Ici, nous faisons deux compteurs : counter et counter2 en utilisant la même fonction makeCounter.
Sont-ils indépendants ? Que va montrer le deuxième compteur ? 0,1 ou 2,3 ou autre chose ?
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
let counter2 = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter2() ); // ?
alert( counter2() ); // ?
La réponse : 0,1.
Les fonctions counter et counter2 sont créées par différentes invocations de makeCounter.
Elles ont donc des environnements lexicaux externes indépendants, chacun ayant son propre count.
Ici, un objet compteur est créé à l’aide de la fonction constructeur.
Est-ce que cela fonctionnera ? Que va-t-elle afficher ?
function Counter() {
let count = 0;
this.up = function() {
return ++count;
};
this.down = function() {
return --count;
};
}
let counter = new Counter();
alert( counter.up() ); // ?
alert( counter.up() ); // ?
alert( counter.down() ); // ?
Cela fonctionnera sûrement très bien.
Les deux fonctions imbriquées sont créées dans le même environnement Lexical externe. Elles partagent donc l’accès à la même variable count :
Regardez ce code. Quel sera le résultat de l’appel à la dernière ligne ?
Le résultat est une erreur.
La fonction sayHi est déclarée à l’intérieur du if, elle ne vit donc qu’à l’intérieur. Il n’y a pas de sayHi dehors.
Écrivez une fonction sum qui fonctionne comme ceci :sum(a)(b) = a + b.
Oui, exactement de cette façon, en utilisant des doubles parenthèses (ce n’est pas une faute de frappe).
Par exemple :
sum(1)(2) = 3
sum(5)(-1) = 4
Quel sera le résultat de ce code ?
let x = 1;
function func() {
console.log(x); // ?
let x = 2;
}
func();
P.S. Il y a un piège dans cette tâche. La solution n’est pas évidente.
Le résultat est : error.
Essayez de l’exécuter :
Dans cet exemple, nous pouvons observer la différence particulière entre une variable “non existante” et une variable “non initialisée”.
Comme vous l’avez peut-être lu dans l’article Variable scope, closure, une variable démarre à l’état “non initialisée” à partir du moment où l’exécution entre dans un bloc de code (ou une fonction). Et elle reste non initialisée jusqu’à la déclaration let correspondante.
En d’autres termes, une variable existe techniquement, mais ne peut pas être utilisée avant let.
Le code ci-dessus le démontre.
function func() {
// la variable locale x est connue du moteur depuis le début de la fonction,
// mais "non initialisée" (inutilisable) jusqu'à let ("zone morte")
// d'où l'erreur
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 2;
}
Cette zone d’inutilisabilité temporaire d’une variable (du début du bloc de code jusqu’à let) est parfois appelée “dead zone” (zone morte).
Nous avons une méthode intégrée arr.filter(f) pour les tableaux. Elle filtre tous les éléments à travers la fonction f. S’elle renvoie true, cet élément est renvoyé dans le tableau résultant.
Créez un ensemble de filtres “prêts à l’emploi”:
inBetween(a, b)– entreaetbou égal à eux (inclusivement).inArray([...])– dans le tableau donné.
L’usage doit être comme ceci :
arr.filter(inBetween(3,6))– sélectionne uniquement les valeurs entre 3 et 6.arr.filter(inArray([1,2,3]))– sélectionne uniquement les éléments correspondant à l’un des membres de[1,2,3].
Par exemple :
/* .. votre code pour inBetween et inArray */
let arr = [1, 2, 3, 4, 5, 6, 7];
alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6
alert( arr.filter(inArray([1, 2, 10])) ); // 1,2
Filter inArray
Nous avons un tableau d’objets à trier :
let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" }
];
La manière habituelle de le faire serait :
// par nom (Ann, John, Pete)
users.sort((a, b) => a.name > b.name ? 1 : -1);
// par age (Pete, Ann, John)
users.sort((a, b) => a.age > b.age ? 1 : -1);
Peut-on le rendre encore moins verbeux, comme ceci ?
users.sort(byField('name'));
users.sort(byField('age'));
Donc, au lieu d’écrire une fonction, il suffit de mettre byField(fieldName).
Ecrivez la fonction byField qui peut être utilisée pour cela.
function byField(fieldName){
return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;
}
Le code suivant crée un tableau de shooters.
Chaque fonction est censée sortir son numéro. Mais quelque chose ne va pas …
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // créer une fonction shooter
alert( i ); // qui devrait afficher son numéro
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// tous les shooters affichent 10 au lieu de leurs numéros 0, 1, 2, 3 ...
army[0](); // 10 du shooter numéro 0
army[1](); // 10 du shooter numéro 1
army[2](); // 10 ...etc.
Pourquoi tous les shooters affichent-ils la même valeur ?
Corrigez le code pour qu’il fonctionne comme prévu.
Examinons exactement ce qui se fait à l’intérieur de makeArmy, et la solution deviendra évidente.
-
Elle crée un tableau vide
shooters:let shooters = []; -
Le remplit avec des fonctions dans la boucle via
shooters.push(function).Chaque élément est une fonction, le tableau résultant ressemble à ceci :
shooters = [ function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); } ]; -
Le tableau est renvoyé par la fonction.
Puis, plus tard, l’appel à n’importe quel membre, par ex.
Army[5]()récupérera l’élémentarmy[5]du tableau (qui est une fonction) et l’appellera.Maintenant, pourquoi toutes ces fonctions affichent-elles la même valeur,
10?C’est parce qu’il n’y a pas de variable locale
idans les fonctions deshooter. Lorsqu’une telle fonction est appelée, elle prendide son environnement lexical externe.Alors, quelle sera la valeur de
i?Si nous regardons la source :
function makeArmy() { ... let i = 0; while (i < 10) { let shooter = function() { // fonction shooter alert( i ); // should show its number }; shooters.push(shooter); // ajoute une fonction au tableau i++; } ... }Nous pouvons voir que toutes les fonctions
shootersont créées dans l’environnement lexical de la fonctionmakeArmy(). Mais quandarmy[5]()est appelé,makeArmya déjà terminé son travail, et la valeur finale deiest10(whiles’arrête ài=10).En conséquence, toutes les fonctions
shooterobtiennent la même valeur de l’environnement lexical externe et c’est-à-dire la dernière valeur,i=10.Comme vous pouvez le voir ci-dessus, à chaque itération d’un bloc
while {...}, un nouvel environnement lexical est créé. Donc, pour résoudre ce problème, nous pouvons copier la valeur deidans une variable dans le blocwhile {...}, comme ceci :function makeArmy() { let shooters = []; let i = 0; while (i < 10) { let j = i; let shooter = function() { // fonction shooter alert( j ); // devrait afficher son numéro }; shooters.push(shooter); i++; } return shooters; } let army = makeArmy(); // Maintenant, le code fonctionne correctement army[0](); // 0 army[5](); // 5Ici,
let j = idéclare une variable “itération-locale”jet y copiei. Les primitives sont copiées “par valeur”, donc nous obtenons en fait une copie indépendante dei, appartenant à l’itération de boucle courante.Les shooters fonctionnent correctement, car la valeur de
ivit maintenant un peu plus près. Pas dans l’environnement lexicalmakeArmy(), mais dans l’environnement lexical qui correspond à l’itération de la boucle actuelle :Ce genre de problème pourrait également être évité si nous utilisions
forau début, comme ceci :C’est essentiellement la même chose, car
forgénère un nouvel environnement lexical à chaque itération avec sa propre variablei. Ainsi, leshootergénéré à chaque itération fait référence à son proprei, à partir de cette itération même.Maintenant que vous avez déployé tant d’efforts pour lire ceci, et que la recette finale est si simple – utilisez simplement
for, vous vous demandez peut-être – cela en valait-il la peine ?Eh bien, si vous pouviez facilement répondre à la question, vous ne liriez pas la solution. Donc, j’espère que cette tâche doit vous avoir aidé à comprendre un peu mieux les choses.
En outre, il existe en effet des cas où l’on préfère while à for, et d’autres scénarios où de tels problèmes sont réels.
- © 2007—2026 Ilya Kantor
- à propos du projet
- nous contacter
Commentaires
<code>, pour plusieurs lignes – enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen…)