Aujourd'hui je vous propose de vous aider à déboguer une erreur classique en javascript.

Cannot read property 'XXX' of null
Cannot read property 'XXX' of undefined

Cette erreur survient lorsque que vous essayez d'appeler une méthode ou d'accéder à une propriété sur une variable qui n'est pas un objet mais qui a pris la valeur null ou undefined. Cette erreur est le résultat d'un petit problème de logique dans votre code.

// Exemple
const element = document.querySelector('#demo') // On s'attend à avoir un objet de type HTMLElement
// Mais l'élément n'existe du coup element = null
const items = [].slice.call(element.children) // On se retrouve donc ici à faire null.children
// TypeError : Cannot read property 'children' of null

Heureusement pour nous les navigateurs sont équipés d'un outil formidable pour déboguer notre code et arriver à identifier facilement l'origine de l'erreur. Pour trouver l'origine du problème, il faut identifier la raison qui fait que la variable n'a pas la valeur attendue. Pour cela il va falloir remonter l'éxécution du script pour trouver l'origine de la variable. Le meilleur moyen pour procéder à cette recherche est d'utiliser les points d'arrêts. Ces points d'arrêts nous permettent de mettre en pause notre script et de pouvoir inspecter la valeur qu'ont les différentes variables à un instant T. Vous avez d'ailleurs la possibilité de demander au navigateur de s'arrêter automatiquement lorsqu'une une exception est renvoyée.

Pause on exception

Une fois le script arrêté, il vous suffit d'inspecter la valeur des différentes variables pour voir ce qui ne se passe pas correctement. Si la variable est passée en paramètre vous pouvez remonter la "Call stack" afin de voir les fonctions qui ont menées à l'éxécution du code que vous observez.

Placer manuellement des points d'arrêts

En plus des pause lors des exceptions vous pouvez placer des points d'arrêts manuellement en vous rendant sur l'onglet Source de votre inspecteur et en cliquant sur les numéros de lignes où vous souhaitez mettre en pause votre script. Vous pouvez aussi placer un point d'arrêt dans votre code gràce à l'instruction debugger. Cette dernière solution peut s'avérer très utile si votre code est transpilé par exemple.

const element = document.querySelector('#demo')
debugger // Le script s'arrètera ici
const items = [].slice.call(element.children)

Naviguer dans le code

Lorsque vous êtes sur un point d'arrêt vous avez plusieurs bouton que vous pouvez utiliser pour naviguer dans le code :

  • Step over next function call (F10), permet de se déplacer vers l'instruction suivante.
  • Step into the next function call (F11), permet de se déplacer vers la prochaine fonction et suspend le code à la première instruction de cette dernière. Dans le cas de code asynchrone, L'inspecteur va suspendre le code asynchrone (par exemple il entrera dans le callback d'un setTimeout).
  • Step out of current function (Shift + F11), remonte d'un niveau vers la fonction parente.
  • Step (F9), permet de se déplacer vers la prochaine fonction sans prendre en code le code asynchrone.

Console.log

Enfin, on peut aussi être tenté d'utiliser le console.log() pour inspecter ces objets mais cette méthode peut être trompeuse dans le cas de l'analyse des objets car l'inspecteur affichera une référence à l'objet. Aussi, si votre script mute l'objet, la valeur que vous allez voir en console peut ne pas correspondre à la valeur de l'objet au moment du console.log.

const a = {b: 2, c: 3}
console.log(a)
a.c = 5
// En console on verra {b: 2, c: 5} et pas la valeur de "a" à l'instant du console.log

Soyez donc bien conscient de cette limitation lorsque vous utilisez le console.log.