Special Values

There are several special values spread across the various types that the alert JS developer needs to be aware of, and use properly.

The Non-value Values

For the undefined type, there is one and only one value: undefined. For the null type, there is one and only one value: null. So for both of them, the label is both its type and its value.

Both undefined and null are often taken to be interchangeable as either “empty” values or “non” values. Other developers prefer to distinguish between them with nuance. For example:

  • null is an empty value
  • undefined is a missing value

Or:

  • undefined hasn’t had a value yet
  • null had a value and doesn’t anymore

Regardless of how you choose to “define” and use these two values, null is a special keyword, not an identifier, and thus you cannot treat it as a variable to assign to (why would you!?). However, undefined is (unfortunately) an identifier. Uh oh.

Undefined

In non-strict mode, it’s actually possible (though incredibly ill-advised!) to assign a value to the globally provided undefined identifier:

  1. function foo() {
  2. undefined = 2; // really bad idea!
  3. }
  4. foo();
  1. function foo() {
  2. "use strict";
  3. undefined = 2; // TypeError!
  4. }
  5. foo();

In both non-strict mode and strict mode, however, you can create a local variable of the name undefined. But again, this is a terrible idea!

  1. function foo() {
  2. "use strict";
  3. var undefined = 2;
  4. console.log( undefined ); // 2
  5. }
  6. foo();

Friends don’t let friends override undefined. Ever.

void Operator

While undefined is a built-in identifier that holds (unless modified — see above!) the built-in undefined value, another way to get this value is the void operator.

The expression void ___ “voids” out any value, so that the result of the expression is always the undefined value. It doesn’t modify the existing value; it just ensures that no value comes back from the operator expression.

  1. var a = 42;
  2. console.log( void a, a ); // undefined 42

By convention (mostly from C-language programming), to represent the undefined value stand-alone by using void, you’d use void 0 (though clearly even void true or any other void expression does the same thing). There’s no practical difference between void 0, void 1, and undefined.

But the void operator can be useful in a few other circumstances, if you need to ensure that an expression has no result value (even if it has side effects).

For example:

  1. function doSomething() {
  2. // note: `APP.ready` is provided by our application
  3. if (!APP.ready) {
  4. // try again later
  5. return void setTimeout( doSomething, 100 );
  6. }
  7. var result;
  8. // do some other stuff
  9. return result;
  10. }
  11. // were we able to do it right away?
  12. if (doSomething()) {
  13. // handle next tasks right away
  14. }

Here, the setTimeout(..) function returns a numeric value (the unique identifier of the timer interval, if you wanted to cancel it), but we want to void that out so that the return value of our function doesn’t give a false-positive with the if statement.

Many devs prefer to just do these actions separately, which works the same but doesn’t use the void operator:

  1. if (!APP.ready) {
  2. // try again later
  3. setTimeout( doSomething, 100 );
  4. return;
  5. }

In general, if there’s ever a place where a value exists (from some expression) and you’d find it useful for the value to be undefined instead, use the void operator. That probably won’t be terribly common in your programs, but in the rare cases you do need it, it can be quite helpful.

Special Numbers

The number type includes several special values. We’ll take a look at each in detail.

The Not Number, Number

Any mathematic operation you perform without both operands being numbers (or values that can be interpreted as regular numbers in base 10 or base 16) will result in the operation failing to produce a valid number, in which case you will get the NaN value.

NaN literally stands for “not a number“, though this label/description is very poor and misleading, as we’ll see shortly. It would be much more accurate to think of NaN as being “invalid number,” “failed number,” or even “bad number,” than to think of it as “not a number.”

For example:

  1. var a = 2 / "foo"; // NaN
  2. typeof a === "number"; // true

In other words: “the type of not-a-number is ‘number’!” Hooray for confusing names and semantics.

NaN is a kind of “sentinel value” (an otherwise normal value that’s assigned a special meaning) that represents a special kind of error condition within the number set. The error condition is, in essence: “I tried to perform a mathematic operation but failed, so here’s the failed number result instead.”

So, if you have a value in some variable and want to test to see if it’s this special failed-number NaN, you might think you could directly compare to NaN itself, as you can with any other value, like null or undefined. Nope.

  1. var a = 2 / "foo";
  2. a == NaN; // false
  3. a === NaN; // false

