Un nom écrit dans du code doit correspondre à quelque chose. Quand le moteur cherche ce nom partout où il a le droit de chercher et ne trouve rien, il s'arrête net.
Le message est court et sans ambiguïté, ce qui en fait l'une des erreurs les plus faciles à corriger, à une exception près.
Définition
Un ReferenceError est levé quand un identifiant utilisé n'est déclaré dans aucune portée accessible. La valeur n'est pas mauvaise : le nom n'existe tout simplement pas.
try {
console.log(utilisateur);
} catch (err) {
console.log(err.name); // ReferenceError
console.log(err.message); // utilisateur is not defined
}
// Aucune erreur, parce que typeof accepte les noms inconnus
console.log(typeof utilisateur); // undefinedtypeof est la seule opération qui tolère un nom inconnu. C'est une survivance utile pour tester la présence d'un objet global sans planter.
Les trois causes habituelles
- Une faute de frappe. Le cas le plus courant, et celui qu'un éditeur signale avant même l'exécution.
- Un import oublié. Le nom existe dans un autre fichier, mais rien ne l'a fait entrer dans celui-ci.
- Un objet global absent de l'environnement.
documentetwindown'existent pas sous Node.js,processn'existe pas dans un navigateur.
Ce dernier point explique la plupart des ReferenceError vus dans un projet moderne, où le même code peut tourner côté serveur puis côté navigateur.
La zone morte temporelle
C'est l'exception annoncée, et elle déroute parce que le nom existe bel et bien. Une variable déclarée avec let ou const est réservée dès l'entrée dans son bloc, mais reste inaccessible jusqu'à sa ligne de déclaration.
try {
console.log(compteur);
let compteur = 1;
} catch (err) {
console.log(err.message); // Cannot access 'compteur' before initialization
}
// Avec var, pas d'erreur mais une valeur inutilisable
console.log(total); // undefined
var total = 10;Le message diffère nettement de is not defined : il indique que la déclaration est plus bas dans le même bloc. Avec var, le même code ne lève rien et rend undefined, ce qui produit un défaut silencieux à la place d'une erreur claire.
Hors mode strict, une affectation à un nom non déclaré crée une variable globale au lieu de lever une erreur. Les modules sont toujours en mode strict, ce qui rétablit le ReferenceError attendu : une raison de plus de travailler en modules.
Questions fréquentes
Pourquoi document is not defined apparaît-il au démarrage du serveur ?
Parce que le code s'exécute d'abord côté serveur, où le document du navigateur n'existe pas. La lecture doit alors être déplacée dans un effet déclenché après l'affichage, ou protégée par un test de présence de l'objet global.
Quelle différence avec un TypeError ?
Le ReferenceError se produit avant toute utilisation : le nom est introuvable. Le TypeError survient après : le nom existe, la valeur aussi, mais elle ne supporte pas l'opération demandée. Le premier se corrige dans la déclaration, le second dans les données.
Comment éviter ces erreurs plutôt que les découvrir à l'exécution ?
Un analyseur statique les signale à l'écriture : une règle dédiée d'ESLint refuse tout nom non déclaré, et TypeScript refuse carrément de compiler. Les deux transforment une panne d'exécution en un avertissement dans l'éditeur, ce qui change complètement le coût de l'erreur.