OK, that’s all the cards we care about. Moving on to heroes. If you write an expression like true - "a" , you’re calling the minus operator hero. That hero only has the ToNumber card at its disposal, and it will use it to convert both operands to numbers and give you back NaN .

Most of coercion and confusion in JavaScript programming happens when operators are involved. There are some other places where coercion happens, like the if statement mentioned at the beginning, the condition part of a loop, or property access expressions that might wrap objects around primitive values, but these are pretty straightforward.

Operators is where it’s at. With all these operator heroes spread all over the language, how does one know which card will any one of them play at any given time? Well, I started this article to draw fancy cards and teach you how JavaScript coercion works with one rule and three exceptions. And I’m all out of cards to draw…

Rule 1

The “remarkably average — extremely average person” image above, is there to suggest the first rule applies to most operators and value types. And it could be phrased something like “most operators convert almost everything to numbers”. That would be a correct, but not so useful rule. To add usefulness / dramatic effect, I will later add some exceptions to keep the rule correct, and rephrase the rule itself like this:

RULE 1: All operators convert everything to numbers

Whoa! How’s that for effect? I know, it’s not entirely correct standing by itself like that, and I will define some exceptions to the rule later to account for all the… well exceptions. But I think it’s helpful to start looking at it from that perspective. You will learn the exceptions below, and when facing any code that does coercion, if there are no exceptions you can think of that apply to it, you just apply the one rule you’ve already learned.

For example, I know there are no exceptions that apply to the code false == [] , so according to Rule 1 it will convert both of its operands to numbers. Both false and [] convert to the number 0 so the comparison returns true .

If I didn’t know about Rule 1, I would try to reason about how the == operator might work, and most probably assume that since it has a boolean on the left side, it will also convert the array to its right into a boolean. And since that empty array converts to the boolean true , I would wrongly conclude that the comparison returns false . When in fact the == operator never played the ToBoolean card on the array. It just played the ToNumber card on both operands like most operators do in most cases.

Exceptions

Exception 1: Logical operators convert their operands to booleans

Well, not much to add here except that the conditional ( ternary a ? b : c ) operator also falls into this category as it will convert the expression before the question mark to a boolean. The other logical operators are the logical AND ( && ), the logical OR ( || ), and the logical NOT ( ! ) and they all convert their operands to booleans, not numbers.

Exception 2: == and + play the more neutral ToPrimitive card against objects instead of ToNumber

Whaaat? A new card? Well yes, but no need to freak out. ToPrimitive is so unimportant I didn’t even draw a card for it. It only makes a difference when Date or Symbol objects are used with == or + operators.

It differs from ToString and ToNumber in that it won’t try to convert any further the primitive representation of the object that it gets from valueOf or toString . As for the order in which it tries to call these methods, it’s valueOf first for all objects, just like ToNumber, except for date objects, for which it’s toString first. For Symbol objects, it returns the underlying primitive symbol without calling any of the two methods at all. What this means in code:

// Date object and +

const today = new Date();

1 + today; // "1Mon Dec 18 2017 ..." date converted to string and concatenated to "1". // Symbol object and ==

const sym = Symbol('some symbol'); // primitive symbol value

const symObj = Object(sym); // object wrapper

symObj == sym; // true. object converted to symbol not number

Minor note about == ( exception 2.1 if you will): null and undefined are only considered equal to each other and not converted to any other types.

Exception 3: The plus operator works too hard and cares too much

I initially thought of using the “meme overload” meme to describe the overloaded plus operator, but that would be a more accurate description of this article itself.

Here’s how the plus operator works: