JavaScript In Depth - isFinite & IsNaN Functions
Tags: javascript, webdev, node
Published: 2021-11-01 08:36:30

> Javascript is not a language for the unwary - Kobby Owen 

Just before you stop reading, I know what you are thinking! "Who would read an article about these simple functions ?. These are basic functions every beginner of Javascript can quickly master, and easily learn to use. There is no need for an article about this!". While this is true, their behavior may be somewhat surprising, especially when dealing with non-number arguments. Learning more about their implementation will give you an in-depth knowledge of JavaScript and its core implementation. 

If you can answer the following questions, then you may stop reading this article. If not, I suggest you keep reading, since you will learn a lot from studying these functions and their implementation.

1. Why is `isNaN(new Date())` false and `isNaN(Date())` true 
2. Why is `isFinite(null)` true and `isFinite(undefined)` false 
3. Why is `isFinite(" ")` true and `isFinite("a")` false 
4. Why is `isNaN("Infinity")` true and `isNaN("infinity")` false 
5. Under what conditions do `isNaN` and `isFinite` throw a TypeError 
6. What is the value of `isNaN(" 23 ")`, `isNaN(23)`, `isNaN(" 23." )`, `isNaN("12 .")`


`isFinite` function determines if the passed argument is finite value. It checks if its argument is not `NaN`, or negative infinity or positive positive infinity. 

`isNaN` on the other hand determines whether the argument passed is a `NaN` or not. This function is necessary because of the nature of `NaN`. `NaN` is the only floating point value that does not compare to itself. This behavior is so true that, ECMAScript documentation suggests that one of the reliable ways to check for `NaN` is the expression`(x === x)`, which returns false only if `x` is a `NaN`. 
Mostly, to determine if a number is okay to be used in arithmetic operation without few surprises, you should find yourself using `isFinite` more than `isNaN`, since `isFinite` checks for `NaN` values and goes on to check for infinite values. In cases where infinite values are legitimately allowed to participate in operations, `isNaN` will be the best function to use.

## Implementation Details 
The first thing `isNaN` and `isFinite` function does is to try to convert its argument to a Number. This conversion is done using an ECMAScript internal function which is not exposed to the developer. It is this internal function that forms the core of these two functions, and is therefore worth studying. For the purpose of the article, lets call this function `ToNumber` function. This function is used a lot in ECMAScript behind the scenes. Understanding how it works, will give you a lot of understanding about the results of most operations in JavaScript. In an attempt to explain this internal function, we will use many helper functions and explain other internal methods used in ECMAScript that helps the `ToNumber` function to perform its conversion. I will use to top down approach to explain the implementation of this function.

### ToNumber Internal Function
`ToNumber` function takes a single argument, which is the argument to convert. To convert the argument, it takes the following step.
1. If the argument is undefined, it returns `NaN`
2. If the argument is null , it returns `0` 
3. If the argument is a number, it returns it 
4. If the argument is a BigInt , throw a `TypeError` 
5. If the argument is a Symbol, throw a `TypeError` 
6. If the argument is a String, call another internal method(`StringToNumber`)
7. If the argument is an Object, call another internal method(`ToPrimitive`) and pass its result through `ToNumber` function again.

NB. Step 7 involves 2 steps, it calls a helper function to convert the object to a primitive value, preferably a number, and call the `ToNumber` function recursively on its return value. The astute reader may reason at this point that this can cause an infinite recursion. That is not the case, because ECMAScript makes sure the return of `ToPrimitive` is not another object.

Now lets look at the two helper functions used by `ToNumber` to aid the conversion of its argument. 

### StringToNumber Internal Function
`StringToNumber` function simply parses its string argument and converts it to a number. One important thing to note about this function is the kind of input the parser accepts. The parser allows for optional white-space before and after the main string decimal characters. Any invalid character present in the argument, no matter where it is, will cause the parser to return `NaN`, and consequently the function too. Invalid characters include any character that is not part of the set [+ - E e .].  These valid non decimal characters are however allowed to appear only once. Making it appear twice will cause the function to return `NaN`. The function however recognizes the "Infinity" and returns the mathematical representation of it. An optional + or - is allowed before the decimal characters. They should however be the first non white-space character, if it exists in the sequence except it is being used before an E or e. An empty string, or a string full of white-space will cause the function to return the number 0. The following examples demonstrates the use of the function.

```javascript

function StringToNumber( argument ){
    /** implementation code **/
}

StringToNumber(" 23") // 23
StringToNumber(" 23 ") // 23
StringToNumber("+23.5") // 23.5 
StringToNumber("+ 23.5") // NaN ( space after the plus sign)
StringToNumber("-23.5") // -23.5 
StringToNumber("23.2.3") // NaN
StringToNumber("23ab") //NaN
StringToNumber("Infinity") // Infinity 
StringToNumber("-Infinity") // -Infinity
StringToNumber("+Infinity") // Infinity 
StringToNumber("ab") //NaN
StringToNumber("NaN") 
/**NaN ( not because the phrase NaN can be parsed , but because the characters N a N cannot be represented as a number) **/

StringToNumber("23E-14") //23E-14
StringToNumber("23E -14") //NaN ( space after E. )

```


### ToPrimitive Internal Function
The last function to examine before we can proceed is `ToPrimitive` method. This method takes an input and converts it to a primitive type, basically a number or a string. The function also takes an optional argument called hint. The `hint` argument can either be [default, number or string]. When the function is called, it first checks if the input is an object. If it is and it defines a `Symbol.toPrimitive` method, it is called on the object  while passing "number" as a hint to the function. If the method returns an object ( null not included ), a `TypeError` is thrown, otherwise its value is returned. If the object does not define its `Symbol.ToPrimitive`, it looks for two methods on the object, ie `toString` and `valueOf`. If the hint is a number, `valueOf` is called first, else `toString` is called first, and the other is called next. When the function to be called first is resolved, it is checked if it exists on the object or any of its bases, if it exists, and its return value when called is not an object, it returns it results. The second function, which is based on the value passed to the hint argument is called next. Its value is returned if is is not an object. If both methods returns an object, a `TypeError` is thrown by the function. 

