Comprendre et corriger « [object Object] » en JavaScript : causes, prototype, toString et solutions d’affichage
Un alert(monObjet) qui affiche [object Object]. Tout développeur JavaScript y passe un jour. Ce n’est pas un bug, pas une erreur — c’est le comportement par défaut du langage quand on force un objet à devenir une string. Sauf que 90 % des tutos se contentent de dire « utilisez JSON.stringify ». Le vrai sujet va bien plus loin : prototype, toString, coercition implicite, objets sans prototype (nullprotoobj), et pièges autour de this.
On va décortiquer le mécanisme complet, avec du code qui tourne, des cas concrets et un arbre de décision pour ne plus jamais rester bloqué sur ce output.
Pourquoi JavaScript retourne « [object Object] » : coercition, toString() et chaîne de prototype
Quand on écrit "Résultat : " + monObjet, le moteur JavaScript doit convertir cet objet en string. Il remonte la chaîne de prototype jusqu’à trouver une méthode toString(). Sur un objet standard, cette méthode vit sur Object.prototype.
const user = { name: "Alice", age: 28 };
console.log(String(user)); // "[object Object]"
L’output [object Object] suit un format précis : [object Type]. Le premier « object » est fixe, le second représente le tag interne. Pour un objet ordinaire, ce tag vaut « Object ». Pour une Date, ce serait « Date ». Pour une RegExp, « RegExp ».
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call("test"); // "[object String]"
La méthode Object.prototype.toString utilise this pour déterminer le type de la value passée. Quand on appelle .call(value), le this pointe sur cette value, et le prototype retourne la representation interne.
💡 Conseil :
Object.prototype.toString.call(x)reste la technique la plus fiable pour identifier le type réel d’une value en JavaScript — plus précis quetypeofqui retourne"object"pour les tableaux etnull.
Trois choses à retenir sur ce mécanisme :
- Le prototype de tout objet ordinaire remonte vers
Object.prototype - La méthode
toString()sur ce prototype retourne toujours"[object Object]"sauf override - La coercition en string est implicite dans beaucoup de contextes (concat, template, alert, display DOM)
Les 4 contextes où « [object Object] » apparaît (et le diagnostic pour chacun)
alert(objet) — le piège classique
const config = { api: "https://api.example.com", timeout: 3000 };
alert(config); // "[object Object]"
La function alert() convertit systématiquement son argument en string. Aucun moyen de contourner ça — alert ne sait pas afficher un objet interactif. L’output sera toujours la representation string.
Concaténation et template strings
const produit = { name: "MacBook Pro M4", prix: 2399 };
const msg = "Commande : " + produit; // "Commande : [object Object]"
const msg2 = `Produit sélectionné : ${produit}`; // même résultat
Les deux syntaxes déclenchent la même coercition. Le moteur appelle toString() sur l’objet via son prototype, et retourne la string par défaut.
Display dans le DOM
const data = { score: 42, level: "expert" };
document.getElementById("result").textContent = data;
// Affiche "[object Object]" dans la page
Assigner un objet à textContent provoque une conversion en string. Le navigateur ne va pas sérialiser les propriétés automatiquement — il appelle toString() et affiche l’output.
console.log — le faux ami
const user = { name: "Bob", role: "admin" };
console.log("User: " + user); // "User: [object Object]"
console.log(user); // { name: "Bob", role: "admin" } ← objet interactif
La différence est subtile mais décisive. console.log(user) passe l’objet directement — la console l’affiche comme un objet interactif avec ses propriétés. Mais console.log("User: " + user) force d’abord la conversion en string via le prototype, puis log cette string.
⚠️ Attention :
console.logaffiche une référence « live » de l’objet. Si l’objet change après le log, la console peut montrer les valeurs modifiées. Pour figer un snapshot, passez parJSON.stringifyavant le log.
Checklist de diagnostic — identifier où l’objet devient une string :
- Cherchez les concaténations avec
+impliquant un objet - Vérifiez les template strings
${}contenant un objet - Inspectez les assignations
textContentouinnerHTMLavec un objet - Repérez les appels
alert()avec un objet en paramètre - Vérifiez si
console.logreçoit un objet seul ou une string concaténée
Arbre de décision : inspecter, afficher ou formater un objet
Avant de chercher une solution, posez-vous la bonne question. Le problème n’est pas le même selon le besoin.
Besoin d’inspecter (debug) ? Utilisez console.log(objet) directement — sans concat, sans conversion. La console affiche l’objet avec toutes ses propriétés. Pas besoin de stringify ici.
Besoin d’afficher dans l’UI (display string) ? Deux options : accéder directement à la propriété voulue (objet.name, objet.prix) ou utiliser JSON.stringify(objet) pour une vue complète. Le choix dépend du contexte.
Besoin d’un format contrôlé ? Définissez une méthode toString() custom sur votre objet ou sa class. On y vient dans la section suivante.
Règle d’or : ne jamais concaténer un objet avec une string si on veut voir ses propriétés. Accédez à la propriété par sa key, ou sérialisez explicitement.
console.log, console.dir, console.table — les bons outils pour le debug
console.log reste l’outil de base. Passez l’objet directement, sans le convertir.
const commande = {
id: "CMD-2026-0847",
client: { name: "Marie", email: "[email protected]" },
articles: [
{ name: "Clavier MX Keys S", prix: 119.99 },
{ name: "Souris MX Master 3S", prix: 99.99 }
],
total: 219.98
};
console.log(commande); // Objet interactif, dépliable
console.dir force un affichage sous forme d’objet JavaScript, même pour les éléments DOM (qui sont normalement affichés en HTML par console.log).
const btn = document.querySelector("button");
console.log(btn); // <button>Click</button> (HTML)
console.dir(btn); // HTMLButtonElement { ... } (objet avec propriétés)
console.table affiche un tableau formaté — idéal pour des objets avec des properties key/value simples ou des tableaux d’objets.
const scores = { Alice: 92, Bob: 87, Charlie: 95 };
console.table(scores);
// ┌─────────┬────────┐
// │ (index) │ Values │
// ├─────────┼────────┤
// │ Alice │ 92 │
// │ Bob │ 87 │
// │ Charlie │ 95 │
// └─────────┴────────┘
📌 À retenir :
console.tableaccepte un second argument (tableau de colonnes) pour filtrer l’affichage — pratique sur des objets avec 15+ propriétés.
Pour figer un snapshot d’objet qui risque de changer entre le moment du log et la lecture :
console.log(JSON.parse(JSON.stringify(commande)));
C’est lourd, mais ça produit une copie figée. Le prototype de la copie sera Object.prototype (copie profonde via stringify + parse).
JSON.stringify : la solution rapide pour afficher un objet en string lisible
const config = { host: "localhost", port: 8080, debug: true };
// Compact
console.log(JSON.stringify(config));
// {"host":"localhost","port":8080,"debug":true}
// Lisible (indenté)
console.log(JSON.stringify(config, null, 2));
// {
// "host": "localhost",
// "port": 8080,
// "debug": true
// }
Le second argument (replacer) permet de filtrer les propriétés à inclure. On passe soit un tableau de key, soit une function de transformation :
const user = { name: "Alice", password: "s3cr3t", role: "admin" };
// Filtrer par key
JSON.stringify(user, ["name", "role"], 2);
// { "name": "Alice", "role": "admin" }
// Replacer function
JSON.stringify(user, (key, value) => {
if (key === "password") return undefined; // exclure
return value;
}, 2);
Pour l’affichage dans le DOM (display UI), on combine stringify avec une balise <pre> :
const data = { articles: 12, mots: 18500, score: 87.3 };
document.getElementById("output").innerHTML =
`<pre><code>${JSON.stringify(data, null, 2)}</code></pre>`;
Stringify retourne une string — pas un objet. Cette string peut ensuite être utilisée partout où une chaîne est attendue : display DOM, alert, concat, log.
Limites de JSON.stringify : circular, function, undefined, BigInt
Stringify n’est pas une solution universelle. Plusieurs cas la font échouer ou produire un output incomplet.
Références circulaires — TypeError immédiat :
const a = {};
const b = { ref: a };
a.ref = b; // circulaire
JSON.stringify(a); // TypeError: Converting circular structure to JSON
Functions, undefined, Symbol — silencieusement ignorés :
const obj = {
name: "Test",
greet: function() { return "hello"; },
value: undefined,
id: Symbol("unique")
};
JSON.stringify(obj);
// {"name":"Test"} — les 3 propriétés disparaissent
BigInt — erreur directe :
const data = { count: BigInt(9007199254740991) };
JSON.stringify(data); // TypeError: Do not know how to serialize a BigInt
Snippet : safeStringify avec WeakSet
Pour gérer les références circulaires, un wrapper avec WeakSet fait le travail :
function safeStringify(obj, indent = 2) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
if (typeof value === "function") return "[Function]";
if (typeof value === "bigint") return value.toString();
return value;
}, indent);
}
Ce code intercepte chaque value avant sérialisation. Le WeakSet garde trace des objets déjà vus — si un objet revient, on retourne "[Circular]" au lieu de boucler. Les functions et BigInt reçoivent un traitement de fallback.
📊 Chiffre key : sur un benchmark Node.js 22 (2025, machine M3 Pro),
JSON.stringifytraite environ 150 000 objets simples (5 propriétés) par seconde. Le wrapper safeStringify divise ce chiffre par 3 environ — acceptable pour du debug, pas pour de la sérialisation en production.
Si vous travaillez sur un projet qui manipule des convertisseurs ou outils en ligne, ce genre de sérialisation custom devient vite nécessaire quand les structures de données se complexifient.
Personnaliser toString() : contrôler la chaîne que retourne un objet
Au lieu de subir [object Object], on peut définir sa propre méthode toString() directement sur l’objet ou sur le prototype d’une class.
const produit = {
name: "Galaxy S26 Ultra",
prix: 1459,
toString() {
return `${this.name} — ${this.prix} €`;
}
};
alert(produit); // "Galaxy S26 Ultra — 1459 €"
console.log("Sélection : " + produit); // "Sélection : Galaxy S26 Ultra — 1459 €"
La méthode toString() utilise this pour accéder aux propriétés de l’objet. Quand le moteur JavaScript doit convertir l’objet en string (concat, template, alert, display), il appelle cette méthode au lieu de remonter le prototype jusqu’à Object.prototype.toString.
Sur une class ES6, ça donne :
class Smartphone {
constructor(name, marque, prix) {
this.name = name;
this.marque = marque;
this.prix = prix;
}
toString() {
return `[Smartphone] ${this.marque} ${this.name} (${this.prix} €)`;
}
}
const phone = new Smartphone("Pixel 10 Pro", "Google", 1099);
console.log(`Mon choix : ${phone}`);
// "Mon choix : [Smartphone] Google Pixel 10 Pro (1099 €)"
Symbol.toStringTag : modifier la representation « [object X] »
Symbol.toStringTag change le tag interne utilisé par Object.prototype.toString :
class Database {
get [Symbol.toStringTag]() {
return "Database";
}
}
const db = new Database();
Object.prototype.toString.call(db); // "[object Database]"
Ce tag ne modifie PAS le comportement de votre propre toString() — il agit uniquement sur Object.prototype.toString.call(). Utile pour le typage et le debug, pas pour l’affichage utilisateur.
Bon, concrètement — ne modifiez jamais Object.prototype directement. Ajouter une méthode sur le prototype global affecte tous les objets du runtime. Préférez des méthodes sur les instances ou sur le prototype de vos propres classes.
Pour ceux qui développent des outils de conversion de données, le toString custom est le moyen le plus propre de produire un output lisible sans passer par stringify.
Objets avec prototype null (nullprotoobj) : les pièges cachés
Object.create(null) crée un objet sans prototype. Pas de toString, pas de hasOwnProperty, pas de valueOf. Rien.
const dict = Object.create(null);
dict.name = "config";
dict.value = 42;
console.log(dict.toString); // undefined
console.log(dict.hasOwnProperty); // undefined
String(dict); // TypeError: Cannot convert object to primitive value
Ce nullprotoobj est un dictionnaire pur — aucune propriété héritée ne peut entrer en collision avec vos key. C’est pourquoi certains frameworks (Express, Fastify) l’utilisent pour stocker des headers HTTP ou des options de configuration.
Le problème : impossible de convertir un nullprotoobj en string par les voies classiques. String() plante, alert() plante, la concaténation plante. Même Object.prototype.toString.call() retourne "[object Object]" sans information utile sur les propriétés.
Accéder aux propriétés d’un nullprotoobj en sécurité :
const nullObj = Object.create(null);
nullObj.host = "localhost";
nullObj.port = 3000;
// ❌ Dangereux
nullObj.hasOwnProperty("host"); // TypeError
// ✅ Pattern sûr
Object.prototype.hasOwnProperty.call(nullObj, "host"); // true
// ✅ Encore mieux (ES2022+)
Object.hasOwn(nullObj, "host"); // true
// Lister les key
Object.keys(nullObj); // ["host", "port"]
Object.entries(nullObj); // [["host", "localhost"], ["port", 3000]]
Afficher un nullprotoobj proprement :
// console.log fonctionne (pas de conversion string)
console.log(nullObj); // [Object: null prototype] { host: "localhost", port: 3000 }
// JSON.stringify fonctionne (n'utilise pas toString)
JSON.stringify(nullObj, null, 2);
// { "host": "localhost", "port": 3000 }
// Fallback custom
function displayObj(obj) {
return Object.entries(obj)
.map(([key, value]) => `${key}: ${value}`)
.join(", ");
}
displayObj(nullObj); // "host: localhost, port: 3000"
La bonne nouvelle : JSON.stringify fonctionne sur les nullprotoobj sans problème. Le moteur itère les propriétés propres (properties) sans passer par le prototype. Pour le display dans une UI, stringify reste la voie la plus sûre.
⚠️ Attention : si vous utilisez un nullprotoobj comme dictionnaire et testez l’existence d’une key avec
if (obj[key]), la valeurnullou0donnerontfalse. PréférezObject.hasOwn(obj, key)(ou le pattern.call()sur les runtimes plus anciens).
Tableau de conversion : value → output string selon le type
Voici ce que JavaScript retourne quand on force une conversion en string sur différentes values :
| Expression | Type | String(x) | x + "" | alert(x) output |
|---|---|---|---|---|
null | null | "null" | "null" | null |
undefined | undefined | "undefined" | "undefined" | undefined |
42 | number | "42" | "42" | 42 |
"hello" | string | "hello" | "hello" | hello |
true | boolean | "true" | "true" | true |
{} | object | "[object Object]" | "[object Object]" | [object Object] |
[] | object (Array) | "" | "" | (vide) |
[1,2,3] | object (Array) | "1,2,3" | "1,2,3" | 1,2,3 |
new Date() | object (Date) | date ISO string | date ISO string | date lisible |
function(){} | function | "function(){}" | "function(){}" | function(){} |
Object.create(null) | nullprotoobj | TypeError | TypeError | TypeError |
Pourquoi null est spécial : null est techniquement un type primitif, pas un objet. Pourtant typeof null retourne "object" — un bug historique de JavaScript (1995) jamais corrigé pour ne pas casser le web. La conversion en string fonctionne normalement ("null"), contrairement aux objets.
Les tableaux ([]) ont un toString() custom sur Array.prototype qui retourne les éléments joints par des virgules. C’est pourquoi [1,2,3] + "" donne "1,2,3" et pas "[object Array]".
Pour mieux comprendre les enjeux de compatibilité entre formats de données, notre guide sur les meilleures tablettes Android aborde aussi la gestion des formats de fichiers cross-platform.
Erreurs fréquentes et corrections : checklist de 8 pièges courants
1. Concaténer un objet au lieu d’accéder à sa propriété
// ❌
const user = { name: "Alice" };
element.textContent = "Bienvenue " + user; // "[object Object]"
// ✅
element.textContent = "Bienvenue " + user.name; // "Bienvenue Alice"
2. Afficher un objet entier dans un template string
// ❌
const config = { theme: "dark", lang: "fr" };
console.log(`Config : ${config}`); // "Config : [object Object]"
// ✅
console.log(`Config : ${JSON.stringify(config)}`);
3. Confondre input.value (string) et un objet parsé
// input.value retourne toujours une string
const raw = document.getElementById("data").value;
// Si raw = '{"name":"Bob"}', c'est une STRING, pas un objet
const obj = JSON.parse(raw); // maintenant c'est un objet
console.log(obj.name); // "Bob"
4. Oublier de sélectionner la bonne key dans un objet imbriqué
const response = { data: { users: [{ name: "Alice" }] } };
// ❌
console.log("Premier user : " + response.data.users); // output: objet
// ✅
console.log("Premier user : " + response.data.users[0].name);
5. Log un objet avec concat au lieu de virgule
// ❌ — conversion en string
console.log("User: " + user);
// ✅ — objet passé séparément, affiché interactif
console.log("User:", user);
6. Stocker un objet dans localStorage sans stringify
// ❌
localStorage.setItem("prefs", { theme: "dark" }); // stocke "[object Object]"
// ✅
localStorage.setItem("prefs", JSON.stringify({ theme: "dark" }));
7. Envoyer un objet comme body HTTP sans sérialisation
// ❌ — le body sera "[object Object]"
fetch("/api", { method: "POST", body: myData });
// ✅
fetch("/api", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(myData)
});
8. Utiliser toString/hasOwnProperty sur un nullprotoobj
const dict = Object.create(null);
dict.key = "value";
// ❌
dict.hasOwnProperty("key"); // TypeError
// ✅
Object.hasOwn(dict, "key"); // true
Checklist finale pour ne plus voir « [object Object] » :
- Vérifier si on passe un objet ou une propriété (key) de l’objet
- Préférer la virgule au
+dansconsole.log - Utiliser
JSON.stringifypour tout display en string - Définir
toString()sur les objets métier - Tester avec
typeofetObject.prototype.toString.call()en cas de doute sur le type
Les développeurs qui gèrent des applications multi-format rencontrent souvent ces problèmes de sérialisation quand les données passent d’un format à l’autre.
Prototype, héritage et toString : comment la chaîne de résolution fonctionne
Le prototype en JavaScript est un mécanisme de délégation. Quand on accède à une propriété ou une méthode sur un objet, le moteur cherche d’abord sur l’objet lui-même. S’il ne trouve pas, il remonte la chaîne de prototype.
const animal = {
type: "animal",
toString() {
return `[${this.type}]`;
}
};
const chat = Object.create(animal);
chat.name = "Moustache";
chat.type = "chat";
String(chat); // "[chat]" — toString trouvé sur le prototype (animal)
L’objet chat n’a pas de méthode toString propre. Le moteur remonte au prototype (animal), trouve toString, l’appelle avec this pointant sur chat. Le this.type retourne donc "chat".
Cette chaîne de prototype remonte jusqu’à Object.prototype (sauf pour les nullprotoobj). Chaque objet hérite des méthodes définies sur le prototype de son constructeur :
function Gadget(name, prix) {
this.name = name;
this.prix = prix;
}
Gadget.prototype.toString = function() {
return `${this.name} (${this.prix} €)`;
};
const g = new Gadget("AirPods Pro 3", 279);
alert(g); // "AirPods Pro 3 (279 €)"
Avec le mot-clé new, JavaScript crée un objet dont le prototype pointe vers Gadget.prototype. La méthode toString définie sur ce prototype est accessible à toutes les instances.
Pour vérifier la chaîne :
Object.getPrototypeOf(g) === Gadget.prototype; // true
Object.getPrototypeOf(Gadget.prototype) === Object.prototype; // true
Object.getPrototypeOf(Object.prototype) === null; // true — fin de chaîne
Le null au bout de la chaîne est la raison pour laquelle un nullprotoobj (créé via Object.create(null)) n’a accès à aucune méthode héritée — sa chaîne de prototype s’arrête immédiatement.
setPrototypeOf : modifier la chaîne (use avec précaution)
const base = {
toString() { return "[Base]"; }
};
const obj = { name: "test" };
Object.setPrototypeOf(obj, base);
String(obj); // "[Base]"
Object.setPrototypeOf permet de changer le prototype d’un objet existant. Ça fonctionne, mais les moteurs JavaScript (V8, SpiderMonkey) dé-optimisent l’objet après cette opération. Les accès aux propriétés deviennent plus lents. Préférez Object.create(base) au moment de la création.
Le cas des smartphones et objets connectés : sérialisation dans les apps IoT
Bon, on parle code depuis un moment. Concrètement, où rencontre-t-on ce problème dans le monde réel ? Les applications IoT et domotique manipulent constamment des objets JavaScript représentant des capteurs, des appareils connectés ou des configurations.
Prenons une app de monitoring de montres connectées ou de smartphones : chaque appareil est un objet avec des propriétés (name, batterie, firmware, etc.). Afficher ces objets dans une interface web sans stringify, c’est garantir des [object Object] partout dans le display.
const montre = {
name: "Apple Watch Ultra 3",
batterie: 87,
firmware: "12.1.1",
capteurs: ["ECG", "SpO2", "température"],
toString() {
return `${this.name} — ${this.batterie}% — fw ${this.firmware}`;
}
};
// Display dans une notification
new Notification(String(montre));
// "Apple Watch Ultra 3 — 87% — fw 12.1.1"
Les développeurs qui bossent sur des interfaces de gestion de projets tech rencontrent ce même pattern : des objets métier complexes qu’il faut afficher proprement dans des dashboards, des logs ou des alertes.
FAQ
Qu’est-ce que « [object Object] » et pourquoi mon code l’affiche au lieu des données ?
C’est la string par défaut que Object.prototype.toString() retourne quand JavaScript doit convertir un objet en chaîne de caractères. Ça arrive dans 4 cas : concaténation avec +, template string ${}, alert(), ou assignation à textContent. La correction la plus rapide : accédez à la propriété voulue (objet.name) au lieu de passer l’objet entier, ou utilisez JSON.stringify(objet, null, 2) pour une vue complète.
Comment afficher proprement un objet avec des références circulaires ?
JSON.stringify plante avec une TypeError sur les structures circulaires. La solution standard est un wrapper avec WeakSet qui détecte les objets déjà vus et les remplace par un marqueur "[Circular]". En Node.js 22+, util.inspect(objet, { depth: null }) gère ça nativement. En navigateur, console.dir(objet, { depth: null }) affiche l’objet sans conversion string.
Pourquoi Object.create(null) casse toString et hasOwnProperty ?
Object.create(null) crée un objet dont le prototype est null — aucune méthode héritée de Object.prototype n’existe sur cet objet. Ni toString, ni hasOwnProperty, ni valueOf. Pour vérifier l’existence d’une key, utilisez Object.hasOwn(obj, key) (ES2022) ou le pattern Object.prototype.hasOwnProperty.call(obj, key). Pour l’affichage, JSON.stringify fonctionne normalement car il itère les propriétés propres sans passer par le prototype.