[Hard] đ Closure
1. What is Closure ?â
Qu'est-ce qu'une Closure ?
Pour comprendre les closures, il faut d'abord comprendre la portée des variables en JavaScript et comment les fonctions accÚdent aux variables externes.
Variable Scope (PortĂ©e des variables)â
En JavaScript, la portée des variables se divise en deux types : global scope et function scope.
// global scope
let a = 1;
function parentFunction() {
// function scope
let b = 2;
function childFunction() {
let c = 3;
console.log(a, b, c); // print 1 2 3, can access global scope & function scope
}
childFunction();
}
parentFunction();
console.log(a); // print 1, can access global scope
console.log(b, c); // Erreur générée, impossible d'accéder aux variables dans le function scope
Closure exampleâ
La condition de déclenchement d'une Closure est qu'une fonction enfant soit définie à l'intérieur d'une fonction parent et retournée via return, permettant de conserver les variables d'environnement de la fonction enfant (ce qui évite le Garbage Collection).
function parentFunction() {
let count = 0;
return function childFunction() {
count += 1;
console.log(`Compteur actuel : ${count}`);
};
}
const counter = parentFunction();
counter(); // print Compteur actuel : 1
counter(); // print Compteur actuel : 2
// La variable count n'est pas récupérée par le garbage collector, car childFunction existe toujours et met à jour la valeur de count à chaque appel
Cependant, il faut noter que les closures conservent les variables en mémoire. Si les variables sont trop nombreuses, cela peut entraßner une utilisation excessive de la mémoire (il ne faut pas abuser des closures), affectant ainsi les performances.
2. Create a function that meets the following conditionsâ
Créez une fonction qui remplit les conditions suivantes (en utilisant le concept de closure)
plus(2, 5); // output 7
plus(2)(5); // output 7
First Solution : two functionsâ
On sépare le traitement en deux fonctions
function plus(value, subValue) {
return value + subValue;
}
console.log(plus(2, 5));
// use closure save variable
function plus(value) {
return function (subValue) {
return value + subValue;
};
}
console.log(plus(2)(5));
Second Solution : single functionâ
Bien sĂ»r, la premiĂšre solution a de fortes chances d'ĂȘtre rejetĂ©e, il faut donc essayer de tout combiner dans une seule fonction.
function plus(value, subValue) {
// On détermine en fonction du nombre de paramÚtres passés
if (arguments.length > 1) {
return value + subValue;
} else {
return function (item) {
return value + item;
};
}
}
console.log(plus(2, 5));
console.log(plus(2)(5));
3. Please take advantage of the closure feature to increase the numberâ
Utilisez la caractéristique de closure pour incrémenter un nombre
function plus() {
// code
}
var obj = plus();
obj.add(); // print 1
obj.add(); // print 2
First Solution : return variableâ
Ici, on n'utilise pas les Arrow Functions, mais la forme classique de function.
function plus() {
let cash = 0;
let counter = {
add() {
cash += 1;
console.log(cash);
},
};
return counter;
}
var obj = plus();
obj.add();
obj.add();
Second Solution : return objectâ
Dans la solution précédente, on peut aussi inclure directement l'objet dans le return
function plus() {
let cash = 0;
return {
add: function () {
cash += 1;
console.log(cash);
},
};
}
var obj = plus();
obj.add();
obj.add();
4. What will be printed in this nested function call?â
Qu'affichera cet appel de fonctions imbriquées ?
function a(aa) {
aa();
}
function b(bb) {
bb();
}
function c() {
console.log('hello');
}
a(b(c));
Analyseâ
Résultat d'exécution :
hello
TypeError: aa is not a function
Flux d'exĂ©cution dĂ©taillĂ©â
// Exécution de a(b(c))
// JavaScript exécute les fonctions de l'intérieur vers l'extérieur
// Ătape 1 : ExĂ©cuter la fonction la plus interne b(c)
b(c)
â
// La fonction c est passée en paramÚtre à b
// à l'intérieur de b, bb() est exécuté, c'est-à -dire c()
c() // Affiche 'hello'
â
// La fonction b n'a pas d'instruction return
// Elle retourne donc undefined
return undefined
// Ătape 2 : ExĂ©cuter a(undefined)
a(undefined)
â
// undefined est passé en paramÚtre à a
// à l'intérieur de a, on tente d'exécuter aa()
// C'est-Ă -dire undefined()
undefined() // â Erreur : TypeError: aa is not a function
Pourquoi cela se produit-il ?â
1. Ordre d'exĂ©cution des fonctions (de l'intĂ©rieur vers l'extĂ©rieur)â
// Exemple
console.log(add(multiply(2, 3)));
â â
| ââ 2. D'abord exĂ©cuter multiply(2, 3) â 6
âââââââ 3. Ensuite exĂ©cuter add(6)
// MĂȘme concept
a(b(c))
â â
| ââ 1. D'abord exĂ©cuter b(c)
ââââ 2. Ensuite exĂ©cuter a(rĂ©sultat de b(c))
2. Les fonctions sans return retournent undefinedâ
function b(bb) {
bb(); // Exécuté, mais pas de return
} // return undefined implicite
// Ăquivalent Ă
function b(bb) {
bb();
return undefined; // Ajouté automatiquement par JavaScript
}
3. Essayer d'appeler quelque chose qui n'est pas une fonction provoque une erreurâ
const notAFunction = undefined;
notAFunction(); // TypeError: notAFunction is not a function
// Autres cas qui provoquent des erreurs
null(); // TypeError
123(); // TypeError
'string'(); // TypeError
Comment corriger ?â
MĂ©thode 1 : Faire en sorte que la fonction b retourne une fonctionâ
function a(aa) {
aa();
}
function b(bb) {
bb();
return function () {
console.log('b executed');
};
}
function c() {
console.log('hello');
}
a(b(c));
// Sortie :
// hello
// b executed
MĂ©thode 2 : Passer la fonction directement, sans l'exĂ©cuter d'abordâ
function a(aa) {
aa();
}
function b(bb) {
return function () {
bb();
};
}
function c() {
console.log('hello');
}
a(b(c)); // N'affiche que 'hello'
// Ou écrire de cette façon
a(() => b(c)); // Affiche 'hello'
MĂ©thode 3 : Modifier la logique d'exĂ©cutionâ
function a(aa) {
aa();
}
function b(bb) {
bb();
}
function c() {
console.log('hello');
}
// Exécuter séparément
b(c); // Affiche 'hello'
a(() => console.log('a executed')); // Affiche 'a executed'
Questions connexesâ
Question 1 : Que se passe-t-il si on modifie comme ceci ?â
function a(aa) {
return aa();
}
function b(bb) {
return bb();
}
function c() {
console.log('hello');
return 'world';
}
console.log(a(b(c)));
Cliquez pour voir la réponse
hello
TypeError: aa is not a function
Analyse :
b(c)â ExĂ©cutec(), affiche'hello', retourne'world'a('world')â ExĂ©cute'world'()... attendez, ça provoque aussi une erreur !
Bonne réponse :
hello
TypeError: aa is not a function
Parce que b(c) retourne 'world' (une chaĂźne), a('world') tente d'exĂ©cuter 'world'(), une chaĂźne n'est pas une fonction, d'oĂč l'erreur.
Question 2 : Et si tous ont un return ?â
function a(aa) {
return aa;
}
function b(bb) {
return bb;
}
function c() {
return 'hello';
}
const result = a(b(c));
console.log(result);
console.log(result());
Cliquez pour voir la réponse
[Function: c]
hello
Analyse :
b(c)â Retourne la fonctioncelle-mĂȘme (sans l'exĂ©cuter)a(c)â Retourne la fonctioncelle-mĂȘmeresultest la fonctioncresult()â ExĂ©cutec(), retourne'hello'
Points clĂ©s Ă retenirâ
// Priorité des appels de fonctions
a(b(c))
â
// 1. D'abord exécuter la plus interne
b(c) // Si b n'a pas de return, c'est undefined
â
// 2. Ensuite exécuter l'externe
a(undefined) // Tenter d'exécuter undefined() provoque une erreur
// Solutions
// â
1. S'assurer que les fonctions intermédiaires retournent une fonction
// â
2. Ou utiliser des arrow functions pour encapsuler
a(() => b(c))