Copiez un objet JavaScript dans un alert(), et le navigateur vous crache [object Object]. Pas d’erreur rouge dans la console, pas de crash — juste cette chaîne cryptique qui remplace vos données. Le réflexe : penser à un bug. La réalité : c’est le comportement par défaut du langage quand il convertit un objet en string.
Ce message apparaît des milliers de fois par jour sur Stack Overflow. Pourtant, la plupart des guides en ligne se contentent de balancer JSON.stringify() comme remède universel sans expliquer pourquoi ça arrive ni quand cette solution ne suffit pas. On va couvrir les deux.
Que signifie [object Object] et pourquoi le résultat n’est pas un bug
La coercition en chaîne : le mécanisme derrière le rideau
Quand JavaScript doit transformer un objet en string — pour un alert(), une concaténation avec +, ou un template literal — il appelle la méthode toString(). Cette méthode vit sur Object.prototype, le prototype racine dont héritent tous les objets. Et la version par défaut retourne toujours la même chose : [object Object].
Le premier mot entre crochets désigne le type interne (toujours « object » pour les objets classiques). Le second correspond au tag de l’objet, tiré de Symbol.toStringTag ou de la classification interne. Pour un objet littéral {}, le tag est « Object ». D’où [object Object].
const user = { name: "Alice", age: 32 };
Object.prototype.toString.call(user);
// retourne "[object Object]"
Aucun prototype personnalisé, aucun toString() redéfini sur cet objet → le moteur remonte la chaîne de prototypes jusqu’à Object.prototype.toString et retourne la représentation générique. C’est un comportement voulu, pas une anomalie.
alert vs console.log : deux façons de lire un objet
const config = { theme: "dark", lang: "fr" };
alert(config); // output : "[object Object]"
console.log(config); // output : { theme: "dark", lang: "fr" }
alert() convertit son argument en string avant de l’afficher. console.log(), lui, inspecte l’objet sans le convertir — c’est pour ça qu’on voit les propriétés. La confusion vient de là : deux fonctions, deux traitements de l’objet, deux résultats.
💡 Conseil : Remplacez systématiquement
alert()parconsole.log()ouconsole.dir()pour le debug.alert()bloque le thread principal et détruit l’information en convertissant l’objet en string.
Cinq correctifs immédiats pour afficher un objet sans [object Object]
Inspecter avec console.log et console.dir
console.log() affiche l’objet de façon interactive dans le navigateur. On peut déplier les propriétés, naviguer dans les objets imbriqués. console.dir() force un affichage en arbre même quand log affiche du HTML (cas des éléments DOM).
const product = { name: "MacBook Pro M4", price: 2399, stock: true };
console.log(product);
// output : { name: "MacBook Pro M4", price: 2399, stock: true }
console.dir(product);
// output identique, mais garantit le format objet
Convertir en JSON avec JSON.stringify
JSON.stringify() retourne une chaîne JSON lisible. Avec les paramètres d’indentation, l’output devient facile à lire :
const order = { id: 4821, items: ["clavier", "souris"], total: 127.50 };
const json = JSON.stringify(order, null, 2);
console.log(json);
// output :
// {
// "id": 4821,
// "items": ["clavier", "souris"],
// "total": 127.5
// }
Le deuxième argument (null ici) est un replacer — on y reviendra pour filtrer des propriétés sensibles. Le troisième fixe l’indentation. Pour un affichage compact, on l’omet.
Accéder à une propriété précise par sa key
Parfois, on n’a besoin que d’une seule value. Inutile de sérialiser l’objet entier :
const user = { name: "Marc", role: "admin" };
alert(user.name); // output : "Marc" — pas de [object Object]
L’accès par key (object.name ou object["name"]) retourne directement la value, qui est déjà une string ou un nombre. Pas de conversion, pas de surprise.
console.table pour les objets tabulaires
const servers = [
{ name: "prod-1", cpu: "72%", ram: "8.3 GB" },
{ name: "prod-2", cpu: "45%", ram: "6.1 GB" }
];
console.table(servers);
console.table() retourne un tableau formaté directement dans la console. Chaque key devient un en-tête de colonne, chaque objet une ligne. Pour des objets avec 3 à 8 propriétés, c’est le format le plus lisible.
Checklist : quel outil selon le contexte
| Besoin | Méthode | Retourne |
|---|---|---|
| Debug rapide | console.log(objet) | Objet interactif |
| Copier-coller de données | JSON.stringify(objet, null, 2) | String JSON formatée |
| Afficher dans le DOM | element.textContent = objet.name | Propriété ciblée |
| Tableau de données | console.table(objets) | Tableau formaté |
| Envoyer à une API | JSON.stringify(objet) | String JSON compacte |
⚠️ Attention :
JSON.stringify()ignore silencieusement les propriétés de typefunction,Symboletundefined. Si votre objet contient des méthodes, elles disparaîtront de l’output sans avertissement.
Diagnostic par contexte : pourquoi [object Object] apparaît et comment corriger
alert(objet) → coercition string automatique
const data = { status: "ok", code: 200 };
alert(data); // "[object Object]"
// Fix :
alert(JSON.stringify(data)); // '{"status":"ok","code":200}'
alert() appelle toString() via le prototype de l’objet. La méthode retourne la représentation par défaut. Le correctif : passer par stringify ou accéder à la propriété voulue.
Concaténation string + objet
const user = { name: "Léa" };
const msg = "Bonjour " + user;
// msg = "Bonjour [object Object]"
// Fix :
const msg2 = "Bonjour " + user.name;
// msg2 = "Bonjour Léa"
L’opérateur + avec une string à gauche déclenche la coercition. JavaScript appelle toString() sur l’objet à droite. Le prototype retourne [object Object]. Le correctif : cibler la key précise, ou utiliser stringify.
Template literal : même piège, autre syntaxe
const item = { label: "USB-C", price: 12 };
console.log(`Article : ${item}`);
// output : "Article : [object Object]"
// Fix :
console.log(`Article : ${item.label} — ${item.price} €`);
// output : "Article : USB-C — 12 €"
Les template literals appellent aussi toString() sur les objets interpolés. Même mécanisme de prototype, même résultat.
Injection dans le DOM via innerHTML ou textContent
const config = { mode: "sombre", lang: "fr" };
document.getElementById("info").textContent = config;
// display : "[object Object]"
// Fix :
document.getElementById("info").textContent = JSON.stringify(config);
// display : '{"mode":"sombre","lang":"fr"}'
Pour un display propre dans l’interface, on préfère accéder aux propriétés individuelles de l’objet et construire le HTML manuellement plutôt que de dump tout l’objet.
📌 À retenir : Chaque fois qu’un objet est converti implicitement en string — par
+, template literal,alert(), ou affectation au DOM — JavaScript passe partoString()sur le prototype. Identifier le point de coercition permet de choisir le bon correctif.
Le mécanisme interne : Object.prototype.toString, prototype et this
La chaîne de prototypes : où JavaScript trouve toString()
Chaque objet en JavaScript possède un prototype. Ce prototype est lui-même un objet, avec son propre prototype. Cette chaîne remonte jusqu’à Object.prototype, qui est le prototype terminal — son prototype à lui est null.
const car = { brand: "Peugeot", year: 2025 };
// car → Object.prototype → null
car.toString();
// retourne "[object Object]"
// car n'a pas de toString() propre
// → remonte au prototype → Object.prototype.toString()
Quand on appelle toString() sur car, le moteur cherche la méthode sur l’objet lui-même. Absent. Il remonte au prototype (Object.prototype), trouve toString(), et l’exécute. Le prototype fournit la méthode, l’objet fournit le contexte (this).
this : le contexte d’exécution qui change tout
Object.prototype.toString utilise this pour déterminer quel objet il décrit. Quand on appelle car.toString(), this pointe vers car. Le prototype exécute la méthode, mais this cible l’objet appelant.
const date = new Date();
Object.prototype.toString.call(date);
// retourne "[object Date]"
const arr = [1, 2, 3];
Object.prototype.toString.call(arr);
// retourne "[object Array]"
const regex = /test/;
Object.prototype.toString.call(regex);
// retourne "[object RegExp]"
Object.prototype.toString.call(value) est une technique classique pour identifier le type réel d’une value. this est forcé via call(), et la méthode retourne le tag interne de l’objet. Les prototypes d’Array, Date et RegExp redéfinissent toString(), mais en passant par Object.prototype directement, on obtient le type brut.
Pourquoi le résultat est générique pour les objets littéraux
Un objet créé avec {} ou new Object() n’a pas de Symbol.toStringTag. Son prototype est directement Object.prototype. La méthode toString() retourne donc [object Object] — le tag par défaut. Les objets natifs (Date, Map, Set, Error) ont chacun un tag spécifique sur leur prototype, ce qui donne des résultats comme [object Date] ou [object Map].
const obj = {};
obj.toString();
// retourne "[object Object]"
const map = new Map();
map.toString();
// retourne "[object Map]" grâce à Symbol.toStringTag
Pour vos propres objets, ajouter Symbol.toStringTag change le tag :
const myObj = { [Symbol.toStringTag]: "Config" };
Object.prototype.toString.call(myObj);
// retourne "[object Config]"
Bon, concrètement, dans la plupart des cas, on ne touche pas à toStringTag. L’approche stringify ou console.log couvre 95 % des besoins. Cette astuce est surtout utile pour les auteurs de bibliothèques qui veulent un display propre dans les outils de debug.
Si vous travaillez avec d’autres logiciels et outils de développement, comprendre le fonctionnement des prototypes aide aussi à déboguer d’autres situations liées aux objets.
toString vs JSON.stringify vs console : comparatif structuré
toString() : simple mais limité
toString() retourne une string basique. Sur un objet sans méthode custom, elle retourne [object Object]. Sur un objet avec un toString() redéfini, elle retourne ce qu’on a codé.
const point = {
x: 10,
y: 25,
toString() { return `Point(${this.x}, ${this.y})`; }
};
point.toString(); // retourne "Point(10, 25)"
String(point); // retourne "Point(10, 25)"
alert(point); // output : "Point(10, 25)"
Le prototype de point a maintenant sa propre méthode toString() — JavaScript ne remonte plus jusqu’à Object.prototype. L’objet contrôle sa représentation en string.
Limite : toString() ne gère pas les objets imbriqués. Pour un objet complexe avec des propriétés profondes, il faut coder manuellement la représentation.
JSON.stringify() : le couteau suisse
stringify convertit un objet en string JSON. Il parcourt récursivement toutes les propriétés et retourne une représentation complète :
const user = {
name: "Sophie",
address: { city: "Lyon", zip: "69001" },
tags: ["dev", "js"]
};
JSON.stringify(user, null, 2);
// retourne une string JSON indentée avec toutes les propriétés
Avantages : gère les objets imbriqués, les tableaux, les valeurs null. Utilisable pour le stockage (localStorage), le transport (API fetch), et le debug.
Limites importantes :
- Les propriétés de type
functionsont ignorées —stringifyles supprime silencieusement - Les valeurs
undefinedsont supprimées des objets (conservées commenulldans les tableaux) - Les références circulaires lèvent une erreur
TypeError - Les objets
Datesont convertis en string ISO, pas en objet Date auJSON.parse()
console.log / console.dir / console.table
console.log() affiche un objet interactif dans les DevTools. On déplie les propriétés, on navigue dans les prototypes imbriqués. console.dir() fait la même chose mais force le format objet (utile pour les nœuds DOM). console.table() affiche un tableau pour les arrays d’objets.
Le gros avantage : aucune conversion. L’objet reste un objet. On voit les function, les Symbol, les valeurs undefined, les prototypes — tout ce que stringify masque.
Tableau comparatif
| Critère | toString() | JSON.stringify() | console.log() |
|---|---|---|---|
| Objets imbriqués | Non (sauf code custom) | Oui (récursif) | Oui (interactif) |
| Fonctions visibles | Dépend du code | Non (ignorées) | Oui |
| Références circulaires | N/A | Erreur TypeError | Oui (avec indicateur) |
| Propriétés null | Dépend du code | Oui (affiche null) | Oui |
| Usage debug | Limité | Bon (copier-coller) | Meilleur |
| Usage UI/transport | Oui (si custom) | Oui | Non |
| Objets sans prototype | Fonctionne via call() | Oui | Oui |
Si vous développez des outils de conversion ou des scripts d’analyse, maîtriser ces trois méthodes évite des heures de debug sur des propriétés fantômes.
Cas limites : null, objets sans prototype et références circulaires
null : l’objet qui n’en est pas un
typeof null retourne "object" — un bug historique de JavaScript qui date de 1995 et qui ne sera jamais corrigé (trop de code en dépend). Mais null n’est pas un objet. Il n’a pas de prototype, pas de propriétés, pas de méthodes.
const data = null;
data.toString(); // TypeError: Cannot read properties of null
data.name; // TypeError: Cannot read properties of null
JSON.stringify(data); // retourne "null" (string)
console.log(data); // output : null
Accéder à n’importe quelle propriété de null lève une erreur. Le seul moyen sûr de le display : vérifier d’abord que la value n’est pas null.
function safeDisplay(obj) {
if (obj === null) return "null";
if (obj === undefined) return "undefined";
return JSON.stringify(obj, null, 2);
}
Null est la source numéro un des TypeError en production. Avant d’accéder aux propriétés d’un objet retourné par une API ou une base de données, testez toujours la value. Un objet null n’a ni prototype ni propriétés — c’est le néant.
Object.create(null) : l’objet sans prototype
Un objet créé avec Object.create(null) n’a aucun prototype. Pas de Object.prototype dans la chaîne. Pas de toString(). Pas de hasOwnProperty(). Rien.
const nullprotoobj = Object.create(null);
nullprotoobj.name = "cache";
nullprotoobj.ttl = 3600;
nullprotoobj.toString();
// TypeError: nullprotoobj.toString is not a function
String(nullprotoobj);
// TypeError: Cannot convert object to primitive value
Ce type d’objet (parfois appelé « nullprotoobj » ou « dictionary object ») est utilisé dans les moteurs de cache, les parsers, et les objets de configuration sécurisés. L’absence de prototype évite les collisions avec les propriétés héritées — un nullprotoobj avec une key "toString" ne masque pas une méthode du prototype puisqu’il n’y en a pas.
Le problème : impossible d’appeler toString() ou hasOwnProperty() directement sur un nullprotoobj. Les correctifs :
// Sérialiser un nullprotoobj
JSON.stringify(nullprotoobj);
// retourne '{"name":"cache","ttl":3600}' — stringify fonctionne
// Vérifier une propriété sur un nullprotoobj
Object.prototype.hasOwnProperty.call(nullprotoobj, "name");
// retourne true
// Alternative moderne pour vérifier une key
Object.hasOwn(nullprotoobj, "name");
// retourne true (ES2022)
JSON.stringify ne dépend pas du prototype de l’objet — il parcourt les propriétés énumérables directement. C’est le moyen le plus fiable de sérialiser un nullprotoobj.
Pour itérer sur les propriétés d’un nullprotoobj :
const nullprotoobj2 = Object.create(null);
nullprotoobj2.host = "localhost";
nullprotoobj2.port = 5432;
nullprotoobj2.db = "app_prod";
// for...in fonctionne même sans prototype
for (const key in nullprotoobj2) {
console.log(`${key}: ${nullprotoobj2[key]}`);
}
// output :
// host: localhost
// port: 5432
// db: app_prod
// Object.keys fonctionne aussi sur un nullprotoobj
Object.keys(nullprotoobj2);
// retourne ["host", "port", "db"]
📊 Chiffre clé : Dans le code source de Node.js, plus de 200 occurrences de
Object.create(null)sont utilisées pour créer des objets sans prototype — principalement dans les parsers HTTP, le système de modules et les caches internes.
Références circulaires : quand stringify plante
Un objet qui se référence lui-même — ou deux objets qui se référencent mutuellement — crée une référence circulaire. JSON.stringify ne sait pas gérer ça et lève une erreur :
const objA = { name: "A" };
const objB = { name: "B", ref: objA };
objA.ref = objB;
JSON.stringify(objA);
// TypeError: Converting circular structure to JSON
Le correctif : un stringify sécurisé qui détecte les références circulaires :
function safeStringify(obj, indent = 2) {
const seen = new WeakSet();
return JSON.stringify(obj, function(key, value) {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
}, indent);
}
safeStringify(objA);
// retourne :
// {
// "name": "A",
// "ref": {
// "name": "B",
// "ref": "[Circular]"
// }
// }
Le WeakSet stocke chaque objet déjà visité. Quand stringify retombe sur un objet connu, le replacer retourne "[Circular]" au lieu de boucler. Les propriétés null et les valeurs primitives (string, number) ne posent pas de problème — seuls les objets référencés créent des boucles.
console.log() gère les circulaires nativement : les DevTools affichent un indicateur [Circular] ou un lien cliquable. Pour du code serveur (Node.js), util.inspect() fait la même chose.
Bonnes pratiques : toString custom et logging en production
Écrire un toString() utile sur vos objets
La méthode toString() par défaut du prototype retourne [object Object]. Redéfinir cette méthode sur vos objets (ou classes) change le comportement de alert(), de la concaténation, et du template literal :
class User {
constructor(name, role) {
this.name = name;
this.role = role;
this._token = "sk-abc123"; // propriété sensible
}
toString() {
return `User(${this.name}, ${this.role})`;
}
}
const admin = new User("Claire", "admin");
console.log(`Connecté : ${admin}`);
// output : "Connecté : User(Claire, admin)"
// → pas de [object Object], et _token n'est pas exposé
La méthode toString() sur la classe remplace celle du prototype Object.prototype. JavaScript ne remonte plus la chaîne — il trouve la méthode directement sur le prototype de User. Le résultat : un display contrôlé qui n’expose pas les propriétés sensibles.
Masquer des properties avant le display
En production, JSON.stringify peut exposer des tokens, mots de passe ou données personnelles si on n’est pas vigilant. Le replacer permet de filtrer :
function safelog(obj, sensitiveKeys = ["password", "token", "_token", "secret"]) {
return JSON.stringify(obj, function(key, value) {
if (sensitiveKeys.includes(key)) return "[REDACTED]";
if (typeof value === "object" && value !== null) {
// Gérer les nullprotoobj qui n'ont pas hasOwnProperty
return value;
}
return value;
}, 2);
}
const config = { host: "api.example.com", token: "sk-live-xyz", port: 443 };
console.log(safelog(config));
// output :
// {
// "host": "api.example.com",
// "token": "[REDACTED]",
// "port": 443
// }
Logger pour dev et prod : deux comportements
const logger = {
log(label, obj) {
if (process.env.NODE_ENV === "production") {
// En prod : JSON compact, propriétés filtrées
const clean = JSON.stringify(obj, (key, value) => {
if (key.startsWith("_")) return undefined;
return value;
});
process.stdout.write(`[${label}] ${clean}\n`);
} else {
// En dev : objet complet, inspecté par console
console.log(`[${label}]`, obj);
}
}
};
En production, on utilise stringify avec un replacer qui supprime les propriétés privées (préfixées _). Les valeurs null passent tel quel. Les propriétés de type function sont automatiquement exclues par stringify — un avantage ici.
En développement, console.log affiche l’objet complet avec ses prototypes, ses fonctions et ses objets imbriqués. Les deux modes couvrent des besoins différents : traçabilité en prod, inspection libre en dev.
💡 Conseil : Ajoutez
toJSON()sur vos classes métier.JSON.stringifyappelle automatiquementtoJSON()si la méthode existe sur l’objet. Cela vous donne un contrôle total sur les propriétés sérialisées, indépendamment detoString().
Pour les développeurs qui gèrent des tâches en remote, un logging structuré avec ces techniques rend le debug à distance nettement plus efficace — surtout quand console.dir() n’est pas disponible côté serveur.
La checklist anti [object Object]
Avant d’afficher ou de log un objet, passez par ces étapes :
-
Vérifier null et undefined —
if (value === null || value === undefined)avant tout accès aux propriétés. Un objet null n’a pas de prototype et lèvera une erreur. -
Identifier le contexte — Debug local →
console.log(). UI → accéder aux propriétés par key. Transport API →JSON.stringify(). Chaque contexte a sa méthode. -
Éviter alert() pour le debug —
alert()convertit l’objet en string via le prototypetoString(). Toujours. Sans exception. Préférez la console. -
Gérer les objets sans prototype — Un nullprotoobj n’a pas de
toString(). UtilisezJSON.stringify()ouObject.prototype.toString.call()pour ces objets. -
Prévoir les références circulaires — Si votre objet peut contenir des références croisées, utilisez un
safeStringifyavec WeakSet.stringifystandard lèvera une erreur. -
Filtrer les propriétés sensibles — En production, passez un replacer à
stringifypour masquer les keys contenant des tokens, mots de passe ou données personnelles. -
Définir toString() sur vos classes — Un
toString()personnalisé sur le prototype de vos objets évite le display générique[object Object]dans toute l’application.
// Exemple final : de [object Object] à un output lisible
const item = { name: "RTX 5090", price: 2199, stock: false };
// Mauvais :
alert(item); // "[object Object]"
// Correct :
console.log(item); // objet interactif
console.log(JSON.stringify(item)); // '{"name":"RTX 5090","price":2199,"stock":false}'
console.log(item.name); // "RTX 5090"
Pour les développeurs intéressés par le hardware derrière le code, le guide des tablettes Android ou le comparatif photo smartphone 2026 complètent bien cette approche logicielle avec un angle matériel.
FAQ
Qu’est-ce que [object Object] et d’où vient ce message ?
C’est la valeur retournée par Object.prototype.toString() quand JavaScript convertit un objet en string. Le premier « object » indique le type interne, le second « Object » est le tag par défaut. Ce n’est pas un message d’erreur — c’est le comportement normal du prototype racine. Pour voir les propriétés de l’objet, utilisez console.log() ou JSON.stringify().
Comment corriger [object Object] dans un alert ou dans le DOM ?
Remplacez alert(monObjet) par alert(JSON.stringify(monObjet, null, 2)) pour voir le contenu formaté. Pour le DOM, accédez directement aux propriétés : element.textContent = monObjet.name au lieu de element.textContent = monObjet. La key est de ne jamais laisser JavaScript convertir implicitement un objet en string.
Pourquoi JSON.stringify échoue sur certains objets (circular, null prototype) ?
JSON.stringify() lève une TypeError sur les références circulaires (deux objets qui se référencent mutuellement). Le correctif : un replacer avec WeakSet pour détecter les boucles. Pour les objets créés avec Object.create(null) (nullprotoobj), stringify fonctionne normalement — c’est toString() et hasOwnProperty() qui posent problème car le nullprotoobj n’a pas de prototype. Utilisez Object.hasOwn() (ES2022) ou Object.prototype.hasOwnProperty.call(nullprotoobj, key) pour tester les propriétés.