BeingPool
BeingPool

Reputation: 61

Why does an object log `[object Object]` when called with `console.log`?

I'm honestly not very sure how to format this, as I'm putting a long piece of code in here, so I'll be checking this frequently to clear up any confusion.

The code in question is as follows:

const menu = {
  _courses: {
    appetizers: [],
    mains: [],
    desserts: []
  },
  get appetizers() {
    this._courses.appetizers;
  },
  set appetizers(appetizerIn) {
    this._courses.appetizers = appetizerIn;
  },
  get mains() {
    this._courses.mains;
  },
  set mains(mainIn) {
    this._courses.mains = mainIn;
  },
  get desserts() {
    this._courses.desserts;
  },
  set desserts(dessertIn) {
    this._courses.desserts = dessertIn;
  },
  get courses() {
    return {
      appetizers: this.appetizers,
      mains: this.mains,
      desserts: this.desserts
    }
  },
  addDishToCourse(courseName, dishName, dishPrice) {
    const dish = {
      name: dishName,
      price: dishPrice
    }
    this._courses[courseName].push(dish);
  },
  getRandomDishFromCourse(courseName) {
    const dishes = this._courses[courseName];
    const dishNum = Math.floor(Math.random() * dishes.length);
    return dishes[dishNum];
  },
  generateRandomMeal() {
    const appetizer = this.getRandomDishFromCourse('appetizers');
    const main = this.getRandomDishFromCourse('mains');
    const dessert = this.getRandomDishFromCourse('desserts');
    const totalPrice = appetizer.price + main.price + dessert.price;
    console.log(appetizer)
    return `Appetizer: ${appetizer}
Main: ${main}
Dessert: ${dessert}
Total Price: ${totalPrice}`;
  },
}
menu.addDishToCourse('appetizers', 1, 1)
menu.addDishToCourse('appetizers', 2, 2)
menu.addDishToCourse('appetizers', 3, 3)
menu.addDishToCourse('mains', 1, 1)
menu.addDishToCourse('mains', 2, 2)
menu.addDishToCourse('mains', 3, 3)
menu.addDishToCourse('desserts', 1, 1)
menu.addDishToCourse('desserts', 2, 2)
menu.addDishToCourse('desserts', 3, 3)
const meal = menu.generateRandomMeal();
console.log(meal);

The specific part of the code that I returns the string to be logged to the console is

generateRandomMeal() {
    const appetizer = this.getRandomDishFromCourse('appetizers');
    const main = this.getRandomDishFromCourse('mains');
    const dessert = this.getRandomDishFromCourse('desserts');
    const totalPrice = appetizer.price + main.price + dessert.price;
    console.log(appetizer)
    return Appetizer: ${appetizer}
Main: ${main}
Dessert: ${dessert}
Total Price: ${totalPrice}`;
  },

What I expected it to print to the console was [ *key*: *value*, *key2*: *value2* ] but instead it printed [ object Object ]. The reason I know the object is defined is that when I replace console.log(appetizer) with console.log(appetizer.name) it prints the correct name.

Upvotes: 4

Views: 5338

Answers (4)

Ben Aston
Ben Aston

Reputation: 55729

TLDR

[object Object] is the default string representation of custom objects. You can override this behavior by implementing your own toString method, or by adding a getter property with the well-known symbol @@toStringTag. console.log is not part of the ECMAScript specification, and in most cases will try to print something more meaningful than [object Object].

Details

JavaScript provides default string representations of objects of the form [object <Tag>].

Some built-in objects, such as Function and Date override the Object#toString method, but we can still get to the default string representation by calling Object#toString on those objects. If we review the output, a pattern emerges!

const toString = Function.prototype.call.bind({}.toString)

// Instances
console.log(toString({}))              // [object Object]
console.log(toString(undefined))       // [object Undefined] ¹
console.log(toString(null))            // [object Null] ¹
console.log(toString(/foo/))           // [object RegExp]
console.log(toString(true))            // [object Boolean]
console.log(toString(''))              // [object String]
console.log(toString(new ArrayBuffer)) // [object ArrayBuffer]
console.log(toString(1))               // [object Number]
console.log(toString(Symbol()))        // [object Symbol]

// Constructor functions
console.log(toString(Number))          // [object Function]    
console.log(toString(Date))            // [object Function]
console.log(toString(RegExp))          // [object Function]

// Namespace objects:
console.log(toString(Math))            // [object Math]
console.log(toString(JSON))            // [object JSON]
console.log(toString(Atomics))         // [object Atomics]
console.log(toString(Intl))            // [object Object] ²
console.log(toString(Reflect))         // [object Object] ²

Custom objects inherit the toString method of Object.prototype, but you can override it:

const o = { dish: 'Fish & Chips', toString() { return JSON.stringify(this) } }
console.log('' + o) // { "dish": "Fish & Chips" }

...but if you directly call Object#toString against your custom object, you will still get [object Object]. You can change this by defining a property with the key of the well-known symbol @@toStringTag:

const o = { get [Symbol.toStringTag]() { return 'Bubblegum!' } }
console.log(Object.prototype.toString.call(o)) // '[object Bubblegum!]'

¹ These values might look strange, but I think they arise from the specification saying that the output of Object#toString should simply be [object <string tag>]. For null and undefined, the string tags are Null and Undefined respectively, giving the values [object Null] and [object Undefined].

² Note that the inconsistency of the value emitted by the toString operation on the Intl and Reflect built-in objects was, the last time I looked (early 2020), an open point of discussion in TC-39, and probably results from different teams implementing those APIs.

Upvotes: 0

Vidikov
Vidikov

Reputation: 1

You need to use JSON.stringify(), which turns the object into a string that console.log will then print out.

When confronted with an object, console.log won't dive into the object and print everything out for you.

It simply recognizes it as an object and logs [Object object].

So for your example, you need to do :

return `Appetizer: ${JSON.stringify(appetizer)};
        Main: ${JSON.stringify(main)}
        Dessert: ${JSON.stringify(dessert)}
        Total Price: ${JSON.stringify(totalPrice)}`;

Upvotes: 0

Orkhan Alikhanov
Orkhan Alikhanov

Reputation: 10050

In both browser and nodejs environment, running console.log(appetizer) produces:

{
  "name": 3,
  "price": 3
}

[object Object] is only produced by running console.log(meal)

Appetizer: [object Object]
Main: [object Object]
Dessert: [object Object]
Total Price: 6

And that's normal because the objects are implicitly cast to string. These are equivalent:

const obj = { a: 1 }
console.log('Hello: ' + obj)
console.log('Hello: ' + obj.toString())
console.log(`Hello: ${obj}`)

If you want your string to contain object's JSON reprenstation use JSON.stringify:

const obj = { a: 1 }
const s = 'Hello: ' + JSON.stringify(obj); /// or JSON.stringify(obj, null, 2); with 2 spacing
console.log(s)

Upvotes: 3

Arun Saini
Arun Saini

Reputation: 7814

Well, if appetizer is a JSON object, you can simply use JSON.stringify(object).

console.log(JSON.stringify(appetizer))

While console.log() will work for objects, there is a method specifically meant for displaying objects to the console called console.dir().

console.dir(appetizer)

Upvotes: 2

Related Questions