Truth, Equality and JavaScript

 
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

54 thoughts on “Truth, Equality and JavaScript

  1. Made me realize that == could be very expensive for comparing different types of data (ex: [0]==true). Wonder if there is a way to see (maybe through a tool) if the the browser performs the exact similar steps to reach a conclusion.

  2. ” Its really not so intimidating. ” – I beg to differ!

    I don’t always side with Crockford, but I have to say I agree with him in avoiding == – I think it leads to confusing code.

    I’ll use coercion in conditional statements where the rules are much simpler, but not otherwise.

    Very interesting post nonetheless, thanks!

  3. Hey
    I think there is an error with your table – the table above suggests that when an Object is compared to a String, the toPrimitive will prefer using toValue over toString. This is not true – toString will take precedence over toValue when compared to strings (as it should).

    1. Hey Arieh.

      Check out this example

      var obj = {
          valueOf: function() {return 1},
          toString: function() {return "2"}
      }
      
      obj == "1"; //true
      obj == "2"; //false
      

      ToPrimitive is invoked without a hint which means it defaults to checking valueOf first

  4. Angus excellent article!
    I caught two typos:

    “potato” == true; 
    ....
    NaN = 1; //false!
    

    Should be:
    NaN == 1; //false!

    Also:

    “potato” == false; 
    ....
    NaN = 0; //false!
    

    Should be:
    NaN == 0; //false!

    Also you haven’t mentioned about potential errors in non strict equality. For example if you applied it with host object or object which cannot be converted to primitive value it will throw an error:

    666 == {toString : null, valueOf : null}; //cause TypeError 
    
    1. Hey Asen,
      Thanks for the praise and pointing out typos (fixed now)!

      Actually I do mention potential for error in the ToPrimitive table “(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”

  5. The problem isn’t the coercion, it’s the lack of transitivity. One example from Crockford’s book:

    false == undefined // false
    false == null // false
    null == undefined // true

    I use coercion all the time, but not when trying to find out if things are equal. I only use it in expressions like “if (x.y)” to find out if x.y is one of the “falsy” values.

    1. Hey Tyler

      Sure its confusing if you don’t know the rules – but the rules are not that hard (e.g null and undefined are == to themelves, each other and nothing else) and I recommend learning them because even if you don’t use == in your code someone else will (e.g. jquery)

    2. Actually this is not exactly transitivity.Still, this operator does yield transitive results, see here:

      ” == 0 // true
      0 == ‘0’ // true
      ” == ‘0’ // false

  6. Hi Angus,

    Interesting post. I still think though === enhances code readability and causes less confusion over ==. I totally agree with:

    even if you don’t use == in your code someone else will

    However, I see one more benefit of === over ==, that in cases when the two arguments are not of same type, It can be faster. I am referring to this discussion at SO: http://stackoverflow.com/questions/359494/javascript-vs-does-it-matter-which-equal-operator-i-use

    Would love to know your thoughts about the speed difference.

    1. Hi Rajat

      You can look at it this way. You can’t master a language until you understand how all its features work. So everyone who wants to be javascript expert should understand ==. Now if you understand it and still don’t want to use it – no problem 🙂 But as we discussed you’ll still encounter == in colleague’s code or 3rd party libraries

      The performance improvement if any will be tiny. Assuming operands are of the same type the very first check that == does (at least according to the spec) is to delelgate to ===. So virtually zero cost involved

      In fact there are some benchmarks over at jsperf that suggest == might even be quicker in case of same operand type (for reasons I don’t understand) http://jsperf.com/equality-operators-with-the-same-types

  7. Good article.

    Many coders on the web do both JS and PHP. That’s another reason for confusion. Especially if PHP is your first language, since the type coercion rules almost never become an issue for newbies in PHP. They are much more intuitive.

    I teach web technologies for a living. Students will run into problems in JS a lot, but in PHP it almost never happens.

    Mostly this is because when comparing to a boolean value, in PHP type coercion always happen on the non-boolean side. The fact that JS first makes the boolean value a number is not intuitive at all.

    A few other notable differences:
    In PHP the string “0” is falsy
    In PHP an empty array or object is falsy
    In PHP there is no toxic NaN
    In PHP there are no wrapper objects for primitives, which has downsides too, but makes this stuff less confusing.

    And in PHP you have total transivity (AFAIK)

    (Forgive me from teaching stuff most of you know, but I will point my students to this article, and having those differences spelled out will help them. Given the time, I’d love to make a comparison table between PHP and JS on this. Hint: I do not and somebody else should beat me to it.)

    1. Thanks Lars, maybe I’m biased but I can cope with all objects being truthey even those that have zero properties.

      Now someone build that comparison table ! 🙂

  8. Nice article, and I whole-heartedly agree that understanding the language thoroughly is a worthy objective; you *will* come across code which uses ==, and you *will* come across bugs in code that is using == unsafely or inappropriately — even code you write yourself, knowing the rules; we all make mistakes! 🙂

    When writing code, though, you should also consider how it will be read by other developers, perhaps not as well versed in the language semantics as you. For example, not everyone has the operator precedence rules memorized, so I tend to use more parens than strictly necessary in expressions (especially complex ones) to ensure they will be interpreted the same way by other people as by the runtime…

  9. How does coercian work on the other comparison operators? Because they also evaluate to either true or false they might as well fit in nicely into this post. I’ll expect them to stay close to the == rules, but I’m not sure …

    1. good question

      while there is == and ===, there is only >= (there is no >==)

      So the same folks who insist on checking a === b can only check a >= b (which will perform == style coercion)

      3 >= false; //true 😐

  10. Very nice article. I think it’s the tenth time I read this article. I love now to know exactly how internally javascript process this old magic coercion. It would be nice to talk about the switch case because I recently found that it use a strictly comparison without coercion compared to the ‘if’ statement. Learning and mastering javascript and understanding exactly how it work internally in all situations drives me nuts because of my poor brain that try to shoot in so many places. Your blog is my favorite about javascript on the entire web. I have a couple of javascript books but no one talk so deeply about javascript. The best thing would be to create a book with all these posts and I will buy a copy for me and all my coworkers. Thank you.

  11. Excellent article,I have never used the coercive equality because I(resources like Crockford on Youtube,Codecademy and others) kept myself(me) in the dark about what it did,now I know.I will still avoid it as much as possible and when I do use it,I will write a comment about what to expect from it.I wish JS would do something the obvious way(sad smiley here),if it did it wouldnt be infinitely interesting.

  12. Pingback: Quora

Leave a comment