RAICODE
ProcessusProjetsBlogOffresClientsContact
Développement

Les 10 Erreurs JavaScript Que Même les Seniors Font

Closures, async/await, this... Les pièges classiques du JS et comment les éviter définitivement.

Mustapha Hamadi
Développeur Full-Stack
15 décembre 2025
10 min read
Les 10 Erreurs JavaScript Que Même les Seniors Font
#JavaScript#développement#erreurs#debugging#bonnes pratiques
Partager :

title: "Les 10 Erreurs JavaScript Que Même les Seniors Font" description: "Closures, async/await, this... Les pièges classiques du JS et comment les éviter définitivement." date: "2025-12-15" author: "Équipe Raicode" tags: ["JavaScript", "développement", "erreurs", "debugging", "bonnes pratiques"] category: "Développement" image: "/blog/erreurs-javascript-seniors-hero.png" ogImage: "/blog/erreurs-javascript-seniors-hero.png" keywords: ["erreurs JavaScript", "bugs JavaScript", "async await erreurs", "closures JavaScript", "this JavaScript"]

8 ans d'expérience JavaScript. Des centaines de code reviews. Et je vois toujours les mêmes erreurs, y compris dans du code écrit par des devs seniors.

Ce n'est pas une question de compétence. C'est que JavaScript a des pièges subtils qui demandent de la vigilance constante.

Voici les 10 erreurs les plus courantes - et comment les éviter.


Erreur #1 : Le this Perdu dans les Callbacks

Le Bug

class UserService {
  constructor() {
    this.users = [];
  }

  fetchUsers() {
    fetch('/api/users')
      .then(function(response) {
        return response.json();
      })
      .then(function(data) {
        this.users = data; // ❌ TypeError: Cannot set property 'users' of undefined
      });
  }
}

Pourquoi Ça Arrive

Dans une fonction classique, this dépend du contexte d'appel. Dans un callback, this n'est plus l'instance de la classe.

La Solution

// ✅ Solution 1 : Arrow functions (pas de this propre)
fetchUsers() {
  fetch('/api/users')
    .then(response => response.json())
    .then(data => {
      this.users = data; // ✅ this = l'instance
    });
}

// ✅ Solution 2 : bind explicite
fetchUsers() {
  fetch('/api/users')
    .then(function(response) {
      return response.json();
    })
    .then(function(data) {
      this.users = data;
    }.bind(this));
}

// ✅ Solution 3 : Variable de closure
fetchUsers() {
  const self = this;
  fetch('/api/users')
    .then(function(response) {
      return response.json();
    })
    .then(function(data) {
      self.users = data;
    });
}

Règle d'or : Utilisez les arrow functions pour les callbacks, sauf si vous avez besoin d'un this dynamique.


Erreur #2 : Async/Await dans une Boucle forEach

Le Bug

async function processUsers(userIds) {
  userIds.forEach(async (id) => {
    const user = await fetchUser(id); // ❌ N'attend pas !
    console.log(user.name);
  });
  console.log('Terminé'); // S'affiche AVANT les users !
}

Pourquoi Ça Arrive

forEach ne sait pas gérer les promesses. Il lance tous les callbacks et continue immédiatement.

La Solution

// ✅ Solution 1 : for...of (séquentiel)
async function processUsers(userIds) {
  for (const id of userIds) {
    const user = await fetchUser(id);
    console.log(user.name);
  }
  console.log('Terminé'); // S'affiche après tous les users
}

// ✅ Solution 2 : Promise.all (parallèle)
async function processUsers(userIds) {
  const users = await Promise.all(
    userIds.map(id => fetchUser(id))
  );
  users.forEach(user => console.log(user.name));
  console.log('Terminé');
}

// ✅ Solution 3 : for await...of (streams)
async function processUsers(userIds) {
  const promises = userIds.map(id => fetchUser(id));
  for await (const user of promises) {
    console.log(user.name);
  }
}

Règle d'or : forEach + async = problème. Utilisez for...of ou Promise.all.


Erreur #3 : Comparaison avec == au lieu de ===

Le Bug

console.log(0 == '');        // true 😱
console.log(0 == false);     // true 😱
console.log('' == false);    // true 😱
console.log(null == undefined); // true 😱
console.log([1] == '1');     // true 😱

Pourquoi Ça Arrive

L'opérateur == fait de la coercition de type. JavaScript essaie de convertir les valeurs avant de comparer.

La Solution

// ✅ Toujours utiliser ===
console.log(0 === '');        // false
console.log(0 === false);     // false
console.log('' === false);    // false
console.log(null === undefined); // false

// ✅ Exception : vérifier null OU undefined
if (value == null) {
  // value est null ou undefined
}

// ✅ Équivalent explicite
if (value === null || value === undefined) {
  // ...
}

Règle d'or : Configurez ESLint avec eqeqeq: 'error' pour forcer ===.


Erreur #4 : Mutation d'Objets Par Référence

Le Bug

const user = { name: 'Alice', settings: { theme: 'dark' } };
const copy = user; // ❌ Ce n'est pas une copie !

copy.name = 'Bob';
console.log(user.name); // 'Bob' 😱

// Même avec spread...
const shallowCopy = { ...user };
shallowCopy.settings.theme = 'light';
console.log(user.settings.theme); // 'light' 😱 (shallow copy)

Pourquoi Ça Arrive

Les objets sont passés par référence. = copie la référence, pas l'objet. Le spread operator fait une copie superficielle.

La Solution

// ✅ Deep copy avec structuredClone (moderne)
const deepCopy = structuredClone(user);
deepCopy.settings.theme = 'light';
console.log(user.settings.theme); // 'dark' ✅

// ✅ Deep copy avec JSON (legacy, limité)
const deepCopy2 = JSON.parse(JSON.stringify(user));
// ⚠️ Ne fonctionne pas avec Date, Functions, undefined, etc.

// ✅ Immutabilité avec spread imbriqué
const updated = {
  ...user,
  settings: {
    ...user.settings,
    theme: 'light'
  }
};

Règle d'or : Utilisez structuredClone() pour les deep copies. Préférez l'immutabilité.


Erreur #5 : Oublier le Return dans les Arrow Functions

Le Bug

// ❌ Oubli de return avec les accolades
const doubled = [1, 2, 3].map(n => {
  n * 2; // Pas de return !
});
console.log(doubled); // [undefined, undefined, undefined]

// ❌ Return d'un objet mal formaté
const users = data.map(d => { name: d.name }); // ❌ Syntax error ou undefined

Pourquoi Ça Arrive

Avec {}, le return n'est pas implicite. Pour retourner un objet littéral, il faut des parenthèses.

La Solution

// ✅ Return implicite (sans accolades)
const doubled = [1, 2, 3].map(n => n * 2);

// ✅ Return explicite (avec accolades)
const doubled2 = [1, 2, 3].map(n => {
  return n * 2;
});

// ✅ Return d'objet (parenthèses obligatoires)
const users = data.map(d => ({ name: d.name }));

// ✅ Multi-lignes avec return explicite
const processed = data.map(d => {
  const name = d.name.toUpperCase();
  const age = d.age + 1;
  return { name, age };
});

Règle d'or : Arrow function + { = pensez à return. Objet littéral = ({ }).


Erreur #6 : Closure et Variables de Boucle

Le Bug

// ❌ Toutes les callbacks voient i = 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 3, 3, 3
  }, 100);
}

Pourquoi Ça Arrive

var a une portée de fonction, pas de bloc. Au moment où les callbacks s'exécutent, la boucle est terminée et i vaut 3.

La Solution

// ✅ Solution 1 : let (portée de bloc)
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 0, 1, 2
  }, 100);
}

// ✅ Solution 2 : IIFE (si var obligatoire)
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => {
      console.log(j); // 0, 1, 2
    }, 100);
  })(i);
}

// ✅ Solution 3 : forEach avec index
[0, 1, 2].forEach((_, i) => {
  setTimeout(() => {
    console.log(i); // 0, 1, 2
  }, 100);
});

Règle d'or : N'utilisez jamais var. Toujours let ou const.


Erreur #7 : Promesses Non Catchées

