Aller au contenu principal
14 min de lecture

Comprendre et corriger l'affichage « [object Object] » en JavaScript : causes, prototype chain et solutions

Pourquoi JavaScript affiche [object Object] dans alert, console ou le DOM. Prototype, toString, stringify : guide complet avec code reproductible.

14 min de lecture
Partager
Comprendre et corriger l'affichage « [object Object] » en JavaScript : causes, prototype chain et solutions

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) par alert(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) et console.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 de console.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

  1. Localiser l’output — est-ce dans le DOM, une alerte (alert), la console, un log serveur ?
  2. Vérifier le typetypeof suspect === "object" et suspect !== null
  3. Lister les propriétésObject.keys(suspect) pour voir les key disponibles
  4. Vérifier le prototypeObject.getPrototypeOf(suspect) : est-ce null (nullprotoobj), Object.prototype, ou un prototype custom ?
  5. Vérifier this — si c’est dans une méthode ou un callback, le contexte this est-il correct ?
  6. 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.dir et console.table pour 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 tester null avant 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.

L'auteur

L'auteur

Redacteur passionné. Il partage ses connaissances à travers des guides pratiques et des outils gratuits.

Cet article est publie a titre informatif. Faites vos propres recherches avant toute decision.