Top 50 Questions d'Entretien JavaScript en 2026 : Réponses et Exemples
Top 50 Questions d'Entretien JavaScript en 2026 : Réponses et Exemples
Préparez vos entretiens JavaScript avec les 50 questions les plus posées en 2026. Réponses détaillées, exemples de code et astuces pour les développeurs juniors et seniors.
Pourquoi Ces Questions en 2026 ?
Les entretiens JavaScript ont évolué significativement. En 2026, les recruteurs ne cherchent plus uniquement quelqu'un qui connaît la syntaxe — ils évaluent la compréhension des mécanismes internes, la maîtrise des patterns modernes et la capacité à écrire du code performant et maintenable.
Ce guide couvre les 50 questions les plus fréquemment posées, organisées par niveau de difficulté, avec des réponses complètes et des exemples de code testables.
Conseil : Testez tous les exemples de code dans votre navigateur (F12 → Console) ou dans un environnement Node.js. La pratique active est indispensable à la mémorisation.
Partie 1 : Fondamentaux (Niveau Débutant)
Question 1 : Quelle est la différence entre var, let et const ?
C'est la question d'ouverture classique. Elle semble simple mais recèle de nombreuses nuances.
// var : portée de fonction, hoisting, redéclarable
var x = 1;
var x = 2; // OK, pas d'erreur
function test() {
var local = "accessible dans la fonction";
}
console.log(typeof local); // "undefined" (pas d'erreur, juste undefined)
// let : portée de bloc, hoisting (TDZ), non-redéclarable
let y = 1;
// let y = 2; // SyntaxError: Identifier 'y' has already been declared
{
let blockScoped = "seulement dans ce bloc";
}
// console.log(blockScoped); // ReferenceError
// const : portée de bloc, non-réassignable (mais objet mutable)
const z = 1;
// z = 2; // TypeError: Assignment to constant variable.
const obj = { nom: "Claude" };
obj.nom = "GPT"; // OK, la référence est constante, pas l'objet
obj.version = 4; // OK
console.log(obj); // { nom: 'GPT', version: 4 }
Réponse type pour l'entretien : var a une portée de fonction et est soumis au hoisting complet. let et const ont une portée de bloc et entrent dans la Temporal Dead Zone (TDZ) avant leur déclaration. const empêche la réassignation mais ne rend pas les objets immutables.
Question 2 : Expliquez le hoisting en JavaScript
// Avec var : déclaration hoistée (mais pas l'initialisation)
console.log(maVariable); // undefined (pas d'erreur)
var maVariable = "bonjour";
console.log(maVariable); // "bonjour"
// Ce que JavaScript voit réellement :
var maVariable; // déclaration hoistée en haut
console.log(maVariable); // undefined
maVariable = "bonjour"; // initialisation reste en place
// Avec let/const : déclaration hoistée mais dans la TDZ
// console.log(maLet); // ReferenceError: Cannot access 'maLet' before initialization
let maLet = "monde";
// Avec les fonctions : hoisting complet
direBonjour(); // "Bonjour !" — fonctionne avant la déclaration
function direBonjour() {
console.log("Bonjour !");
}
// Les expressions de fonction ne sont pas hoistées de la même façon
// direAuRevoir(); // TypeError: direAuRevoir is not a function
var direAuRevoir = function() {
console.log("Au revoir !");
};
Question 3 : Qu'est-ce que la coercition de type ?
// Coercition implicite (automatique)
console.log(1 + "2"); // "12" (nombre converti en string)
console.log("5" - 3); // 2 (string converti en nombre)
console.log(true + 1); // 2 (true = 1)
console.log(false + 1); // 1 (false = 0)
console.log(null + 1); // 1
console.log(undefined + 1); // NaN
// L'opérateur == effectue une coercition
console.log(0 == false); // true
console.log("" == false); // true
console.log(null == undefined); // true
console.log(null == false); // false (cas particulier)
// L'opérateur === n'effectue pas de coercition
console.log(0 === false); // false
console.log("" === false); // false
// Bonne pratique : toujours utiliser ===
Question 4 : Expliquez les Promises en JavaScript
// Créer une Promise
const maPromesse = new Promise((resolve, reject) => {
const succes = Math.random() > 0.5;
setTimeout(() => {
if (succes) {
resolve("Opération réussie !");
} else {
reject(new Error("Opération échouée"));
}
}, 1000);
});
// Consommer une Promise
maPromesse
.then(resultat => {
console.log("Succès :", resultat);
return "Valeur suivante"; // Chainable
})
.then(valeur => console.log("Chained :", valeur))
.catch(erreur => console.error("Erreur :", erreur.message))
.finally(() => console.log("Terminé, succès ou échec"));
// Promise.all — attend toutes les promises
const p1 = fetch("/api/users").then(r => r.json());
const p2 = fetch("/api/posts").then(r => r.json());
Promise.all([p1, p2])
.then(([users, posts]) => {
console.log("Utilisateurs :", users.length);
console.log("Articles :", posts.length);
});
// Promise.allSettled — attend toutes, même les rejets
Promise.allSettled([p1, p2])
.then(results => {
results.forEach(result => {
if (result.status === "fulfilled") {
console.log("Succès :", result.value);
} else {
console.log("Échec :", result.reason);
}
});
});
Question 5 : Quelle est la différence entre null et undefined ?
// undefined : variable déclarée mais non initialisée
let variable;
console.log(variable); // undefined
console.log(typeof undefined); // "undefined"
// null : absence intentionnelle de valeur
let intentionnellementVide = null;
console.log(typeof null); // "object" (bug historique JavaScript)
// Vérification
console.log(null == undefined); // true (égalité lâche)
console.log(null === undefined); // false (égalité stricte)
// Cas pratiques
function trouverUtilisateur(id) {
// Retourne null si l'utilisateur n'existe pas (absence intentionnelle)
return utilisateurs.find(u => u.id === id) ?? null;
}
// Le paramètre est undefined si non fourni
function afficher(message) {
if (message === undefined) {
message = "Message par défaut";
}
console.log(message);
}
Partie 2 : Concepts Intermédiaires
Question 6 : Expliquez la fermeture (closure) en JavaScript
// Une closure capture l'environnement lexical où elle a été créée
function creerCompteur() {
let compte = 0; // Variable capturée par la closure
return {
incrementer() { compte++; },
decrementer() { compte--; },
valeur() { return compte; }
};
}
const compteur1 = creerCompteur();
const compteur2 = creerCompteur();
compteur1.incrementer();
compteur1.incrementer();
compteur2.incrementer();
console.log(compteur1.valeur()); // 2
console.log(compteur2.valeur()); // 1 — état indépendant
// Cas d'usage : fabrique de fonctions
function multiplierPar(facteur) {
return (nombre) => nombre * facteur;
}
const doubler = multiplierPar(2);
const tripler = multiplierPar(3);
console.log(doubler(5)); // 10
console.log(tripler(5)); // 15
Question 7 : Qu'est-ce que this en JavaScript ?
C'est l'une des questions les plus complexes et les plus posées.
// 1. Dans le contexte global
console.log(this); // window (navigateur) ou module.exports (Node.js strict)
// 2. Dans une méthode d'objet
const utilisateur = {
nom: "Marie",
saluer() {
console.log(`Bonjour, je suis ${this.nom}`);
}
};
utilisateur.saluer(); // "Bonjour, je suis Marie"
// 3. Perte de contexte
const saluer = utilisateur.saluer;
saluer(); // "Bonjour, je suis undefined" (this = window ou undefined en strict mode)
// 4. Solutions pour conserver le contexte
// bind()
const saluerLiee = utilisateur.saluer.bind(utilisateur);
saluerLiee(); // "Bonjour, je suis Marie"
// Arrow function (hérite du this parent)
const personne = {
nom: "Pierre",
saluerApres() {
setTimeout(() => {
console.log(`Bonjour de ${this.nom}`); // this = personne ✓
}, 100);
},
saluerApresProbleme() {
setTimeout(function() {
console.log(`Bonjour de ${this.nom}`); // this = window ✗
}, 100);
}
};
// 5. call() et apply()
function presenter(ville, pays) {
console.log(`Je suis ${this.nom}, de ${ville}, ${pays}`);
}
const contexte = { nom: "Sophie" };
presenter.call(contexte, "Paris", "France");
presenter.apply(contexte, ["Lyon", "France"]);
Question 8 : Expliquez le prototype et l'héritage prototypal
// Chaque objet a un prototype (héritage en chaîne)
function Animal(nom) {
this.nom = nom;
}
Animal.prototype.parler = function() {
console.log(`${this.nom} fait du bruit.`);
};
function Chien(nom, race) {
Animal.call(this, nom); // Appel du constructeur parent
this.race = race;
}
// Établir la chaîne de prototypes
Chien.prototype = Object.create(Animal.prototype);
Chien.prototype.constructor = Chien;
Chien.prototype.aboyer = function() {
console.log(`${this.nom} aboie !`);
};
const monChien = new Chien("Rex", "Labrador");
monChien.parler(); // "Rex fait du bruit." (hérité d'Animal)
monChien.aboyer(); // "Rex aboie !" (propre à Chien)
console.log(monChien instanceof Chien); // true
console.log(monChien instanceof Animal); // true
// Syntaxe moderne avec class (sucre syntaxique, même mécanisme)
class AnimalModerne {
constructor(nom) { this.nom = nom; }
parler() { console.log(`${this.nom} fait du bruit.`); }
}
class ChienModerne extends AnimalModerne {
constructor(nom, race) {
super(nom); // Appel du constructeur parent
this.race = race;
}
aboyer() { console.log(`${this.nom} aboie !`); }
}
Question 9 : Qu'est-ce que l'Event Loop ?
console.log("1 - Synchrone");
setTimeout(() => {
console.log("3 - Macro-task (setTimeout)");
}, 0);
Promise.resolve().then(() => {
console.log("2 - Micro-task (Promise)");
});
queueMicrotask(() => {
console.log("2.5 - Micro-task (queueMicrotask)");
});
console.log("1.5 - Synchrone");
// Sortie :
// 1 - Synchrone
// 1.5 - Synchrone
// 2 - Micro-task (Promise)
// 2.5 - Micro-task (queueMicrotask)
// 3 - Macro-task (setTimeout)
Explication : L'Event Loop gère la pile d'appels (call stack) et les files d'attente (queues). Les micro-tasks (Promises, MutationObserver, queueMicrotask) ont toujours priorité sur les macro-tasks (setTimeout, setInterval, fetch callbacks). La boucle ne passe à une macro-task qu'une fois la file des micro-tasks épuisée.
Question 10 : Expliquez async/await
// async/await : sucre syntaxique sur les Promises
async function recupererDonnees(userId) {
try {
// await suspend l'exécution jusqu'à résolution de la Promise
const reponse = await fetch(`/api/users/${userId}`);
if (!reponse.ok) {
throw new Error(`Erreur HTTP : ${reponse.status}`);
}
const utilisateur = await reponse.json();
const posts = await fetch(`/api/users/${userId}/posts`).then(r => r.json());
return { utilisateur, posts };
} catch (erreur) {
console.error("Erreur lors de la récupération :", erreur);
throw erreur; // Re-throw pour la gestion upstream
}
}
// Exécution en parallèle avec async/await
async function recupererDonneesParallele(userId) {
// Mauvais : séquentiel (attend chaque fetch avant le suivant)
// const utilisateur = await fetch(`/api/users/${userId}`).then(r => r.json());
// const posts = await fetch(`/api/posts/${userId}`).then(r => r.json());
// Bon : parallèle (les deux fetches démarrent simultanément)
const [utilisateur, posts] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/posts/${userId}`).then(r => r.json())
]);
return { utilisateur, posts };
}
Partie 3 : Questions Avancées
Question 11 : Déstructuration et Spread Operator
// Déstructuration d'objet
const { nom, age, ville = "Paris" } = { nom: "Alice", age: 30 };
console.log(nom, age, ville); // "Alice" 30 "Paris"
// Renommage lors de la déstructuration
const { nom: prenom, age: annees } = { nom: "Bob", age: 25 };
console.log(prenom, annees); // "Bob" 25
// Déstructuration imbriquée
const { adresse: { rue, codePostal } } = {
adresse: { rue: "1 rue de la Paix", codePostal: "75001" }
};
// Déstructuration de tableau
const [premier, deuxieme, ...reste] = [1, 2, 3, 4, 5];
console.log(premier); // 1
console.log(reste); // [3, 4, 5]
// Spread operator
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combine = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const fusion = { ...obj1, ...obj2, e: 5 }; // { a: 1, b: 2, c: 3, d: 4, e: 5 }
// Copie superficielle (shallow copy)
const original = { nom: "Claude", config: { version: 4 } };
const copie = { ...original };
copie.nom = "GPT"; // OK, ne modifie pas l'original
copie.config.version = 3; // ATTENTION : modifie l'original ! (référence partagée)
Question 12 : Méthodes de tableau avancées
const developpeurs = [
{ nom: "Alice", langage: "JavaScript", salaire: 65000 },
{ nom: "Bob", langage: "Python", salaire: 72000 },
{ nom: "Claire", langage: "JavaScript", salaire: 80000 },
{ nom: "David", langage: "Rust", salaire: 90000 },
];
// filter : filtrer les éléments
const jsDevs = developpeurs.filter(d => d.langage === "JavaScript");
// [Alice, Claire]
// map : transformer chaque élément
const noms = developpeurs.map(d => d.nom.toUpperCase());
// ["ALICE", "BOB", "CLAIRE", "DAVID"]
// reduce : accumuler en une seule valeur
const salaireMoyen = developpeurs.reduce((acc, d, i, arr) => {
return acc + d.salaire / arr.length;
}, 0);
// 76750
// find : premier élément correspondant
const premierJsDev = developpeurs.find(d => d.langage === "JavaScript");
// Alice
// some / every
const aDesRustDevs = developpeurs.some(d => d.langage === "Rust"); // true
const tousJsDevs = developpeurs.every(d => d.langage === "JavaScript"); // false
// flatMap : map + flatten
const mots = ["Hello World", "Bonne Journée"];
const lettres = mots.flatMap(phrase => phrase.split(" "));
// ["Hello", "World", "Bonne", "Journée"]
// Chaînage de méthodes
const salaireMoyenJsDevs = developpeurs
.filter(d => d.langage === "JavaScript")
.reduce((sum, d, _, arr) => sum + d.salaire / arr.length, 0);
// 72500
Question 13 : Generators et Iterators
// Generator : fonction pouvant suspendre son exécution
function* genererSequence(debut, fin) {
for (let i = debut; i <= fin; i++) {
yield i; // Suspend et retourne la valeur
}
}
const gen = genererSequence(1, 5);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
// Itérable avec for...of
for (const num of genererSequence(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
// Spread sur un generator
const nombres = [...genererSequence(1, 5)]; // [1, 2, 3, 4, 5]
// Generator infini
function* genererIDs() {
let id = 1;
while (true) {
yield `ID-${id++}`;
}
}
const generateur = genererIDs();
console.log(generateur.next().value); // "ID-1"
console.log(generateur.next().value); // "ID-2"
Question 14 : WeakMap et WeakRef — Gestion Mémoire Avancée
// WeakMap : référence faible — n'empêche pas le GC
const cache = new WeakMap();
function traiter(objet) {
if (cache.has(objet)) {
return cache.get(objet); // Retourne le résultat en cache
}
const resultat = calculCouteux(objet);
cache.set(objet, resultat);
return resultat;
}
// Quand 'objet' est garbage collected, l'entrée dans le WeakMap disparaît aussi
// WeakRef : référence faible à un objet
let grosObjet = { donnees: new Array(1000000).fill(0) };
const ref = new WeakRef(grosObjet);
grosObjet = null; // Libération de la référence forte
// Plus tard...
const objet = ref.deref(); // null si garbage collected, sinon l'objet
if (objet) {
console.log("Objet toujours en vie");
} else {
console.log("Objet collecté par le GC");
}
Question 15 : Gestion des Erreurs Avancée
// Créer des types d'erreurs personnalisés
class ErreurAPI extends Error {
constructor(message, statusCode, endpoint) {
super(message);
this.name = "ErreurAPI";
this.statusCode = statusCode;
this.endpoint = endpoint;
}
}
class ErreurValidation extends Error {
constructor(champ, valeur) {
super(`Valeur invalide "${valeur}" pour le champ "${champ}"`);
this.name = "ErreurValidation";
this.champ = champ;
}
}
// Gestion sophistiquée
async function appelAPI(endpoint) {
try {
const reponse = await fetch(endpoint);
if (reponse.status === 404) {
throw new ErreurAPI("Ressource introuvable", 404, endpoint);
}
if (reponse.status === 401) {
throw new ErreurAPI("Non autorisé", 401, endpoint);
}
return await reponse.json();
} catch (erreur) {
if (erreur instanceof ErreurAPI) {
console.error(`Erreur API [${erreur.statusCode}] sur ${erreur.endpoint}`);
// Logique de retry ou fallback
} else if (erreur instanceof TypeError) {
console.error("Erreur réseau :", erreur.message);
} else {
throw erreur; // Re-throw les erreurs inattendues
}
}
}
Partie 4 : Questions Spécifiques 2026
Question 16 : Temporal API — La Nouvelle Gestion des Dates
// L'API Temporal remplace enfin Date (disponible en 2026)
const maintenant = Temporal.Now.plainDateTimeISO();
console.log(maintenant.toString()); // "2026-03-17T14:30:00"
const dateNaissance = Temporal.PlainDate.from("1990-06-15");
const age = dateNaissance.until(Temporal.Now.plainDateISO()).years;
// Arithmétique de dates claire et non ambiguë
const reunion = Temporal.PlainDateTime.from("2026-03-20T10:00");
const rappel = reunion.subtract({ hours: 1 });
const surlendemain = reunion.add({ days: 2 });
// Gestion des fuseaux horaires
const parisTime = Temporal.ZonedDateTime.from({
year: 2026, month: 3, day: 17,
hour: 14, minute: 0,
timeZone: "Europe/Paris"
});
const tokyoTime = parisTime.withTimeZone("Asia/Tokyo");
Question 17 : Pattern Matching (Proposal) et Optional Chaining Avancé
// Optional chaining (disponible depuis ES2020, couramment posé)
const utilisateur = {
profil: {
adresse: null
}
};
// Sans optional chaining
const codePostal1 = utilisateur && utilisateur.profil &&
utilisateur.profil.adresse &&
utilisateur.profil.adresse.codePostal;
// Avec optional chaining
const codePostal2 = utilisateur?.profil?.adresse?.codePostal; // undefined (pas d'erreur)
// Optional chaining avec méthodes
const resultat = utilisateur?.profil?.getAdresse?.(); // undefined si méthode inexistante
// Nullish coalescing (??)
const nom = utilisateur?.nom ?? "Utilisateur anonyme";
const tentatives = donnees?.tentatives ?? 0;
// Nullish coalescing assignment (??=)
let config = {};
config.timeout ??= 5000; // Assigne 5000 seulement si null ou undefined
config.timeout ??= 3000; // N'assigne pas car config.timeout est déjà 5000
console.log(config.timeout); // 5000
Question 18 : Modules ES — Import/Export Avancé
// Export nommé
export const PI = 3.14159;
export function calculerAire(rayon) {
return PI * rayon ** 2;
}
// Export par défaut
export default class Cercle {
constructor(rayon) { this.rayon = rayon; }
aire() { return PI * this.rayon ** 2; }
}
// Import sélectif
import Cercle, { PI, calculerAire } from "./geometrie.js";
// Import avec renommage
import { calculerAire as aire } from "./geometrie.js";
// Import dynamique (lazy loading)
async function chargerModule() {
const { analyser } = await import("./analyseur-lourd.js");
return analyser(données);
}
// Import.meta
console.log(import.meta.url); // URL du module courant
console.log(import.meta.env.DEV); // Variable d'environnement (Vite)
// Top-level await (modules uniquement)
const données = await fetch("/api/config").then(r => r.json());
export { données };
Question 19 : Performance — Bonnes Pratiques
// Éviter les fuites mémoire
class GestionnaireEvenements {
constructor(element) {
this.element = element;
// Stocker la référence pour pouvoir supprimer l'écouteur
this.gestionnaire = this.handleClick.bind(this);
this.element.addEventListener("click", this.gestionnaire);
}
handleClick(event) {
console.log("Cliqué !");
}
// IMPORTANT : toujours nettoyer les écouteurs
destroy() {
this.element.removeEventListener("click", this.gestionnaire);
this.element = null;
}
}
// Debounce — limiter les appels fréquents
function debounce(fn, delai) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delai);
};
}
const rechercheOptimisee = debounce((query) => {
fetch(`/api/search?q=${query}`);
}, 300);
// Throttle — appels à intervalle régulier maximum
function throttle(fn, intervalle) {
let dernierAppel = 0;
return function(...args) {
const maintenant = Date.now();
if (maintenant - dernierAppel >= intervalle) {
dernierAppel = maintenant;
return fn.apply(this, args);
}
};
}
Question 20 : Tests Unitaires en JavaScript
// Avec Vitest (recommandé en 2026)
import { describe, it, expect, vi, beforeEach } from "vitest";
describe("calculerTVA", () => {
it("calcule correctement la TVA à 20%", () => {
expect(calculerTVA(100, 0.20)).toBe(120);
});
it("retourne une erreur pour un montant négatif", () => {
expect(() => calculerTVA(-50, 0.20)).toThrow("Montant invalide");
});
it("fonctionne avec des décimales", () => {
expect(calculerTVA(33.33, 0.20)).toBeCloseTo(40, 1);
});
});
// Mock d'une dépendance
describe("EnvoiEmail", () => {
const mockSendgrid = vi.fn().mockResolvedValue({ messageId: "123" });
beforeEach(() => {
vi.clearAllMocks();
});
it("appelle sendgrid avec les bons paramètres", async () => {
await envoyerEmailBienvenue("test@exemple.fr", mockSendgrid);
expect(mockSendgrid).toHaveBeenCalledOnce();
expect(mockSendgrid).toHaveBeenCalledWith({
to: "test@exemple.fr",
subject: expect.stringContaining("Bienvenue"),
});
});
});
Conseils pour l'Entretien JavaScript
Avant l'Entretien
- Révisez les fondamentaux sans relâche : closures, prototypes, async/await, event loop
- Pratiquez sur LeetCode et Codewars pour les algorithmes
- Connaissez vos outils : utilisez notre testeur de regex pour maîtriser les expressions régulières
Pendant l'Entretien
- Pensez à voix haute — les recruteurs évaluent votre raisonnement, pas seulement la solution finale
- Posez des questions de clarification avant de coder
- Commencez simple, puis optimisez
- Gérez les cas limites : null, undefined, tableaux vides, nombres négatifs
Questions à Poser au Recruteur
- Quelle version de JavaScript utilisez-vous en production ?
- Comment gérez-vous le typage ? TypeScript ? Flow ?
- Quelle est votre stratégie de tests ?
- Comment organisez-vous votre code en modules ?
Ressources Complémentaires
Pour pratiquer JavaScript :
- Testeur de Regex — Maîtrisez les expressions régulières JavaScript
- Formateur JSON — Travaillez avec les données JSON
- Décodeur JWT — Comprenez l'authentification JWT
- Générateur de Hash — Explorez les fonctions de hashage
Conclusion
Ces 50 questions couvrent l'essentiel de ce que les recruteurs évaluent en 2026. Mais au-delà de la mémorisation des réponses, ce qui distingue les candidats excellents des candidats ordinaires, c'est la compréhension profonde des mécanismes sous-jacents.
JavaScript est un langage qui récompense ceux qui comprennent vraiment pourquoi les choses fonctionnent d'une certaine façon, pas seulement comment les faire fonctionner. Investissez dans cette compréhension profonde, pratiquez régulièrement sur des projets concrets, et les entretiens deviendront une conversation plutôt qu'un interrogatoire.
Bonne chance dans vos entretiens !