

You don’t have to be a JavaScript novice to get confused by this…

if ([0]) { console.log([0] == true); //false console.log(!![0]); //true }



or this…

if ("potato") { console.log("potato" == false); //false console.log("potato" == true); //false }



The good news is that there is a standard and all browsers follow it. Some authors will tell you to fear coercion and and code against it. I hope to persuade you that coercion is a feature to be leveraged (or at the very least understood), not avoided…



Is x true? Does x equal y? Questions of truth and equality at the kernel of three major areas of JavaScript: conditional statements and operators (if, ternaries, &&, || etc.), the equals operator (==), and the strict equals operator (===). Lets see what happens in each case…

Conditionals

In JavaScript, all conditional statements and operators follow the same coercion paradigm. We’ll use the if statement by way of example.

The construct if ( Expression ) Statement will coerce the result of evaluating the Expression to a boolean using the abstract method ToBoolean for which the ES5 spec defines the following algorithm:

Argument Type Result Undefined false Null false Boolean The result equals the input argument (no conversion). Number The result is false if the argument is +0, −0, or NaN;

otherwise the result is true. String The result is false if the argument is the empty String (its length is zero);

otherwise the result is true. Object true.

This is the formula JavaScript uses to classify values as truthy ( true , "potato" , 36 , [1,2,4] and {a:16} ) or falsey ( false , 0 , "" , null and undefined ).

Now we can see why, in the introductory example, if ([0]) allows entry to the subsequent block: an array is an object and all objects coerce to true .

Here’s a few more examples. Some results may be surprising but they always adhere to the simple rules specified above:

var trutheyTester = function(expr) { return expr ? "truthey" : "falsey"; } trutheyTester({}); //truthey (an object is always true) trutheyTester(false); //falsey trutheyTester(new Boolean(false)); //truthey (an object!) trutheyTester(""); //falsey trutheyTester(new String("")); //truthey (an object!) trutheyTester(NaN); //falsey trutheyTester(new Number(NaN)); //truthey (an object!)



The Equals Operator (==)

The == version of equality is quite liberal. Values may be considered equal even if they are different types, since the operator will force coercion of one or both operators into a single type (usually a number) before performing a comparison. Many developers find this a little scary, no doubt egged on by at least one well-known JavaScript guru who recommends avoiding the == operator altogether.

The avoidance strategy bothers me because you can’t master a language until you know it inside out – and fear and evasion are the enemies of knowledge. Moreover pretending == does not exist will not let you off the hook when it comes to understanding coercion because in JavaScript coercion is everywhere! Its in conditional expressions (as we’ve just seen), its in array indexing, its in concatenation and more. What’s more coercion, when used safely, can be an instrument of concise, elegant and readable code.

Anyway, rant over, lets take a look at the way ECMA defines how == works. Its really not so intimidating. Just remember that undefined and null equal each other (and nothing else) and most other types get coerced to a number to facilitate comparison:

Type(x) Type(y) Result x and y are the same type See Strict Equality (===) Algorithm null Undefined true Undefined null true Number String x == toNumber (y) String Number toNumber (x) == y Boolean (any) toNumber (x) == y (any) Boolean x == toNumber (y) String or Number Object x == toPrimitive (y) Object String or Number toPrimitive (x) == y otherwise… false

Where the result is an expression the algorithm is reapplied until the result is a boolean. toNumber and toPrimitive are internal methods which convert their arguments according to the following rules:

ToNumber Argument Type Result Undefined NaN Null +0 Boolean The result is 1 if the argument is true.

The result is +0 if the argument is false. Number The result equals the input argument (no conversion). String In effect evaluates Number(string)

“abc” -> NaN

“123” -> 123 Object Apply the following steps: 1. Let primValue be ToPrimitive(input argument, hint Number).

2. Return ToNumber(primValue).

ToPrimitive Argument Type Result Object (in the case of equality operator coercion) if valueOf returns a primitive, return it. Otherwise if toString returns a primitive return it. Otherwise throw an error otherwise… The result equals the input argument (no conversion).

Here are some examples – I’ll use pseudo code to demonstrate step-by-step how the coercion algorithm is applied:

[0] == true;

//EQUALITY CHECK... [0] == true; //HOW IT WORKS... //convert boolean using toNumber [0] == 1; //convert object using toPrimitive //[0].valueOf() is not a primitive so use... //[0].toString() -> "0" "0" == 1; //convert string using toNumber 0 == 1; //false!



“potato” == true;

//EQUALITY CHECK... "potato" == true; //HOW IT WORKS... //convert boolean using toNumber "potato" == 1; //convert string using toNumber NaN == 1; //false!



“potato” == false;

//EQUALITY CHECK... "potato" == false; //HOW IT WORKS... //convert boolean using toNumber "potato" == 0; //convert string using toNumber NaN == 0; //false!



object with valueOf

//EQUALITY CHECK... crazyNumeric = new Number(1); crazyNumeric.toString = function() {return "2"}; crazyNumeric == 1; //HOW IT WORKS... //convert object using toPrimitive //valueOf returns a primitive so use it 1 == 1; //true!



object with toString

//EQUALITY CHECK... var crazyObj = { toString: function() {return "2"} } crazyObj == 1; //HOW IT WORKS... //convert object using toPrimitive //valueOf returns an object so use toString "2" == 1; //convert string using toNumber 2 == 1; //false!



The Strict Equals Operator (===)

This one’s easy. If the operands are of different types the answer is always false. If they are of the same type an intuitive equality test is applied: object identifiers must reference the same object, strings must contain identical character sets, other primitives must share the same value. NaN , null and undefined will never === another type. NaN does not even === itself.

Type(x) Values Result Type(x) different from Type(y) false Undefined or Null true Number x same value as y (but not NaN ) true String x and y are identical characters true Boolean x and y are both true or both false true Object x and y reference same object true otherwise… false

Common Examples of Equality Overkill

//unnecessary if (typeof myVar === "function"); //better if (typeof myVar == "function");

..since typeOf returns a string, this operation will always compare two strings. Therefore == is 100% coercion-proof.

//unnecessary var missing = (myVar === undefined || myVar === null); //better var missing = (myVar == null);

…null and undefined are == to themselves and each other.

Note: because of the (very minor) risk that the undefined variable might get redefined, equating to null is slightly safer.

//unnecessary if (myArray.length === 3) {//..} //better if (myArray.length == 3) {//..}

…enough said 😉

Further Reading

Peter van der Zee: JavaScript coercion tool

A nice summation of the equality coercion process, replete with an impressive automated tutorial

Andrea Giammarchi: JavaScript Coercion Demystified

ECMA-262 5th Edition

11.9.3 The Abstract Equality Comparison Algorithm

11.9.6 The Strict Equality Comparison Algorithm

9.1 toPrimitive

9.2 toBoolean

9.3 toNumber