If you did not understand these functions, here are their implementation in JavaScript( note JavaScript). In a real ECMAScript implementation, these functions are probably implemented in C/C++. 

```javascript

function StringToNumber( argument ){
    const res = argument.trim()
    // return 0 for empty string after stripping space characters
    if ( res.length === 0 ) return 0
    return Number(res)
}

function OrdinaryToPrimitive( input, hint){
    let methodNames = []
    if ( hint === "string" )
        methodNames = ["toString", "toValueOf"]
    else 
        methodNames = ["valueOf", "toString"]
    
    for ( const name of methodNames) {
        if ( typeof name === "function" ){
            const res = input[name]()
            if ( typeof res !== 'object' || res === null) 
                return res 
        }
    }
    throw TypeError
}

function ToPrimitive( input, hint){

    if ( typeof input === "object" ){
        if ( input[Symbol.toPrimitive] !== undefined ){
            if ( hint === undefined ) hint = 'default'
            const res = input[Symbol.toPrimitive]( hint )
            if ( typeof res !== 'object' || res === null) 
                return res 
            throw TypeError 
        }
        else{
            if ( hint === undefined ) hint = "number"
            return OrdinaryToPrimitive(input, hint)
        }
    }
    return input 
    
    

}

function ToNumber( argument ) {

    switch( typeof argument) {
        case 'undefined' : 
            return NaN
        case 'number' : 
            return argument 
        case 'bigint': case 'symbol': 
            throw TypeError 
        case 'string' : 
            return StringToNumber(argument)
        case 'object':{
            if (argument === null ) 
                return 0
            const hint = "number"
            const primitive = ToPrimitive(argument, hint)
            return ToNumber(primitive)  
        }
    }
}


```

There are a few things to note here. `ToPrimitive` delegates to another method called `OrdinaryToPrimitive` if the input provided does not defined `Symbol.toPrimitive` method.

## isNaN and isFinite

Now that we understand these internal functions. Let us go back to our functions. 
`isNaN` first converts its argument to a number using the `ToNumber` method and checks for `NaN`. If the result of that conversion is a `NaN`, true is return otherwise false is returned.
`isFinite` also first converts its argument to a number using the same `ToNumber` method. If then proceeds to check if the result of that conversion is not a `NaN` or `-Infinity` or `Infinity`. 
There is nothing interesting about these functions apart from the internal method that it calls to convert its argument before checking it. `ToNumber` internal methods are used by a lot of JavaScript functions including `parseInt` to convert its `radix` argument., All functions defined on the global Math object calls the function on its arguments before it starts processing the result, it is used by `Date.UTC` to convert it parameters into acceptable values and almost all the setter methods on the Date object ( example `setHours`, `setMonth`, `setYear`) and almost all methods and functions that operates with numbers. Understanding how this internal method works will save you from opening your jaws wide while you stare at the screen trying to understand the return values of some functions. Try to take a moment to go through this internal method one more time. Now let us answer the five questions at the beginning of the article, which you should be able to answer if you paid enough attention to it.

### Question 1
Why is `isNaN(new Date())` false and `isNaN(Date())` true 

### Answer 
The result of `new Date()` is an object. When that object is passed to `isNaN` as an argument, `ToPrimitive` is called to convert it to a primitive value, preferably a number. This ends up calling `valueOf` method on the object and returning its results, which is a number. This number is then checked for `NaN` , which is ultimately false. The result of `Date()` on the other hand is a string that represents the current time. This string is passed to `StringToNumber` internal method by `ToNumber`. The result is a string that cannot be parsed into a number, thus returning `NaN`. `isNaN` proceeds to check the result of this conversion and finds that its `NaN` and ultimately return true  

### Question 2
Why is `isFinite(null)` true and `isFinite(undefined)` false

### Answer 
`ToNumber` converts null to 0 and undefined to `NaN`, thus the return values of `isFinite` when called with these two values

### Question 3
Why is `isFinite(" ")` true and `isFinite("a")` false

### Answer 
Both arguments are strings, so `ToNumber` calls `StringToNumber` internal method on them. Empty strings after trimming white spaces causes the method to return 0. Thus the first `isFinite` call is the result of checking if 0 is a finite number, which it is. "a" on the other hand returns `NaN` when converted. 

### Question 4
Why is `isNaN("Infinity")` true and `isNaN("infinity")` false

### Answer 
`StringToNumber` recognizes the string "Infinity" , "-Infinity", "-Infinity". It rightly returns Infinity and the result is checked whether its `NaN`, which ends up being false. `Infinity` is not `NaN`. 
"infinity" on the other hand is not recognized, neither can it be parsed as a number. It returns `NaN` as a result of the conversion.

### Question 5.
Under what conditions do `isNaN` and `isFinite` throw a `TypeError` 

### Answer 
If their argument in either a `BigInt`, Symbol or they defined `toString` and `valueOf` which both returns an object instead of a primitive value like a string or a number 


### Question 6.
What is the value of `isNaN(" 23 ")`, `isNaN(23)`, `isNaN(" 23." )`, `isNaN("12 .")`

### Answer 
`isNaN(" 23 ")` is false
`isNaN("23.")` is false 
`isNaN("12 .")` is true