Unraveling JavaScript's Quirky Object Addition
JavaScript is known for its quirks, and one particularly perplexing behavior is how it handles addition with objects and arrays. Consider these two expressions:
console.log({} + {}); // NaN
console.log({} + []); // "[object Object]" (outputs "0" in some environments)
Why does adding two empty objects result in NaN
, while adding an empty object and an empty array yields "[object Object]"
(or "0"
in some environments)? Let's dive into the details.
The Plus Operator and Type Coercion
In JavaScript, the +
operator isn't just for addition. When given non-number operands, it tries to convert them to strings and concatenate them. This behavior is called type coercion.
When you use +
with an object, JavaScript first calls the object's valueOf()
method. If that doesn't return a primitive value, it tries calling toString()
. The resulting primitive value is then used in the addition.
The Curious Case of {} + {}
When you write ({} + {})
, JavaScript interprets the first {}
as an empty object literal. However, the second {}
is treated as an empty block statement, not an object.
So {} + {}
is really evaluated as {} + {}
, which is equivalent to +{}
. The unary +
operator tries to convert its operand to a number. Calling valueOf()
on an empty object returns the object itself, which isn't a primitive. So JavaScript tries toString()
, which returns "[object Object]"
.
Trying to convert "[object Object]"
to a number results in NaN
. That's why {} + {}
evaluates to NaN
.
The Surprising Result of {} + []
In the expression ({} + [])
, both {}
and []
are interpreted as object literals. Calling valueOf()
on an empty array returns the array itself, so JavaScript moves on to toString()
. An empty array converted to a string is just ""
.
Now we have ({}).toString() + "".toString()
, which is "[object Object]" + ""
. Concatenating these strings results in "[object Object]"
.
However, some JavaScript environments (notably, browsers) go a step further. After getting the string "[object Object]"
, they try to convert it back to a number for the addition. Converting "[object Object]"
to a number yields 0
. So in these environments, ({} + [])
evaluates to 0
.
Preventing Confusion
To avoid these unexpected behaviors, it's best to be explicit about types and conversions. If you really need to add objects or arrays, convert them to numbers or strings explicitly before the addition.
For example, you could use the Number()
function to convert an object to a number (which will result in NaN
for non-numeric objects), or the String()
function to convert it to a string:
console.log(Number({}) + Number({})); // NaN
console.log(String({}) + String({})); // "[object Object][object Object]"
Better yet, avoid using +
for anything other than numeric addition or string concatenation. For objects and arrays, use appropriate methods like Object.assign()
or the spread operator (...
) to combine them.
By being explicit and avoiding unnecessary type coercion, you can write clearer, more predictable code. JavaScript's quirks can be amusing, but they shouldn't be allowed to cause bugs in your programs!