What determines whether node prints `[object Object]` or a full object?

Viewed 619

Minor note: I'm very familiar with console.log(), JSON.stringify(), Object.prototype.toString(), util.inspect() etc - this question isn't asking how to show the contents of objects, but rather why node's behavior changes in different circumstances.

I know I can console.log(someObject) and node will print:

[object Object]

node js function return [object Object] instead of a string value has some good information on this.

I know that [object Object] is from Object.prototype.toString()

I know I can console.log(JSON.stringify(someObject, null, 2) and node will print:

{
  foo: 'bar'
}

Or use util.inspect() etc. See this answer

However it seems that sometimes node will actually print the objects contents

If I make a new file called runme.js with the contents:

console.log({foo: 'bar'})

And run node runme.js node will print

{ foo: 'bar' }

Not [object Object]

Why is node not printing [object Object]?

Edit: per Keith's question, [object Object] will appear when I run:

console.log(`Check me out ${{foo: 'bar'}}`)

Logs [object Object]

What determines whether node uses Object.prototype.toString() (aka [object Object]) vs printing the contents of the object?

3 Answers

Console logging will output [object Object] for an object value that was converted to a string before being passed as a parameter to a console method, or if an object value argument is consumed and inserted into a format string passed as the first argument.

The latter occurs if a format string contains C-like percentage prefixed conversion sequences within it that consume the next unused argument and embed it the format string.

If a format string is used, and more (object) arguments are passed to the method than are used by the format string, remaining unconsumed arguments that are objects are logged as object values without conversion to strings.

So some expected values and actual log output:

const someObject = {a: "a"};

// expected output: "string [object Object]"
console.log( "string " + someObject);

// expected output: "same deal: [object Object]"
console.log( "same deal: %s", someObject); // converted by format string

// expected output: 'someObject = {a: "a"}'
console.log( "someObject = ", someObject) // someObject was not consumed by format string!

Of course Node does not present an interactive object view on the text console, and in simple cases it can look a lot like JSON.stringify() output. However Node does make an attempt to

  • identify the constructor of the object,
  • color code values according to their data type
  • handle presentation of circular references

that expand its debugging potential beyond simple JSON.stringify() conversions.

In my experience, console.log detects when it is passed the object and prints the entire object like the example you showed.

console.log({foo: 'bar'})
// => { foo: 'bar' } 

However when an object is concatenated to a string, it's converted to a string using the .toString() method and then passed to the console.log method.

Example:

console.log('' + { foo: 'bar' }) 
// => [object Object]

This is because before passing to console.log method, the expression is evaluated to '' + { foo: 'bar' }.toString(). That's my understanding of how it works. Though the internal workings might be more complex.

As has been worked out, template literals will box the args using .toString.

But if you find you use template literals a lot for debugging, and you want to use JSON.stringify instead.

Here is a simple example below.

function tagJSON(strings, ...args) {
  const output = [];
  let nextString = 0;
  let nextArg = 0;
  //loop all string & args
  while (nextArg < args.length) {
    output.push( strings[nextString] );
    output.push( JSON.stringify(args[nextArg]) );
    nextArg += 1;
    nextString += 1;
  } 
  //might be a string left at the end..
  if (nextString < strings.length)
    output.push( strings[nextString] ); 
  //finally output our new concated string & stringified(args)
  return output.join('');
}

var someobject = {x: 'y'};
let output = tagJSON`someobject = ${someobject}`;

console.log(output);
console.log(tagJSON`boolean = ${true} or ${false}`);
console.log(tagJSON`strings will double quote ${'hello world'}`);

Related