Que signifie « [object Object] » et pourquoi ça apparaît
Quand on passe un objet JavaScript là où une string est attendue, le moteur appelle la méthode toString() héritée du prototype. Par défaut, Object.prototype.toString() retourne la chaîne "[object Object]". Rien de cassé : c’est la représentation textuelle standard de n’importe quel objet (object) dont le prototype n’a pas redéfini cette méthode.
Réponse courte : pourquoi cet output apparaît
Un objet n’est pas une string. Quand JavaScript doit convertir un objet en chaîne — concaténation, alert, template literal — il remonte la chaîne de prototype jusqu’à Object.prototype.toString. Cette function retourne "[object Object]", où le second « Object » est le tag interne de l’objet. Résultat : au lieu des propriétés qu’on espérait voir, on récupère cette représentation (representation) opaque.
Mini example : String(obj) et obj.toString()
const user = { name: "Alice", age: 32 };
console.log(String(user)); // output : "[object Object]"
console.log(user.toString()); // output : "[object Object]"
console.log(JSON.stringify(user)); // output : '{"name":"Alice","age":32}'
Les deux premières lignes produisent le même résultat parce que String() appelle toString() en interne. Seul JSON.stringify retourne quelque chose de lisible ici.
💡 Conseil : dans 90 % des cas, remplacer
alert(obj)paralert(JSON.stringify(obj, null, 2))suffit à débloquer la situation. Les 10 % restants concernent les objets circulaires — on y revient plus bas.
Quand JavaScript convertit un objet en string : les déclencheurs
Le problème ne vient jamais de l’objet lui-même. C’est le contexte d’utilisation qui force la conversion. Voici les quatre situations les plus courantes, avec le mécanisme exact en jeu.
Cas 1 : alert(obj) et concaténation "" + obj
alert() attend une string. Si on lui passe un objet, JavaScript applique l’algorithme ToPrimitive avec hint « string », ce qui déclenche toString(). La concaténation avec + fonctionne pareil dès qu’un des opérandes est une chaîne.
const config = { key: "API_KEY", value: "abc123" };
alert(config); // affiche "[object Object]"
alert("Config : " + config); // affiche "Config : [object Object]"
Cas 2 : template literals `${obj}`
Les template literals appellent aussi toString() sur chaque expression interpolée. C’est le piège le plus fréquent dans du code moderne.
const item = { name: "Clavier", value: 89 };
console.log(`Article : ${item}`); // log : "Article : [object Object]"
Cas 3 : DOM display (textContent / innerHTML)
Quand on assigne un objet à textContent, le navigateur convertit en string. Avec innerHTML, c’est pire : la chaîne "[object Object]" s’affiche directement dans le DOM.
const data = { name: "Wattlet", key: "tech" };
document.getElementById("output").textContent = data;
// display dans le DOM : "[object Object]"
Cas 4 : logs (console vs log serveur)
console.log(obj) dans un navigateur affiche un objet interactif — pas une string. Mais console.log("Résultat : " + obj) déclenche la concaténation et on retombe sur [object Object]. Côté serveur (Node.js), le log formatte selon util.inspect, ce qui donne un résultat différent.
const obj = { prototype: "test", key: "value" };
console.log(obj); // objet interactif dans le navigateur
console.log("Data : " + obj); // log : "Data : [object Object]"
⚠️ Attention : ne confondez pas
console.log(obj)(inspection d’objet) etconsole.log(String(obj))(conversion en string). Le premier montre les propriétés, le second affiche[object Object].
Démonstration : alert vs console.log vs toString vs stringify
Prenons un objet simple et passons-le dans cinq méthodes de display. Le code ci-dessous est reproductible dans n’importe quelle console navigateur.
Example complet : mêmes données, 5 méthodes de display
const product = { name: "Galaxy S26", key: "smartphone", value: 899 };
// 1. alert — force toString()
alert(product);
// output : "[object Object]"
// 2. console.log — objet interactif
console.log(product);
// output dans la console : { name: "Galaxy S26", key: "smartphone", value: 899 }
// 3. String() — toString() explicite
console.log(String(product));
// output : "[object Object]"
// 4. toString() — appel direct
console.log(product.toString());
// output : "[object Object]"
// 5. JSON.stringify — sérialisation
console.log(JSON.stringify(product));
// output : '{"name":"Galaxy S26","key":"smartphone","value":899}'
Cinq lignes de code, trois résultats différents. La méthode 2 (console.log direct) est la seule qui expose les propriétés de l’objet sans conversion.
Pourquoi console.log(obj) n’est pas équivalent à console.log(String(obj))
console.log est polymorphe. Quand il reçoit un objet (pas une string), il utilise un formatter interne qui affiche les propriétés sous forme d’arbre cliquable. Dès qu’on force la conversion — String(obj), concaténation, template literal — on perd cette inspection et on retombe sur Object.prototype.toString.
Un point que beaucoup de développeurs ignorent : dans Chrome, l’objet affiché par console.log est une référence live. Si on modifie l’objet après le log, l’affichage dans la console peut changer quand on déplie l’arbre. Pour capturer un snapshot, console.log(JSON.parse(JSON.stringify(obj))) fait le travail.
La vraie cause : Object.prototype.toString, prototype chain et le tag « Object »
Pour comprendre pourquoi cette string apparaît, il faut remonter la chaîne de prototype (prototype chain) de l’objet.
Chaîne de prototype : où se trouve toString()
Tout objet créé avec {} ou new Object() hérite de Object.prototype. Ce prototype contient une trentaine de méthodes, dont toString(), hasOwnProperty(), valueOf(), et constructor. Quand on appelle obj.toString(), le moteur cherche d’abord une propriété toString sur l’objet lui-même. S’il n’en trouve pas, il remonte au prototype, puis au prototype du prototype, et ainsi de suite.
const obj = { name: "test" };
console.log(obj.hasOwnProperty("toString")); // false
console.log(obj.hasOwnProperty("name")); // true
// toString() vient du prototype, pas de l'objet
Pour la majorité des objets, la recherche s’arrête à Object.prototype.toString. Cette méthode retourne une string au format "[object Tag]".
Format "[object Type]" : d’où vient le name
Le tag « Object » dans [object Object] est déterminé par un mécanisme interne. Par défaut, les objets ordinaires ont le tag "Object". Les types natifs ont des tags spécifiques :
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call("abc")); // "[object String]"
console.log(Object.prototype.toString.call(42)); // "[object Number]"
console.log(Object.prototype.toString.call(new Map())); // "[object Map]"
C’est d’ailleurs pour ça que Object.prototype.toString.call() est utilisé comme méthode fiable de détection de type — bien plus précise que typeof.
Cas avancé : Symbol.toStringTag (particular_object)
Depuis ES2015, on peut personnaliser le tag via Symbol.toStringTag. Les objets natifs comme Map, Set, Promise l’utilisent déjà. Pour un objet custom :
const particular_object = {
[Symbol.toStringTag]: "MonObjet",
name: "demo",
value: 42
};
console.log(Object.prototype.toString.call(particular_object));
// output : "[object MonObjet]"
Ce mécanisme ne change pas le comportement de JSON.stringify — seulement celui de toString.
Rôle de this dans l’appel (méthode vs function)
Quand toString() est appelé comme méthode d’un objet (obj.toString()), this pointe vers cet objet. Mais si on extrait la function et qu’on l’appelle sans contexte, this pointe vers globalThis (ou undefined en mode strict). Ça change complètement l’output.
const obj = { name: "test" };
const fn = obj.toString;
console.log(fn()); // "[object Undefined]" en strict mode
Un piège classique avec this dans les callbacks : passer obj.toString comme argument à une function perd le binding.
Guide de résolution : tableau symptôme → cause → solution
Selon l’endroit où [object Object] apparaît, la solution diffère. Voici un tableau de correspondance directe, chaque entrée avec du code reproductible.
Si c’est dans alert() : afficher une key ou stringify
const user = { name: "Bob", key: "admin", value: 1 };
// Problème
alert(user); // "[object Object]"
// Solution 1 : afficher une propriété
alert(user.name); // "Bob"
// Solution 2 : stringify complet
alert(JSON.stringify(user, null, 2));
// output : '{\n "name": "Bob",\n "key": "admin",\n "value": 1\n}'
Si c’est dans le DOM : contrôler la chaîne rendue
const product = { name: "AirPods Pro 3", value: 279 };
const el = document.getElementById("output");
// Problème
el.textContent = product; // display : "[object Object]"
// Solution : formater la string manuellement
el.textContent = `${product.name} — ${product.value} €`;
// display : "AirPods Pro 3 — 279 €"
Si c’est dans console/log : inspecter l’objet vs sérialiser
const data = { name: "sensor", properties: ["temp", "humidity"], value: null };
// Inspection (recommandé pour le debug)
console.log(data); // objet interactif
console.dir(data); // arbre complet
console.table([data]); // tableau formaté
// Sérialisation (pour les logs serveur)
console.log(JSON.stringify(data));
📌 À retenir :
console.table()affiche les propriétés d’un objet (ou d’un tableau d’objets) sous forme de tableau HTML dans les DevTools. Pratique pour comparer 5-10 objets d’un coup — bien plus lisible qu’une série deconsole.log.
Si c’est dans une concaténation : corriger le code
const obj = { name: "item", value: 10 };
// Problème
const msg = "Objet : " + obj; // "Objet : [object Object]"
// Solution
const msg2 = `Objet : ${obj.name} (${obj.value})`; // "Objet : item (10)"
La règle : ne jamais concaténer un objet avec une string sans extraire d’abord la propriété ou les propriétés voulues.
Solutions pratiques : accéder aux propriétés, custom toString, et inspection console
Afficher une propriété : obj.name et obj[key]
L’approche la plus simple. On cible une key précise et on récupère sa valeur (value) :
const config = { name: "prod", key: "API_123", value: "secret" };
console.log(config.name); // "prod"
console.log(config["key"]); // "API_123"
// Accès dynamique avec une variable
const prop = "value";
console.log(config[prop]); // "secret"
Afficher plusieurs values : formatage contrôlé
Pour afficher plusieurs propriétés, le destructuring combiné à un template literal retourne une string propre :
const { name, value } = config;
const output = `${name} = ${value}`;
console.log(output); // "prod = secret"
On peut aussi boucler sur les clés (key) de l’objet :
for (const key in config) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
console.log(`${key}: ${config[key]}`);
}
}
Custom toString() : quand c’est utile (et quand éviter)
Redéfinir toString() sur un objet (ou sur le prototype d’une classe) permet de contrôler l’output en cas de conversion :
function Device(name, value) {
this.name = name;
this.value = value;
}
Device.prototype.toString = function() {
return `${this.name} (${this.value} €)`;
};
const phone = new Device("Pixel 9", 799);
alert(phone); // "Pixel 9 (799 €)" — plus de [object Object]
console.log(`Tel : ${phone}`); // "Tel : Pixel 9 (799 €)"
Utile pour les objets métier qu’on affiche souvent. À éviter sur des objets génériques ou éphémères — le jeu n’en vaut pas la chandelle.
hasOwnProperty : distinguer propriétés propres vs héritées
Quand on itère sur un objet avec for...in, la boucle remonte le prototype et inclut les propriétés héritées. hasOwnProperty (ou mieux, Object.hasOwn() depuis ES2022) filtre les propriétés propres :
const obj = Object.create({ inherited: true });
obj.name = "test";
obj.value = 42;
for (const key in obj) {
console.log(key, Object.hasOwn(obj, key));
// "name" true, "value" true, "inherited" false
}
// Alternative : Object.keys() retourne seulement les propriétés propres
console.log(Object.keys(obj)); // ["name", "value"]
Inspection : console.dir / console.table
console.dir affiche toutes les propriétés d’un objet, y compris celles du prototype. console.table formate un tableau :
const objets = [
{ name: "MacBook Air M4", value: 1299, key: "laptop" },
{ name: "iPad Pro M4", value: 1199, key: "tablet" }
];
console.table(objets);
// Affiche un tableau avec colonnes name, value, key
Quand on travaille avec des logiciels et applications modernes, ces outils de debug deviennent vite indispensables.
JSON.stringify : comment l’utiliser, ce qu’il retourne, et ses limites
Ce que JSON.stringify(obj) retourne exactement
JSON.stringify retourne une string JSON valide. Pas une string « lisible » — une string parseable par JSON.parse. La function prend trois arguments : l’objet, un replacer optionnel, et un espacement :
const data = { name: "test", value: null, key: "abc" };
JSON.stringify(data); // '{"name":"test","value":null,"key":"abc"}'
JSON.stringify(data, null, 2); // version indentée (2 espaces)
La valeur null est préservée telle quelle — c’est une valeur JSON valide.
Limites : objets circulaires, function, undefined, BigInt
Quatre situations où stringify échoue ou produit un résultat inattendu :
// 1. Objet circulaire → TypeError
const a = {};
a.self = a;
// JSON.stringify(a); // TypeError: circular
// 2. function → ignorée
const obj = { name: "x", fn: function() {} };
JSON.stringify(obj); // '{"name":"x"}' — fn disparaît
// 3. undefined → ignoré dans les objets, null dans les arrays
const obj2 = { name: "x", value: undefined };
JSON.stringify(obj2); // '{"name":"x"}'
// 4. BigInt → TypeError
// JSON.stringify({ n: 42n }); // TypeError
Replacer : filtrer/transformer des properties
Le deuxième argument de stringify est un replacer. En passant un tableau, on sélectionne les key à inclure. En passant une function, on transforme chaque value :
const obj = { name: "secret", key: "API_KEY", value: "abc123", internal: true };
// Filtrer par key
JSON.stringify(obj, ["name", "value"]);
// '{"name":"secret","value":"abc123"}'
// Transformer
JSON.stringify(obj, (key, value) => {
if (key === "key") return "[HIDDEN]";
return value;
});
// '{"name":"secret","key":"[HIDDEN]","value":"abc123","internal":true}'
Safe stringify : stratégie simple avec Set
Pour les objets potentiellement circulaires, un Set de références déjà vues fait office de garde-fou :
function safeStringify(obj) {
const seen = new Set();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circulaire]";
seen.add(value);
}
return value;
}, 2);
}
const circular = { name: "loop", value: null };
circular.self = circular;
console.log(safeStringify(circular));
// output : '{ "name": "loop", "value": null, "self": "[Circulaire]" }'
C’est une technique qu’on retrouve dans pas mal de librairies de logging Node.js.
Cas avancés : objets avec prototype null (nullprotoobj) et pièges liés à null
Ce sujet est rarement couvert dans les tutos. Les objets sans prototype existent, et leur comportement face à toString est radicalement différent.
Object.create(null) : pourquoi il n’y a pas de prototype
Object.create(null) crée un objet dont le prototype est null. Pas Object.prototype — littéralement null. Cet objet (qu’on appelle « nullprotoobj » ou « dictionary object ») n’hérite d’aucune méthode.
const nullprotoobj = Object.create(null);
nullprotoobj.name = "bare";
nullprotoobj.value = 42;
console.log(Object.getPrototypeOf(nullprotoobj)); // null
On croise ces objets dans les caches, les dictionnaires, et les parseurs. Leur avantage : aucune collision avec les propriétés héritées du prototype (constructor, toString, hasOwnProperty).
Conséquences : pas de toString(), pas de hasOwnProperty()
Un nullprotoobj ne possède ni toString ni hasOwnProperty ni aucune des méthodes habituelles. Tenter d’appeler ces méthodes déclenche une TypeError :
const nullprotoobj = Object.create(null);
nullprotoobj.name = "bare";
nullprotoobj.key = "test";
// nullprotoobj.toString(); // TypeError: nullprotoobj.toString is not a function
// nullprotoobj.hasOwnProperty("name"); // TypeError
// Vérifier les propriétés sans hasOwnProperty :
console.log(Object.keys(nullprotoobj)); // ["name", "key"]
console.log("name" in nullprotoobj); // true
Pour un convertisseur YouTube MP4 en JavaScript côté client, ce genre de piège arrive quand on manipule des objets parsés depuis des sources externes.
Solution : Object.prototype.toString.call(nullprotoobj)
On peut forcer l’appel à toString en passant le nullprotoobj comme contexte via call :
const nullprotoobj = Object.create(null);
nullprotoobj.name = "dict";
console.log(Object.prototype.toString.call(nullprotoobj));
// output : "[object Object]"
// stringify fonctionne normalement
console.log(JSON.stringify(nullprotoobj));
// output : '{"name":"dict"}'
JSON.stringify n’a pas besoin de toString pour fonctionner — il accède directement aux propriétés énumérables de l’objet. C’est la méthode la plus fiable pour sérialiser un nullprotoobj.
null vs objet : éviter les erreurs d’accès aux propriétés
typeof null retourne "object" — un bug historique de JavaScript qui remonte à 1995 et qui ne sera jamais corrigé (trop de code dépend de ce comportement). Conséquence : tester typeof x === "object" ne suffit pas pour garantir qu’on a un objet avec des propriétés.
const values = [null, undefined, {}, Object.create(null), { name: "ok" }];
for (const val of values) {
if (val !== null && typeof val === "object") {
// Safe : on sait que val est un objet (pas null)
console.log(Object.keys(val));
}
}
Toujours tester null avant d’accéder aux propriétés d’un objet. Chaîner obj?.propriete (optional chaining) est l’approche moderne — disponible dans tous les navigateurs depuis 2020.
📊 Chiffre clé : selon le State of JS 2024, 87 % des développeurs JavaScript utilisent l’optional chaining (
?.) en production. Le null check explicite reste la méthode recommandée dans les environnements sans transpilation.
Checklist de debug : retrouver la source de « [object Object] »
Quand on tombe sur [object Object] dans une app, le réflexe est de tracer l’origine. Six étapes suffisent dans la majorité des cas.
Étapes rapides (6 points) pour isoler la cause
- Localiser l’output — est-ce dans le DOM, une alerte (alert), la console, un log serveur ?
- Vérifier le type —
typeof suspect === "object"etsuspect !== null - Lister les propriétés —
Object.keys(suspect)pour voir les key disponibles - Vérifier le prototype —
Object.getPrototypeOf(suspect): est-ce null (nullprotoobj),Object.prototype, ou un prototype custom ? - Vérifier
this— si c’est dans une méthode ou un callback, le contextethisest-il correct ? - Choisir la bonne stratégie — inspection console (debug) vs
JSON.stringify(log) vs accès direct à une propriété (display)
Snippet diagnostic : typeof, null-check, prototype, keys
function diagnose(obj) {
console.log("Type :", typeof obj);
console.log("Est null :", obj === null);
if (obj !== null && typeof obj === "object") {
console.log("Prototype :", Object.getPrototypeOf(obj));
console.log("Keys :", Object.keys(obj));
console.log("Propriétés (count) :", Object.keys(obj).length);
console.log("toString retourne :", Object.prototype.toString.call(obj));
try {
console.log("stringify :", JSON.stringify(obj));
} catch (e) {
console.log("stringify erreur :", e.message);
}
}
}
// Use :
diagnose({ name: "test", value: 1 });
diagnose(null);
diagnose(Object.create(null));
Ce snippet couvre les trois cas problématiques : objet normal, null, et nullprotoobj. Il retourne des informations exploitables pour chaque situation.
Des réflexes similaires s’appliquent quand on débogue des outils côté tablette Android ou qu’on teste une application sur smartphone — la console Chrome DevTools en remote fonctionne exactement pareil.
FAQ : questions fréquentes sur « [object Object] »
Qu’est-ce que « [object Object] » en JavaScript ?
C’est la string que retourne Object.prototype.toString() quand on convertit un objet en chaîne. Le premier « object » (minuscule) est fixe, le second « Object » (majuscule) est le tag interne du type. Pour voir les propriétés de l’objet, on accède directement à une key (obj.name) ou on sérialise avec JSON.stringify(obj).
Pourquoi alert(obj) affiche « [object Object] » alors que console.log(obj) montre les propriétés ?
alert() convertit tout en string avant d’afficher — d’où l’appel à toString() et le résultat [object Object]. console.log, lui, détecte qu’il reçoit un objet et utilise un formatter spécial qui affiche les propriétés de manière interactive. Pour afficher un objet dans une alerte : alert(JSON.stringify(obj, null, 2)).
Comment gérer un objet sans prototype (nullprotoobj) qui n’a pas de toString ?
Un nullprotoobj créé via Object.create(null) ne possède aucune méthode héritée — ni toString, ni hasOwnProperty. Pour le convertir en string : Object.prototype.toString.call(nullprotoobj) retourne "[object Object]". Pour le sérialiser : JSON.stringify(nullprotoobj) fonctionne normalement car stringify accède aux propriétés énumérables sans passer par le prototype. Pour lister les key : Object.keys(nullprotoobj).
Récapitulatif : les 5 réflexes pour ne plus subir « [object Object] »
Pas besoin de relire les 1 500 mots au-dessus. Les règles tiennent en cinq points :
[object Object]= conversion automatique en string. L’objet n’est pas corrompu — JavaScript fait juste ce qu’on lui demande (mal).- Pour inspecter :
console.log(obj)sans concaténation.console.diretconsole.tablepour les cas avancés. - Pour afficher : accéder à une propriété spécifique (
obj.name,obj[key]) ou formater avec un template literal après extraction. - Pour sérialiser :
JSON.stringify(obj)avec gestion des limites (objets circulaires via safe stringify, function ignorées, null préservé). - Cas spéciaux : vérifier le prototype (nullprotoobj sans méthodes), le contexte
this(binding perdu dans les callbacks), et toujours testernullavant d’accéder aux propriétés d’un objet.
Si le sujet du management des outils numériques ou du travail à distance vous intéresse, ces compétences de debug JavaScript sont exactement le genre de savoir-faire technique qui fait la différence au quotidien.