Le Bug

// ❌ Erreur silencieuse
async function fetchData() {
  const response = await fetch('/api/data');
  return response.json();
}

fetchData(); // Si erreur, rien ne se passe (ou UnhandledPromiseRejection)

// ❌ Catch qui ne catch pas
async function process() {
  try {
    fetchData(); // ❌ Oubli du await !
    console.log('OK');
  } catch (e) {
    console.log('Erreur'); // Ne sera jamais appelé
  }
}

Pourquoi Ça Arrive

Sans await, la promesse est lancée mais non attendue. Le try/catch ne peut pas intercepter une erreur asynchrone non awaited.

La Solution

// ✅ Toujours await les promesses dans try/catch
async function process() {
  try {
    await fetchData();
    console.log('OK');
  } catch (e) {
    console.log('Erreur:', e.message);
  }
}

// ✅ Handler global pour les promesses non catchées
window.addEventListener('unhandledrejection', event => {
  console.error('Promesse non gérée:', event.reason);
  // Envoyer à un service de monitoring
});

// ✅ Wrapper utilitaire
async function safeAsync(promise) {
  try {
    const data = await promise;
    return [null, data];
  } catch (error) {
    return [error, null];
  }
}

const [error, data] = await safeAsync(fetchData());
if (error) {
  // Gérer l'erreur
}

Règle d'or : Chaque await devrait être dans un try/catch ou avoir un .catch().


Erreur #8 : Vérification de Valeurs Falsy

Le Bug

function greet(name) {
  if (!name) {
    name = 'Anonymous'; // ❌ Écrase les valeurs falsy valides
  }
  return `Hello, ${name}`;
}

greet('');  // 'Hello, Anonymous' - OK si voulu
greet(0);   // 'Hello, Anonymous' - 😱 0 est falsy !
greet(false); // 'Hello, Anonymous' - 😱 false est falsy !

Pourquoi Ça Arrive

En JavaScript, 0, '', false, null, undefined, et NaN sont tous falsy.

La Solution

// ✅ Vérification explicite de null/undefined
function greet(name) {
  if (name === null || name === undefined) {
    name = 'Anonymous';
  }
  return `Hello, ${name}`;
}

// ✅ Nullish coalescing (??)
function greet(name) {
  const displayName = name ?? 'Anonymous';
  // ?? ne remplace que null et undefined, pas 0 ou ''
  return `Hello, ${displayName}`;
}

// ✅ Paramètre par défaut
function greet(name = 'Anonymous') {
  return `Hello, ${name}`;
}
// ⚠️ Attention : ne s'applique que si undefined, pas null

// ✅ Optional chaining + nullish coalescing
const userName = user?.profile?.name ?? 'Anonymous';

Règle d'or : Utilisez ?? au lieu de || quand 0 ou '' sont des valeurs valides.


Erreur #9 : Typeof et Arrays

Le Bug

console.log(typeof []);        // 'object' 😱
console.log(typeof null);      // 'object' 😱
console.log(typeof NaN);       // 'number' 😱
console.log(typeof new Date()); // 'object'

// ❌ Mauvaise vérification
if (typeof data === 'object') {
  data.forEach(item => {}); // TypeError si data est null ou un objet
}

Pourquoi Ça Arrive

typeof est limité. C'est un vestige des débuts de JavaScript.

La Solution

// ✅ Vérifier un array
Array.isArray([]);        // true
Array.isArray({});        // false
Array.isArray('string');  // false

// ✅ Vérifier null
value === null;

// ✅ Vérifier NaN
Number.isNaN(NaN);        // true
Number.isNaN('NaN');      // false (contrairement à isNaN global)

// ✅ Vérifier un type précis
Object.prototype.toString.call([]);     // '[object Array]'
Object.prototype.toString.call(null);   // '[object Null]'
Object.prototype.toString.call(new Date()); // '[object Date]'

// ✅ Fonction utilitaire
function getType(value) {
  if (value === null) return 'null';
  if (Array.isArray(value)) return 'array';
  return typeof value;
}

Règle d'or : Array.isArray() pour les tableaux, === null pour null.


