从 [] 到 {}:深入探究 JavaScript 的 isNaN 奇迹

49 阅读3分钟

有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。比如,用 0 除任意数值在其他语言中通常都会导致错误,从而中止代码执行。但在 ECMAScript 中,0、+0 或0 相除会返回 NaN:摘自红宝石35p

9NbAz.jpg

console.log(0 / -0); // 0
console.log(0 / +0); // 0

isNaN全局方法:用于判断传入的值是否为NaN,此方法会通过Number()进行自动类型转换,转换规则如下:(摘自红宝石36p)

Number()函数基于如下规则执行转换。

  • 布尔值,true 转换为 1,false 转换为 0。
  • 数值,直接返回。
  • null,返回 0。
  • undefined,返回 NaN。
  • 字符串,应用以下规则。
  • 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。 因此,Number("1")返回 1,Number("123")返回 123,Number("011")返回 11(忽略前面的零)。
  • 如果字符串包含有效的浮点值格式如"1.1",则会转换为相应的浮点值(同样,忽略前面的零)。 如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整 数值。
  • 如果是空字符串(不包含字符),则返回 0。
  • 如果字符串包含除上述情况之外的其他字符,则返回 NaN。
  • 对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用 toString()方法,再按照转换字符串的规则转换。
let a = NaN;
let b = "1";
let c = 1;
let arr = [];
console.log("------>", arr.valueOf().toString() === ""); // true
let obj = {};
console.log(isNaN(a)); // true
console.log(isNaN(b)); // false
console.log(isNaN(c)); // false
console.log(isNaN(null)); // false
console.log(isNaN(arr)); // false
console.log(isNaN(obj)); // true

根据以上运行结果可知:对于对象而言经过isNaN函数判断返回一定是true。除非我们在对象中初始化一个valueOf()或者toString()并返回一个字符串,并且字符串可以被Number转换为数字

let obj1 = {
  // valueOf: function () {
  //   return 12;
  // },
  toString: function () {
    return "123";
  },
};
console.log(isNaN(obj1)); //false

注意:当传入的为一个空数组的时候toString()方法会返回一个空字符串,根据Number()转换规则可知空字符串会被转为0。所以当使用isNaN进行判断空数组时会返回false

提到空数组可能就会想到空集合和空字典,他们都是以数组的形式定义的但是他们是伪数组,让我们来看看他们的valueOf()返回值

let set = new Set();
let map = new Map();
console.log(set.valueOf()); // Set(0) {size: 0}
console.log(map.valueOf()); // Map(0) {size: 0}
console.log(isNaN(set)); // true
console.log(isNaN(map)); // true

可以看到他们的valueOf()返回值为对象形式,所以使用isNaN()进行判断返回true就不奇怪了。

Number.isNaN()方法

在es6之后我们可以通过Number.isNaN静态方法进行判断传入的参数是否为NaN,因为此方法不会进行类型转换,也就是说只有传入的参数值为NaN的时候才会返回true,传入其他值都会返回false。

console.log(Number.isNaN("hello")); // false
console.log(Number.isNaN(1)); // false
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN(null)); // false
console.log(Number.isNaN({})); // false
console.log(Number.isNaN([])); // false

当然如果希望在不兼容es6的浏览器使用此Number.isNaN静态方法我们以为在Number对象上实现此方法:

if (!obj2.isNaN) {
  obj2.isNaN = function (value) {
    return typeof value === "number" && isNaN(value);
  };
}
console.log(obj2.isNaN(1)); // false
console.log(obj2.isNaN("hello")); // false
console.log(obj2.isNaN(true)); // false
console.log(obj2.isNaN([])); // false
console.log(obj2.isNaN({})); // false
console.log(obj2.isNaN(NaN)); // true

ok, 2024-1-1 希望大家每天开心!!!

OjjB.gif