Fermeture (Closure) en TypeScript
Dans cet article, nous allons expliquer les fermetures (closures) en TypeScript.
YouTube Video
Fermeture (Closure) en TypeScript
Qu'est-ce qu'une fermeture (closure) ?
Une fermeture (closure) désigne la capacité à conserver des références à l'environnement (scope) dans lequel une fonction a été définie, même lorsque cette fonction est appelée en dehors de cet environnement. Ci-dessous, les fermetures (closures) seront expliquées avec les annotations de type.
Pour faire simple, une fermeture (closure) associe une fonction à l’environnement de variables où elle a été définie, permettant d'y accéder lorsque la fonction est appelée.
Mécanisme de base des fermetures (closures)
En TypeScript, lorsqu’une fonction est définie à l’intérieur d’une autre fonction, il devient évident que la fonction interne peut accéder aux variables de la fonction externe. Voici un exemple simple d’une fermeture (closure) avec des annotations de type.
1function outerFunction(): () => void {
2 let outerVariable: string = "I am from outer function";
3
4 function innerFunction(): void {
5 // The inner function accesses the variable of the outer function
6 console.log(outerVariable);
7 }
8
9 return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure(); // "I am from outer function"
- Le type de retour de
outerFunction
est() => void
, ce qui indique qu’elle retourne une fonction. - Le type de
innerFunction
est explicitement défini survoid
, indiquant qu’elle ne retourne aucune valeur.
Utilisations et avantages des fermetures (closures)
Encapsulation des données
Ci-dessous un exemple de fermeture (closure) avec des annotations de type pour encapsuler des données.
1function createCounter(): () => number {
2 let count: number = 0;
3
4 return function (): number {
5 count += 1;
6 return count;
7 };
8}
9
10const counter: () => number = createCounter();
11console.log(counter()); // 1
12console.log(counter()); // 2
13console.log(counter()); // 3
- La fonction
createCounter
retourne une fonction de type() => number
. - La variable
count
est de typenumber
et est manipulée à l’intérieur de la fermeture (closure).
Fonctions d’ordre supérieur
Les fermetures (closures) sont utiles lors de la création de fonctions d’ordre supérieur. Voici un exemple d’une fonction d’ordre supérieur avec des annotations de type claires.
1function createMultiplier(multiplier: number): (value: number) => number {
2 return function (value: number): number {
3 return value * multiplier;
4 };
5}
6
7const double: (value: number) => number = createMultiplier(2);
8console.log(double(5)); // 10
9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5)); // 15
createMultiplier
prend un argument de typenumber
et retourne une fonction de type(value: number) => number
.- La fonction interne accepte également
value
de typenumber
et retourne le résultat en tant quenumber
.
Exemple d’implémentation de fermetures (closures) en TypeScript
Implémentez un compteur borné comme une fermeture (closure) avec des annotations de type.
1function rangeCounter(min: number, max: number): () => number | string {
2 let count: number = min;
3
4 return function (): number | string {
5 if (count <= max) {
6 return count++;
7 } else {
8 return `Count has exceeded the maximum value: ${max}`;
9 }
10 };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter()); // 1
16console.log(counter()); // 2
17console.log(counter()); // 3
18console.log(counter()); // 4
19console.log(counter()); // 5
20console.log(counter()); // "Count has exceeded the maximum value: 5"
- La fonction
rangeCounter
retourne une fonction qui retourne soit unnumber
, soit unestring
. - Dans la fonction interne, si
count
dépassemax
, elle retourne un message de typestring
; sinon, elle retourne unnumber
.
Précautions lors de l’utilisation de fermetures (closures)
Fuites de mémoire potentielles dues aux fermetures (closures)
Les fermetures (closures) peuvent conserver des variables provenant d’un scope externe, ce qui peut parfois provoquer des fuites de mémoire. Les fermetures (closures) inutiles doivent être explicitement libérées de la mémoire.
1function createLeak(): () => void {
2 // Large array consuming significant memory
3 const largeArray: string[] = new Array(1000000).fill("leak");
4
5 // Closure capturing `largeArray`
6 return function (): void {
7 console.log(largeArray[0]); // Using the captured array
8 };
9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
- Dans ce code, le
largeArray
créé à l’intérieur decreateLeak
devrait normalement être libéré lorsqu’il sort du scope, mais ce n’est pas le cas car la fermeture (closure) capturelargeArray
. Tant queleakyFunction
existe, cette mémoire non nécessaire sera conservée. - Lorsqu’un objet ou une variable n’est plus nécessaire, définir sa référence à
null
permet au ramasse-miettes (garbage collector) de la détecter et de libérer la mémoire.
Mauvaise utilisation des fermetures (closures) dans les boucles
Lors de la création de fermetures (closures) à l’intérieur d’une boucle, il peut y avoir des problèmes de référence à la même variable. L’exemple suivant montre un cas où la variable i
ne fonctionne pas correctement.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Ce code ne donne pas le résultat souhaité, car i
fait référence à la valeur 3 à la fin de la boucle. Pour corriger cela, utilisez soit let
pour séparer le scope, soit une fonction immédiatement invoquée.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
En utilisant let
, le scope de i
est séparé à chaque itération de la boucle, produisant ainsi les résultats attendus.
Résumé
En TypeScript, les fermetures (closures) permettent d’écrire un code plus sûr et plus prévisible en profitant du système de types. Une utilisation correcte des fermetures (closures) permet l’encapsulation des données et une conception flexible des fonctions d’ordre supérieur. De plus, il convient de faire attention à la gestion de la mémoire et aux références de scope involontaires lors de l’utilisation de fermetures (closures).
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.