Comprends comment fonctionne la portée en JavaScript — portée globale, de fonction et de bloc — et maîtrise le concept puissant des closures.
La portée détermine où les variables sont accessibles dans ton code. JavaScript possède plusieurs types de portée, et les comprendre est essentiel pour écrire du code sans bugs.
Portée globale — les variables déclarées en dehors de toute fonction ou bloc sont accessibles globalement :
const appName = 'Kodojo'; // portée globale
function greet() {
console.log(appName); // accessible ici
}
console.log(appName); // accessible ici aussiLes variables globales sont disponibles partout, mais en avoir trop pollue l'espace de noms global et peut causer des conflits de nommage. Évite de créer des globales inutiles.
Portée de fonction — les variables déclarées avec var à l'intérieur d'une fonction ne sont accessibles qu'au sein de cette fonction :
function calculate() {
var result = 42;
console.log(result); // 42
}
calculate();
console.log(result); // ReferenceError: result is not definedvar a une portée de fonction — elle ne se soucie pas des blocs comme if ou for, seulement des fonctions :
function example() {
if (true) {
var x = 10; // portée de fonction, pas de bloc !
}
console.log(x); // 10 — x est accessible en dehors du bloc if
}C'est l'une des raisons pour lesquelles let et const ont été introduits — ils se comportent de manière plus prévisible.
let et const ont une portée de bloc — ils ne sont accessibles qu'à l'intérieur des accolades {} les plus proches :
if (true) {
let x = 10;
const y = 20;
console.log(x, y); // 10 20
}
console.log(x); // ReferenceError
console.log(y); // ReferenceErrorCela s'applique à n'importe quel bloc — if, for, while, ou même un {} autonome :
{
let secret = 'hidden';
}
console.log(secret); // ReferenceErrorLa portée de bloc est particulièrement importante dans les boucles for. Avec let, chaque itération obtient sa propre copie de la variable :
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 0, 1, 2 (correct !)Avec var, toutes les itérations partagent la même variable — un piège classique de JavaScript :
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 3, 3, 3 (faux !)C'est pourquoi le JavaScript moderne préfère fortement let et const à var.
Quand JavaScript rencontre une variable, il la recherche à travers la chaîne de portée — en commençant par la portée la plus interne et en remontant vers l'extérieur :
const global = 'I am global';
function outer() {
const outerVar = 'I am outer';
function inner() {
const innerVar = 'I am inner';
console.log(innerVar); // trouvée dans la portée interne
console.log(outerVar); // trouvée dans la portée externe
console.log(global); // trouvée dans la portée globale
}
inner();
}
outer();L'ordre de recherche est : portée interne → portée externe → portée globale. Si la variable n'est trouvée nulle part, JavaScript lève une ReferenceError.
Cela signifie que les fonctions internes ont toujours accès aux variables de leurs fonctions parentes. C'est la fondation des closures.
Le masquage de variable se produit quand une portée interne déclare une variable avec le même nom qu'une variable externe :
const x = 'outer';
function example() {
const x = 'inner'; // masque le x externe
console.log(x); // 'inner'
}
example();
console.log(x); // 'outer' — inchangéLe x interne cache (masque) le x externe dans cette portée. Le x externe reste intact en dehors de la fonction.
Une closure est une fonction qui se souvient et peut accéder aux variables de sa portée externe (englobante), même après que la fonction externe a fini son exécution. Les closures sont créées chaque fois qu'une fonction est créée.
function createGreeter(greeting) {
// greeting est capturé par la closure
return function(name) {
return `${greeting}, ${name}!`;
};
}
const sayHello = createGreeter('Hello');
const sayHi = createGreeter('Hi');
console.log(sayHello('Alice')); // 'Hello, Alice!'
console.log(sayHi('Bob')); // 'Hi, Bob!'Bien que createGreeter ait terminé son exécution, sayHello et sayHi se souviennent encore de leurs valeurs respectives de greeting. C'est la closure en action.
Cas d'usage courants des closures :
Confidentialité des données — cacher des variables de l'accès extérieur :
function makeCounter() {
let count = 0; // privé !
return {
increment() { count++; },
getCount() { return count; },
};
}Fonctions factory — créer des fonctions spécialisées :
function multiplier(factor) {
return (n) => n * factor;
}
const double = multiplier(2);
const triple = multiplier(3);Callbacks qui se souviennent du contexte — gestionnaires d'événements, timers, etc.
Application partielle — pré-remplir certains arguments d'une fonction.
Les closures sont l'un des patterns les plus puissants en JavaScript. Elles permettent les modules, l'état privé et les techniques de programmation fonctionnelle.
<p id="output"></p>
<script>
// A counter factory using closures
function makeCounter() {
let count = 0; // private variable
return {
increment() {
count++;
},
decrement() {
count--;
},
getCount() {
return count;
},
};
}
const counter = makeCounter();
counter.increment();
counter.increment();
counter.increment();
counter.decrement();
// count is not accessible directly:
// console.log(counter.count); // undefined
// Only through the closure:
document.getElementById('output').textContent =
'Count: ' + counter.getCount(); // Count: 2
</script>Qu'est-ce qu'une closure ?