NaN is a very special value in that it’s never equal to another NaN value (i.e., it’s never equal to itself). It’s the only value, in fact, that is not reflexive (without the Identity characteristic x === x). So, NaN !== NaN. A bit strange, huh?

So how do we test for it, if we can’t compare to NaN (since that comparison would always fail)?

  1. var a = 2 / "foo";
  2. isNaN( a ); // true

Easy enough, right? We use the built-in global utility called isNaN(..) and it tells us if the value is NaN or not. Problem solved!

Not so fast.

The isNaN(..) utility has a fatal flaw. It appears it tried to take the meaning of NaN (“Not a Number”) too literally — that its job is basically: “test if the thing passed in is either not a number or is a number.” But that’s not quite accurate.

  1. var a = 2 / "foo";
  2. var b = "foo";
  3. a; // NaN
  4. b; // "foo"
  5. window.isNaN( a ); // true
  6. window.isNaN( b ); // true -- ouch!

Clearly, "foo" is literally not a number, but it’s definitely not the NaN value either! This bug has been in JS since the very beginning (over 19 years of ouch).

As of ES6, finally a replacement utility has been provided: Number.isNaN(..). A simple polyfill for it so that you can safely check NaN values now even in pre-ES6 browsers is:

  1. if (!Number.isNaN) {
  2. Number.isNaN = function(n) {
  3. return (
  4. typeof n === "number" &&
  5. window.isNaN( n )
  6. );
  7. };
  8. }
  9. var a = 2 / "foo";
  10. var b = "foo";
  11. Number.isNaN( a ); // true
  12. Number.isNaN( b ); // false -- phew!

Actually, we can implement a Number.isNaN(..) polyfill even easier, by taking advantage of that peculiar fact that NaN isn’t equal to itself. NaN is the only value in the whole language where that’s true; every other value is always equal to itself.

So:

  1. if (!Number.isNaN) {
  2. Number.isNaN = function(n) {
  3. return n !== n;
  4. };
  5. }

Weird, huh? But it works!

NaNs are probably a reality in a lot of real-world JS programs, either on purpose or by accident. It’s a really good idea to use a reliable test, like Number.isNaN(..) as provided (or polyfilled), to recognize them properly.

If you’re currently using just isNaN(..) in a program, the sad reality is your program has a bug, even if you haven’t been bitten by it yet!

Infinities

Developers from traditional compiled languages like C are probably used to seeing either a compiler error or runtime exception, like “Divide by zero,” for an operation like:

  1. var a = 1 / 0;

However, in JS, this operation is well-defined and results in the value Infinity (aka Number.POSITIVE_INFINITY). Unsurprisingly:

  1. var a = 1 / 0; // Infinity
  2. var b = -1 / 0; // -Infinity

As you can see, -Infinity (aka Number.NEGATIVE_INFINITY) results from a divide-by-zero where either (but not both!) of the divide operands is negative.

JS uses finite numeric representations (IEEE 754 floating-point, which we covered earlier), so contrary to pure mathematics, it seems it is possible to overflow even with an operation like addition or subtraction, in which case you’d get Infinity or -Infinity.

For example:

  1. var a = Number.MAX_VALUE; // 1.7976931348623157e+308
  2. a + a; // Infinity
  3. a + Math.pow( 2, 970 ); // Infinity
  4. a + Math.pow( 2, 969 ); // 1.7976931348623157e+308

According to the specification, if an operation like addition results in a value that’s too big to represent, the IEEE 754 “round-to-nearest” mode specifies what the result should be. So, in a crude sense, Number.MAX_VALUE + Math.pow( 2, 969 ) is closer to Number.MAX_VALUE than to Infinity, so it “rounds down,” whereas Number.MAX_VALUE + Math.pow( 2, 970 ) is closer to Infinity so it “rounds up”.

If you think too much about that, it’s going to make your head hurt. So don’t. Seriously, stop!

Once you overflow to either one of the infinities, however, there’s no going back. In other words, in an almost poetic sense, you can go from finite to infinite but not from infinite back to finite.

It’s almost philosophical to ask: “What is infinity divided by infinity”. Our naive brains would likely say “1” or maybe “infinity.” Turns out neither is true. Both mathematically and in JavaScript, Infinity / Infinity is not a defined operation. In JS, this results in NaN.

