Transcript from the "Special Values" Lesson
>> Kyle Simpson: Now there's some values within the primitive types that we should pay attention to. Some values that we wanna look at. Specifically we're gonna look at NaN, which is transliterated as not a number. Although we'll find out in just a moment. That's a really terrible name for that.
[00:00:40] That's probably super annoying to math folks. [COUGH] But these are special values that we should pay attention to. Null is a value, as well as a type. It's the only value in the null type. Undefined is the only value in the undefined type. And I mentioned here, the void operator.
[00:01:01] That's an important operator, but it doesn't get any attention. That's how you can take any value and produce the undefined value from it. So it takes whatever you put in. Say void true, void function, whatever. You're gonna get back undefined. Now there's not a lot of uses for that, but there's a couple places that I've used that in the past.
[00:02:22] The exact same thing is true. Some languages try to hide it a little bit more than others. But this is just all basic computing science, discrete mathematics stuff, okay? So -0, even though that sounds strange and it's counterintuitive from a mathematic perspective, is something that's required by IEEE.
[00:02:40] And it turns out there's at least one or two small used cases where it's helpful, so we're gonna look at that in just a moment. NaN. Supposedly, NaN stands for not a number. Let me tell you where NaN actually comes from. Any time you try to take a value and convert it to a number, and that conversion fails, the resultant value is to tell you this was an invalid number.
[00:03:08] That is the only place that NaN comes from. So if we look at this line 1, we see the divide operator. The divide operator specifically designed to do mathematic division, which means it needs both of its operands to be numbers. If you give it something that's not a number, we kick in this thing that we're gonna talk about a lot more.
[00:03:28] This implicit coercion thing, it's gonna try to convert it to a number. And when you try to convert "a" to a number, because we're not telling it what base, it's gonna assume base ten. And "a" is not a valid character in base ten, so it's going to fail to convert it.
[00:03:45] Which means it's going to produce this special value. We have a term in computing science we call the sentinel value. A value that represents some special characteristic. We need to give it a special label, if you will, so that you know there's something special about it. In this case, the specialness is, it's invalid.
[00:04:08] So what are some other options that we could have used? If we were to try to go back and backseat drive on design and language, what could he have done here? If you try to convert something to a number and it didn't properly convert to a number, what could we have done?
[00:04:22] You could have resulted in false. But that would be super weird, that you tried to do a numeric operation and you ended up with a Boolean value. That doesn't make any sense. Null or undefined are similarly weird results from that type of operation because they are, in themselves, distinct different primitive types.
[00:04:43] Really, the only resulting type that a number operation should produce is a number. Would we all agree with that? I hope so. If you're gonna do a numeric operation, and you need a result, by well better be a number. Otherwise, what are we doing here? Why do we even programming?
[00:05:01] Okay? Was it just like programming by guessing? No, that would be terrible design. So what's the only other option if it needs to be a number but it failed to be a number? What's the only other option besides something like the special NaN? Well we could have thrown an exception.
[00:05:57] We're gonna see this design aesthetic over and over and over again. That there was an intentional decision at the beginning of the language to say, let's try not to just frustrate the crap out of people where there's just error after error after error and there's no hope of getting anything done.
[00:06:31] Try to be as forgiving as we possibly can while still being sane. And implicit coercion is a nod to that, as a matter of fact. It's a nod to saying instead of throwing an exception and just slapping somebody down and saying sorry, try again. We're saying let's figure out what's the best that we can do.
[00:06:46] Is there any kind of guess that we could make? You did something dumb, but could we try to recover it? Instead of garbage in, garbage out, it's like garbage in, recycled material out, right? Let's try to figure out the best that we can do here. So, we needed a value to represent that this number conversion failed.
[00:07:03] And NaN is that value. NaN is the value that says, number conversion failed. Okay? So it hasn't even happened that the divide has occurred on line 1. It's just the case that we tried to convert and that failed. So now we have a NaN divided by 2 and NaN with anything else is gonna produce NaN.
[00:07:22] NaN times NaN, NaN divided by, whatever you do, if NaN is part of it, NaN is gonna come out. It's just like the invalidating [COUGH] of all values. So of course a has the value NaN on line 3. And if we ask for the type of a, we get "number" because that's the only sensible result is that we did a numeric operation, we got a number back.
[00:07:44] But now we find the conflict with calling NaN not a number, because we say the type of not a number is number, and that makes no sense. The only proper response here, is to stop calling it not a number. That's not what it should be referred to. If I could go back and change it, I'd call it the invalid number, cuz that's what it is.
[00:08:05] I call it IN instead of NaN. I'd call it the invalid number, okay? That's what it really is. Now, NaN, again because of IEEE, this is all required by IEEE. NaN, [COUGH] there's actually a bunch of NaN values and the way you get at them differently, like the bit patterns form can be different depending on your scenario, whatever.
[00:08:28] So there's a requirement in IEEE that NaN is never equal to itself. Which sounds bizarre because it's literally the only value that I'm aware of in all of computing science, that does not have the identity property. It is not equal to itself. We're gonna actually make use of that identity property in just a moment, but NaN is never equal to itself.
[00:08:49] So if you wanna test for NaN, you're gonna need a special privileged utility. And they gave us isNaN. It's built into the global space. We get isNaN or window.isNaN or global.isNaN. Supposedly, it just tests to see whether or not the thing you pass in is the NaN value.
[00:09:04] Unfortunately, there's a fatal flaw in that original utility. A bug that we can't fix, cuz we already just discussed, as soon as we make mistakes. Let's write them in stone. We can't fix this bug. Because if I pass in isNaN and I pass in "foo". Now let me ask you a question, is "foo" a number?
[00:09:25] No. Is it not a number? You see the difference? See the problem with using not a number as our description? It is not the special, sentinel, invalid number type, and yet we're gonna get true back from isNaN because you know what isNaN does? If you pass something that's not already a number to isNaN, guess what it does?
[00:09:47] It tries to convert it to a number first. And then check to see if it resulted in the NaN. Here is a place where that conversion was a pretty terrible idea. We should never have tried to convert it. You should have just taken it and checked that and be done with it, right?
[00:10:03] But unfortunately they automatically convert first before checking so by the time it checks it, we've already converted "foo" to the NaN value and it says sure, that's a NaN. Fatal flaw. So this is a bug in isNaN. Now, thankfully, we finally decided to fix it by making a new isNaN in a different space, Number.isNaN.
[00:10:25] Number.isNaN does exactly what we want, which is it checks only the value, doesn't allow that coercion. This is what we call the polyfill pattern. You notice I've got an if statement that checks to make sure it's not already defined. And that code would only be true in older environments, so we're basically patching an older environment to have something that's been added in newer environments, like for example, ES6.
[00:10:46] And you notice what is NaN does here, it just says typeof num == "number". So at first, checks to make sure it's already of that number type and then it uses the isNaN. So really the true NaN is only gonna pass if both of those are the case.
[00:11:41] All right, so that is NaN and that's how we deal with the NaN. Again, we check to see if these values, not equal to itself is the only one without that identity property.