JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
248: isNaN vs Number.isNaN
I want to show you a snippet of code that looks perfectly reasonable at first glance. Imagine you're building a simple price calculator where you take a value from a user input and check if it's valid before doing some math.
function processPayment(amount) {
if (isNaN(amount)) {
console.log("Invalid input: Please enter a numeric value.");
return;
}
console.log(`Processing payment of $${amount}...`);
}
processPayment(100); // "Processing payment of $100..."
processPayment("100"); // "Processing payment of $100..."
processPayment("hello"); // "Invalid input: Please enter a numeric value."
processPayment(undefined); // "Invalid input: Please enter a numeric value."
Everything seems to be working. You're catching the "bad" data. But here is where the bug creeps in. What happens if the amount is an empty string or null? In a real-world app, these often happen when a user clears a form field.
processPayment(""); // "Processing payment of $0..."
processPayment(null); // "Processing payment of $0..."
Wait, what? An empty string isn't a valid payment amount, but isNaN("") returns false. This is a classic JavaScript "gotcha" that has probably caused more headaches than I care to count.
The Trap of Global Coercion
The problem is that the global isNaN() function is a bit too "helpful." Before it checks if a value is NaN, it tries to convert that value into a number first. This is called coercion.
When you pass "" (an empty string) into isNaN(), JavaScript coerces it into a number. And in the weird world of JavaScript coercion, Number("") is 0. Since 0 is a valid number, isNaN(0) is false. The same thing happens with null, which also coerces to 0.
Essentially, the global isNaN() doesn't actually ask "Is this value NaN?" Instead, it asks "Can this value not be turned into a number?" That's a huge difference when you're trying to debug data types.
Strictness with Number.isNaN
To fix this, we use Number.isNaN(). This was introduced in ES6 specifically to solve the coercion mess. Unlike the global version, Number.isNaN() does not coerce its argument. It only returns true if the value is actually the value NaN and its type is Number.
Let's rewrite our validator to be more explicit. Usually, if we're worried about empty strings or nulls, we should check for those first, and use Number.isNaN() to catch actual mathematical failures (like 0 / 0).
function processPayment(amount) {
// First, we handle the types we know are invalid for payments
if (amount === "" || amount === null) {
console.log("Invalid input: Field cannot be empty.");
return;
}
// Then we check if the result of any coercion is actually NaN
if (Number.isNaN(Number(amount))) {
console.log("Invalid input: Please enter a numeric value.");
return;
}
console.log(`Processing payment of $${amount}...`);
}
processPayment(""); // "Invalid input: Field cannot be empty."
processPayment(null); // "Invalid input: Field cannot be empty."
processPayment("abc"); // "Invalid input: Please enter a numeric value."
By using Number(amount) explicitly and then passing that result into Number.isNaN(), we've taken control of the coercion process. We're no longer guessing what the global isNaN is doing behind the scenes. I always recommend Number.isNaN() over the global version because it's predictable. In software engineering, predictable is always better than "helpful."
📋 Practical Task
Build a Math-Error Guard
You are building a scientific calculator module. Some operations (like dividing zero by zero or calculating the square root of a negative number) result in NaN. You need to create a guard function that differentiates between a "Math Error" (actual NaN) and "Invalid Input" (something that isn't a number at all, like a string).
Requirements:
- Create a function called
categorizeResult(value). - If the value is exactly
NaN, return the string"Math Error". - If the value is not a number (e.g., a string, boolean, or undefined), return the string
"Invalid Input". - If the value is a valid number, return the string
"Success". - Use
Number.isNaN()to ensure you are correctly identifying the actualNaNvalue without accidental coercion.
Test your function with these cases:
categorizeResult(NaN); // Should return "Math Error"
categorizeResult(0 / 0); // Should return "Math Error"
categorizeResult("hello"); // Should return "Invalid Input"
categorizeResult(undefined); // Should return "Invalid Input"
categorizeResult(42); // Should return "Success"
There are no comments for now.