You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The most obvious place where *coercion* is involved in equality checks is with the `==` operator. Despite any pre-conceived notions you may have about `==`, it behaves extremely predictably, ensuring that both operands match types before performing its equality check.
990
990
991
-
Recall and review the steps discussed earlier in the chapter for the `IsLooselyEqual()` operation. [^LooseEquality] Its behavior, and thus how `==` acts, can be pragmatically intuited with just these two facts in mind:
991
+
To state something that may or may not be super obvious: the `==` (and `===`) operators always return a `boolean` (`true` or `false`), indicating the result of the equality check; they never return anything else, regardless of what coercion may happen.
992
+
993
+
Now, recall and review the steps discussed earlier in the chapter for the `IsLooselyEqual()` operation. [^LooseEquality] Its behavior, and thus how `==` acts, can be pragmatically intuited with just these two facts in mind:
992
994
993
995
1. If the types of both operands are the same, `==` has the exact same behavior as `===` -- `IsLooselyEqual()` immediately delegates to `IsStrictlyEqual()`. [^StrictEquality]
994
996
@@ -1075,7 +1077,117 @@ I'd observe that even many diehard `===` fans tend to concede that `== null` is
1075
1077
1076
1078
## Coercion Corner Cases
1077
1079
1078
-
// TODO
1080
+
I've been clear in expressing my pro-coercion opinion thus far. And it *is* just an opinion, though it's based on interpreting facts gleaned from studying the language specification and observable JS behaviors.
1081
+
1082
+
That's not to say that coercion is perfect. There's several frustrating corner cases we need to be aware of, so we avoid tripping into those potholes. In case it's not clear, my following characterizations of these corner cases are just more of my opinions. Your mileage may vary.
1083
+
1084
+
### Strings
1085
+
1086
+
We already saw that the string coercion of an array looks like this:
1087
+
1088
+
```js
1089
+
String([ 1, 2, 3 ]); // "1,2,3"
1090
+
```
1091
+
1092
+
I personally find that super annoying, that it doesn't include the surrounding `[ ]`. In particular, that leads to this absurdity:
1093
+
1094
+
```js
1095
+
String([]); // ""
1096
+
```
1097
+
1098
+
So we can't tell that it's even an array, because all we get is an empty string? Great, JS. That's just stupid. Sorry, but it is. And it gets worse:
1099
+
1100
+
```js
1101
+
String([ null, undefined ]); // ","
1102
+
```
1103
+
1104
+
WAT!? We know that `null` coerces to the string `"null"`, and `undefined` coerces to the string `"undefined"`. But if those values are in an array, they magically just *disappear* as empty strings in the array-to-string coercion. Only the `","` remains to even hint to us there was anything at all in the array! That's just silly town, right there.
1105
+
1106
+
What about objects? Almost as aggravating, though in the opposite direction:
1107
+
1108
+
```js
1109
+
String({}); // "[object Object]"
1110
+
1111
+
String({ a: 1 }); // "[object Object]"
1112
+
```
1113
+
1114
+
Umm...OK. Sure, thanks JSfor no help at all in understanding what the object value is.
1115
+
1116
+
### Numbers
1117
+
1118
+
I'm about to reveal what I think is *the* worst root of all coercion corner case evil. Are you ready for it?!?
1119
+
1120
+
```js
1121
+
Number(""); // 0
1122
+
Number(" "); // 0
1123
+
```
1124
+
1125
+
I'm still shaking my head at this one, and I've known about it for nearly 20 years. I still don't get what Brendan was thinking withthis one.
1126
+
1127
+
The empty string is devoid of any contents; it has nothing in it with which to determine a numeric representation. `0` is absolutely ***NOT*** the numeric equivalent of missing/invalid numeric value. You know what number value we have that is well-suited to communicate that?`NaN`. Don't even get me started on how whitespace is stripped from strings when coercing to a number, so the very-much-not-empty `" "` string is still treated the same as `""` for numeric coercion purposes.
1128
+
1129
+
Even worse, recall how `[]` coerces to the string `""`? By extension:
1130
+
1131
+
```js
1132
+
Number([]); // 0
1133
+
```
1134
+
1135
+
Doh! If `""` didn't coerce to `0`-- remember, this is the root of all coercion evil!--, then `[]` wouldn't coerce to `0` either.
1136
+
1137
+
This is just absurd, upside-down universe territory.
1138
+
1139
+
Much more tame, but still mildly annoying:
1140
+
1141
+
```js
1142
+
Number("NaN"); // NaN <--- accidental!
1143
+
1144
+
Number("Infinity"); // Infinity
1145
+
Number("infinity"); // NaN <--- oops, watch case!
1146
+
```
1147
+
1148
+
The string `"NaN"` is not parsed as a recognizable numeric value, so the coercion fails, producing (accidentally!) the `NaN` value. `"Infinity"` is explicitly parseable for the coercion, but any other casing, including `"infinity"`, will fail, again producing `NaN`.
1149
+
1150
+
This next example, you may not think is a corner case at all:
1151
+
1152
+
```js
1153
+
Number(false); // 0
1154
+
Number(true); // 1
1155
+
```
1156
+
1157
+
It's merely programmer convention, legacy from languages that didn't originally have boolean `true` and `false` values, that we treat `0` as `false`, and `1` as `true`. But does it *really* make sense to go the other direction?
1158
+
1159
+
Think about it this way:
1160
+
1161
+
```js
1162
+
false + true + false + false + true; // 2
1163
+
```
1164
+
1165
+
Really? I don't think there's any case where treating a `boolean` as its `number` equivalent makes any rational sense in a program. I can understand the reverse, for historical reasons: `Boolean(0)` and `Boolean(1)`.
1166
+
1167
+
But I genuniely feel that `Number(false)` and `Number(true)` (as well as any implicit coercion forms) should produce `NaN`, not `0` / `1`.
1168
+
1169
+
### Coercion Absurdity
1170
+
1171
+
To prove my point, let's take the absurdity up to level 11:
1172
+
1173
+
```js
1174
+
[] == ![]; // true
1175
+
```
1176
+
1177
+
How!? That seems beyond credibility that a value could be coercively equal to its negation, right!?
1178
+
1179
+
But follow down the coercion rabbit hole:
1180
+
1181
+
1.`[] == ![]`
1182
+
2.`[] == false`
1183
+
3.`"" == false`
1184
+
4.`0 == false`
1185
+
5.`0 == 0`
1186
+
6.`0 === 0`->`true`
1187
+
1188
+
We've got three different absurdities conspiring against us: `String([])`, `Number("")`, and `Number(false)`; if any of these weren't true, this nonsense corner case outcome wouldn't occur.
1189
+
1190
+
Let me make something absolutely clear, though: none of this is `==`'s fault. It gets the blame here, ofcourse. But the real culprits are the underlying `string` and `number` corner cases.
0 commit comments