Erreur #10 : Event Listeners Non Nettoyés

Le Bug

// ❌ Memory leak
function setupComponent() {
  window.addEventListener('resize', handleResize);
  document.addEventListener('click', handleClick);
}

// Appelé plusieurs fois sans cleanup
setupComponent();
setupComponent();
setupComponent();
// 3 listeners pour chaque événement ! 😱

// ❌ Dans React (class components)
componentDidMount() {
  window.addEventListener('resize', this.handleResize);
}
// Oubli du componentWillUnmount = memory leak

Pourquoi Ça Arrive

Les event listeners persistent tant qu'ils ne sont pas explicitement retirés. Chaque ajout crée une nouvelle référence.

La Solution

// ✅ Cleanup explicite
function setupComponent() {
  const handleResize = () => { /* ... */ };
  const handleClick = () => { /* ... */ };

  window.addEventListener('resize', handleResize);
  document.addEventListener('click', handleClick);

  // Retourner une fonction de cleanup
  return () => {
    window.removeEventListener('resize', handleResize);
    document.removeEventListener('click', handleClick);
  };
}

const cleanup = setupComponent();
// Plus tard...
cleanup();

// ✅ AbortController (moderne)
const controller = new AbortController();

window.addEventListener('resize', handleResize, { signal: controller.signal });
document.addEventListener('click', handleClick, { signal: controller.signal });

// Cleanup en une ligne
controller.abort();

// ✅ React useEffect
useEffect(() => {
  const handleResize = () => { /* ... */ };
  window.addEventListener('resize', handleResize);

  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

Règle d'or : Chaque addEventListener doit avoir son removeEventListener correspondant.


La Checklist Anti-Bugs

□ Utilisez des arrow functions pour les callbacks
□ N'utilisez jamais forEach avec async/await
□ Toujours === sauf pour null check
□ structuredClone() pour les deep copies
□ Parenthèses pour retourner un objet: () => ({ })
□ let/const, jamais var
□ try/catch autour de chaque await
□ ?? au lieu de || pour les valeurs falsy
□ Array.isArray() pour vérifier les tableaux
□ Cleanup des event listeners

Le Mot de la Fin

Ces erreurs ne sont pas des signes d'incompétence. JavaScript est un langage avec des comportements parfois surprenants.

La différence entre un junior et un senior ? Le senior connaît ces pièges et met en place des garde-fous (linting, tests, code reviews).

Configurez ESLint correctement. La moitié de ces erreurs sont détectables automatiquement.


Besoin d'un audit de qualité de code ? On review votre codebase et on identifie les problèmes potentiels. Discutons-en

Partager :

Prêt à lancer votre projet ?

Transformez vos idées en réalité avec un développeur passionné par la performance et le SEO. Discutons de votre projet dès aujourd'hui.

Demander un devis
Voir mes réalisations
Réponse < 48h
15+ projets livrés
100% satisfaction client

Table des matières

Articles similaires

Comment Debugger N'importe Quel Bug en 15 Minutes
Développement

Comment Debugger N'importe Quel Bug en 15 Minutes

15 décembre 2025
9 min read
J'ai Audité le Site de Mon Boulanger : 47 Problèmes en 10 Minutes
Audit

J'ai Audité le Site de Mon Boulanger : 47 Problèmes en 10 Minutes

14 décembre 2025
10 min read
Pourquoi 90% des Refontes de Site Échouent (Et Comment Éviter le Piège)
Gestion de Projet

Pourquoi 90% des Refontes de Site Échouent (Et Comment Éviter le Piège)

12 décembre 2025
8 min read
RAICODE

Développeur Full-Stack spécialisé en Next.js & React.
Je crée des applications web performantes et sur mesure.

SERVICES

  • Sites Vitrines
  • Applications SaaS
  • E-commerce
  • API & Backend

NAVIGATION

  • Processus
  • Projets
  • Blog
  • Tarifs
  • Contact

LÉGAL

  • Mentions légales
  • Confidentialité
  • CGU
  • CGV

© 2025 Raicode. Tous droits réservés.

Créé parRaicode.
↑ Retour en haut