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:
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). |
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
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.
Sweet.
” 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!
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 usingtoValue
overtoString
. This is not true –toString
will take precedence overtoValue
when compared to strings (as it should).Hey Arieh.
Check out this example
ToPrimitive is invoked without a hint which means it defaults to checking valueOf first
Angus excellent article!
I caught two typos:
Should be:
NaN == 1; //false!
Also:
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:
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 iftoString
returns a primitive return it. Otherwise throw an error”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.
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)
> even if you don’t use == in your code someone else will
On this point we are in complete agreement. =)
Actually this is not exactly transitivity.Still, this operator does yield transitive results, see here:
” == 0 // true
0 == ‘0’ // true
” == ‘0’ // false
Another great article from my js teacher. Keep it up…
Haha thanks boss! Still working late I see 😉
Fantastic article – definitely cleared up a few things I’d never considered 🙂
Thanks!
Great article Angus.
Hi Angus,
Interesting post. I still think though === enhances code readability and causes less confusion over ==. I totally agree with:
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.
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
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.)
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 ! 🙂
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…
agreed – an alternative is well chosen comments – they serve to disambiguate without adding unnecessary clutter to the code itself
This is very useful stuff! I love that
NaN != NaN
and("oele" == false) == ("oele" == true)
Very useful stuff indeed!
Nice article (I’m not a ninja coder) I’ll save it on my computer. 🙂
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 …
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 😐
good question and good answer! this helps a lot, Thanks!
For the second-to-last example, wouldn’t the following be just as effective?
var missing = !(myVar);
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.
wonder if we could get all these rules from ES5 specs or not…
Its annoying.
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.
Wow, great write-up. Immediately bookmarked. Thanks!