Rationale for why ECMAScript converts primitive values to numbers in == operator comparisons when one is boolean
Raymond Plante wrote:
Just curious to find out what the motivation for converting both values of an == operation to integers if one is an integer or a boolean.
This situation allows for: if ("true"){} // to evaluate to true if("true" == true){} // to evaluate to false as it's really checking if( NaN == 1){}
Consider Perl:
$ perl -e 'print 0 == "true";' 1
Ok, poor rationale -- but I created JS in May 1995, in the shadow of AWK, Perl 4, Python 1.2 (IIRC), TCL.
I should have paid more attention to AWK than Perl, given
$ awk 'END {print(0 == "0")}' 1D $ awk 'END {print(0 == "")}' 0D
In some ways, JS's == operator splits the difference between Perl (where non-numeric strings such as "true" convert to 0) and AWK (where only "0" converts to 0) by converting to NaN. That way, at least, we have
js> 0 == ""
true js> 0 == "true"
false
But the full truth is not that I was carefully emulating other languages. Rather, some Netscapers working to embed JS (then "Mocha") in a PHP-like server (LiveWire) wanted sloppy conversions, so programmers could match HTTP header strings (server side) or HTML form fields (client side) against, e.g., 404 and the like, without explicit coercion by the programmer.
But it was the 90s, I was in a tearing hurry, these ex-Borland Netscapers were persistent. So, as I said at Strange Loop last year (www.infoq.com/presentations/State-JavaScript), "I was an idiot! I gave them what they wanted!"
Implicit conversions are my biggest regret in JS's rushed design, bar none. Even including 'with'!
Does anyone know the exact reason the choice was made not to convert to boolean any value compared against a boolean in with the == operator?
The general idea is the narrower type should widen. Thus, true == 1 follows by projecting boolean {false, true} onto {0, 1}, as in C++.
But why not widen true to string, since the other operand in your example is "true"? Good question. The bias toward comparing strings as numbers if either operand is a number or a boolean stems from the HTTP header and numeric-string HTML form field use-cases. Not good reasons, again, but that's how JS "works" :-|.
You can see this in the ECMA-262 Edition 5.1 spec, 11.9.3 The Abstract Equality Comparison Algorithm, steps 6 & 7 (read in light of steps 4 & 5):
- If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
- If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
- If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
- If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
This is all in a big "else clause where Type(x) and Type(y) for x == y are not the same.
Sorry there's no pearl (sic) of wisdom here. In addition to implicit conversions, == and != do not widen operands directly (no intermediate conversions) to the narrowest width that can hold the other operand without data loss. This narrowing string to number is just a botch.
If we fixed this botch, we'd still have:
0 == "0" true == "1" false == "0"
But we would also have what your example wants:
true == "true"
Some take this botch, on top of any implicit conversion under the hood, as another reason to use === and !== always (because they never convert), and to utterly shun == and !=. Others disagree (especially when testing x == null, a one-operator way to test x === null || x === undefined).
Since the web grows mostly-compatibly until very old forms die off, we're stuck with == and !=, so I say it pays to learn what the sloppy equality operators do. Having done that, it seems to me one may use them where they win: when you know the operands are same-type, e.g.
typeof x == "function", etc. x == null
And otherwise, use === and !==.
Brendan Eich wrote:
If we fixed this botch, we'd still have:
0 == "0" true == "1" false == "0"
Sorry, make that:
0 == "0" 1 == "1" true != "1" false != "0"
Also:
false != ""
Per my calling the preference for number over string conversion a botch, we would not have true == "1" or false == "0", because that narrows from string to number. It's true the narrowing loses no bits, and one can widen 0 back to "0" and 1 back to "1", but I meant to illustrate what removing all number-over-string bias from the implicit conversion spec for == would do.
Would such a change break a lot of code on the web? I'd bet large sums it would.
But we would also have what your example wants:
true == "true"
This part stands.
Brendan Eich wrote:
The bias toward comparing strings as numbers if either operand is a number or a boolean stems from the HTTP header and numeric-string HTML form field use-cases. Not good reasons, again, but that's how JS "works" :-|.
One more note: it could be argued that narrowing from string to number would be ok (as in, useful most of the time, and not unsafe) if any non-numeric, non-empty string-to-number implicit conversion attempt threw an exception.
Here's where another path-dependent bias in JS's design bit: no try/catch in JS1 or any ECMA-262 standard till ES3.
The lack of exception handling also meant undefined is imputed for missing obj.foo property get where obj has no such property. That is still biting back, perhaps as much as or more than implicit conversions bite ==. It is also the basis of web JS's winning "object detection" pattern, which fairly beats all other versioning schemes I've seen, especially a-priori ones based on explicit numbering and opt-in.
If only I'd taken the time to add an existential operator for object detection, so one could write
function emulateRequestAnimationFrame(...) {...} if (!window.requestAnimationFrame?) window.requestAnimationFrame = emulateRequestAnimationFrame;
IOW, if only I'd made window.noSuchProperty throw but window.noSuchProperty? evaluate to truthy or false (details still TBD, see the "fail-fast object destructuring" thread revival, the Nil idea).
Just curious to find out what the motivation for converting both values of an == operation to integers if one is an integer or a boolean.
This situation allows for: if ("true"){} // to evaluate to true if("true" == true){} // to evaluate to false as it's really checking if( NaN == 1){}
Does anyone know the exact reason the choice was made not to convert to boolean any value compared against a boolean in with the == operator?
, Raymond J. Plante
StackOverflow: stackoverflow.com/users/722263/ray LinkedIn: www.linkedin.com/in/raymondjplante Blog: www.kineticklink.com