Why {} + {} gives "[object Object][object Object]" in JavaScript

Chidume Nnamdi 🔥💻🎵🎮
Dev Proto
Published in
6 min readJul 30, 2020

--

JavaScript is huge and has unforgiven rules and specs we must abide to.

It’s operations give surprising results. One of the operations is the addition of two objects {} + {}.

Can you guess the result?

“[object Object][object Object]”

How and why should that be the result? Do you know?

If you don’t know, don’t worry. In this article, I will explain why that contraption was returned.

{} + {}

Now, this {} + {} is an addition of two objects.

JavaScript has a specification that all JavaScript engines must follow when running JavaScript code. Like that, the addition operator has its spec on what to do.

The spec says that:

Addition operation either performs string concatenation or numeric addition.

The above is the rule for addition in JavaScript.

See, the right and left values are converted to their primitive form first using the ToPrimitive function.

What is primitive or primitive form? In JS we have primitive data types and object data types:

Primitives are boolean, number, string, null, undefined while Objects are Objects, Array, Function, RegExp, Date.

So if the right or left value is a primitive the ToPrimitive function will just return it, no conversion needed. But if the right or left value is an Object, the ToPrimitive will try converting it to its primitive form.

As our right value {} and left {} values are Objects, the ToPrimitive will convert them to their primitive form.

Let’s see ToPrimitive spec:

We see that this ToPrimitive uses a hint to try to convert its input.

The method first checks the input is an Object because only Objects are to be converted to its primitive form. If the input is not an Object then it must be a primitive, so it just returns the input, no need for conversion to primitive.

This function was called without a hint, so the hint becomes “number” following the if checks. Next, the method checks @@toPrimitive is not defined in the object. If it’s defined the implementation in the @@toPrimitive will be called and the result returned as the primitive conversion of the object.

@@toPrimitive is the way JS gives the outside world power to add their implementation of ToPrimitive conversion. So instead of the built-in method to run theirs is run.

If @@toPrimitive is not defined, OrdinaryToPrimitive function is called with the hint “number” and the object, {}.

Let’s see the OrdinaryToPrimitive spec:

Here two methods are called: toString and valueOf(). OrdinaryToPrimitive will call each method on the argument, which must be an Object. Any method that returns a primitive value, the result will be returned to ToPrimitive. The order of their calls depends on the hint passed. If the hint is string, then toString is called first. If the hint is not a string then valueOf is called first. The hint must either be a string or a number.

Since the hint passed here is “number”, valueOf method will be called on our object first.

See the spec:

Note: Object provides functionality common to all JavaScript objects.

ToObject will be called with our object:

Below is ToObject conversion table:

Looking at the table, we see that what is returned is based on the argument. All the data types are converted to Objects. The primitives are converted to object by calling new on their corresponding data class. boolean primitives have new Boolean() to convert them to Boolean object, strings have new String(), null and undefined throw TypeError exception because they have no corresponding class. numbers, symbols have new Number() and new Symbol() called respectively. With new called, the primitives are converted to objects. objects are returned, no conversion needed because already they are Objects. Remember, JavaScript has two types of values, primitives, and objects.

We see that the argument is simply returned if it is an object, so in our case, the argument is {} which is an object so {}will be returned.

Now, the OrdinaryToPrimitive will see an object returned:

Since the result is an object, it will loop again to the toString and call the toString method on {}.

Let’s see the spec

This method tries to convert its input to string primitive value.

See, there are checks for undefined and null, they will return “[object Undefined]” and “[object Null]” respectively. Next, it calls ToObject on our argument which is our object {}, we know previously it will return the object. The result of the ToObject is passed through many checks to return the specified string.

There are checks for String, Function, Error, Boolean, Number, Date, Arguments, and RegExp.

The builtinTag is used to hold the string to return.

Ours is an object, so the method assigns “Object” to builtinTag.

The method then calls @@toStringTag on the object. @@toStringTag is the string description of an object.

JS uses the @@toStringTag to let users and objects define the string name of their objects.

So, here the method checks by calling @@toStringTag to see if the object has its defined string name.

A String valued property that is used in the creation of the default string description of an object. Accessed by the built-in method Object.prototype.toString.

In Object their is no @@StringTag, so the method uses the builtinTag value “Object”, then, it concatenates the builtinTag (“Object”) in between “[object “ and “]”, which will give:

“[object Object]”

and return the concatenation.

:)

Also, the right value {} , will follow through the same process and return "[object Object]"

Now, this:

({} + {})

will be this:

“[object Object]” + “[object Object]”

In the Addition operator, lprim will be “[object Object]” and rprim will be “[object Object]”. It will check if either lprim or rprim is a string:

The lprim and rprim both hold “[object Object]”, so they check succeeds. They are both converted to a string, as seen above. Which checking the ToString table returns the same string passed.

There is no need for conversion it’s already a string.

Back to the Addition operator, both lstr and rstr will be “[Object object]”. So, the lstr and rstr are concatenated:

“[Object object][Object object]”

Conclusion

You now, know why {} + {} gives "[Object object][Object object]".

If you feel lost, just cool down and follow the method calls carefully.

Always look at the specifications.Thanks for stopping by my little corner of the web. I think you’ll love my email newsletter about programming advice, tutoring, tech, programming and software development. Just sign up below:

Follow me on Twitter.

--

--

Chidume Nnamdi 🔥💻🎵🎮
Dev Proto

JS | Blockchain dev | Author of “Understanding JavaScript” and “Array Methods in JavaScript” - https://app.gumroad.com/chidumennamdi 📕