But what about any positive finite number divided by Infinity? That’s easy! 0. And what about a negative finite number divided by Infinity? Keep reading!

Zeros

While it may confuse the mathematics-minded reader, JavaScript has both a normal zero 0 (otherwise known as a positive zero +0) and a negative zero -0. Before we explain why the -0 exists, we should examine how JS handles it, because it can be quite confusing.

Besides being specified literally as -0, negative zero also results from certain mathematic operations. For example:

  1. var a = 0 / -3; // -0
  2. var b = 0 * -3; // -0

Addition and subtraction cannot result in a negative zero.

A negative zero when examined in the developer console will usually reveal -0, though that was not the common case until fairly recently, so some older browsers you encounter may still report it as 0.

However, if you try to stringify a negative zero value, it will always be reported as "0", according to the spec.

  1. var a = 0 / -3;
  2. // (some browser) consoles at least get it right
  3. a; // -0
  4. // but the spec insists on lying to you!
  5. a.toString(); // "0"
  6. a + ""; // "0"
  7. String( a ); // "0"
  8. // strangely, even JSON gets in on the deception
  9. JSON.stringify( a ); // "0"

Interestingly, the reverse operations (going from string to number) don’t lie:

  1. +"-0"; // -0
  2. Number( "-0" ); // -0
  3. JSON.parse( "-0" ); // -0

Warning: The JSON.stringify( -0 ) behavior of "0" is particularly strange when you observe that it’s inconsistent with the reverse: JSON.parse( "-0" ) reports -0 as you’d correctly expect.

In addition to stringification of negative zero being deceptive to hide its true value, the comparison operators are also (intentionally) configured to lie.

  1. var a = 0;
  2. var b = 0 / -3;
  3. a == b; // true
  4. -0 == 0; // true
  5. a === b; // true
  6. -0 === 0; // true
  7. 0 > -0; // false
  8. a > b; // false

Clearly, if you want to distinguish a -0 from a 0 in your code, you can’t just rely on what the developer console outputs, so you’re going to have to be a bit more clever:

  1. function isNegZero(n) {
  2. n = Number( n );
  3. return (n === 0) && (1 / n === -Infinity);
  4. }
  5. isNegZero( -0 ); // true
  6. isNegZero( 0 / -3 ); // true
  7. isNegZero( 0 ); // false

Now, why do we need a negative zero, besides academic trivia?

There are certain applications where developers use the magnitude of a value to represent one piece of information (like speed of movement per animation frame) and the sign of that number to represent another piece of information (like the direction of that movement).

In those applications, as one example, if a variable arrives at zero and it loses its sign, then you would lose the information of what direction it was moving in before it arrived at zero. Preserving the sign of the zero prevents potentially unwanted information loss.

Special Equality

As we saw above, the NaN value and the -0 value have special behavior when it comes to equality comparison. NaN is never equal to itself, so you have to use ES6’s Number.isNaN(..) (or a polyfill). Similarly, -0 lies and pretends that it’s equal (even === strict equal — see Chapter 4) to regular positive 0, so you have to use the somewhat hackish isNegZero(..) utility we suggested above.

As of ES6, there’s a new utility that can be used to test two values for absolute equality, without any of these exceptions. It’s called Object.is(..):

  1. var a = 2 / "foo";
  2. var b = -3 * 0;
  3. Object.is( a, NaN ); // true
  4. Object.is( b, -0 ); // true
  5. Object.is( b, 0 ); // false

There’s a pretty simple polyfill for Object.is(..) for pre-ES6 environments:

  1. if (!Object.is) {
  2. Object.is = function(v1, v2) {
  3. // test for `-0`
  4. if (v1 === 0 && v2 === 0) {
  5. return 1 / v1 === 1 / v2;
  6. }
  7. // test for `NaN`
  8. if (v1 !== v1) {
  9. return v2 !== v2;
  10. }
  11. // everything else
  12. return v1 === v2;
  13. };
  14. }

Object.is(..) probably shouldn’t be used in cases where == or === are known to be safe (see Chapter 4 “Coercion”), as the operators are likely much more efficient and certainly are more idiomatic/common. Object.is(..) is mostly for these special cases of equality.