Difference Between Lodash _.isNaN and Number.isNaN

While both _.isNaN from Lodash and the native ES6 Number.isNaN solve the type-coercion issues of the legacy global isNaN function, their primary distinction lies in how they handle primitive values versus Number object instances. Both methods ensure that non-numeric types are not coerced into NaN, but _.isNaN is designed to recognize NaN even when it is wrapped in an instantiated Number object, whereas Number.isNaN strictly evaluates primitive numeric values.

The Problem with Global isNaN

To understand both implementations, consider the legacy global isNaN function. The global function performs implicit type coercion, converting the passed argument to a number before evaluation:

isNaN("hello"); // true, because Number("hello") is NaN
isNaN(undefined); // true, because Number(undefined) is NaN

This behavior leads to false positives for values that are not the actual numeric NaN value.

ECMAScript 2015: Number.isNaN

ES6 introduced Number.isNaN to address this issue with strict type checking. According to the ECMAScript specification, Number.isNaN(value) first checks if the type of the argument is strictly a primitive number. If it is not, it immediately returns false. Only if the value is both a primitive number and evaluates to NaN will it return true.

Number.isNaN(NaN); // true
Number.isNaN("hello"); // false (no coercion)
Number.isNaN(undefined); // false (no coercion)
Number.isNaN(new Number(NaN)); // false

Because new Number(NaN) returns an object rather than a primitive, Number.isNaN rejects it.

Lodash: _.isNaN

Lodash provides _.isNaN to identify NaN values safely across varied JavaScript runtime quirks and legacy patterns. Unlike Number.isNaN, Lodash checks whether the value is either a primitive number or a boxed Number object before validating whether the value represents NaN.

Internally, Lodash verifies that the value has an internal [[Class]] or Symbol.toStringTag of [object Number], combined with a check where the value does not equal itself:

_.isNaN(NaN); // true
_.isNaN("hello"); // false
_.isNaN(undefined); // false
_.isNaN(new Number(NaN)); // true

Key Differences Summary

For modern codebases where new Number() object wrappers are rarely, if ever, used, Number.isNaN is the standard native solution. _.isNaN remains useful in legacy projects or environments where boxed number instances must be accommodated.