JS中的NaN

322 阅读5分钟

关于NaN的一些整理

原文地址:www.kilig.xyz/#/home/blog…

NaN(Not-A-Number)

  • 全局属性的NaN的值表示不是一个数字
  • Number属性的NaN的值表示不是一个数字

看一段代码

typeof(Number.NaN)
//"number"
typeof(window.NaN)
//"number"

是的,它表示它的值不是一个数字,但它是number类型

插个题外话,今天在看  Object.assign 这个方法的时候,发现了一个更 离谱的东西  难理解的东西

题外话部分

Object.assign  方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

其他的不重要,去阮一峰老师博客看一下就明白了,有两点 可以探讨一下

  1. Object.assign 方法的目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
  2. Object.assign 方法的参数不是对象,则会先转成对象,然后返回。

从第二点入手

代码测试一下

var s = Object.assign(2)
console.log(s)
//  Number {2}

这里产生了一个困惑,既然返回的是一个对象,为什么不是对象的形式(属性键值对)

同事提醒是不是像有些语言一样,它的键是个特殊类型,空字符。

既然它是对象,那就好办了,直接通过 Object.keys()拿你的键就行了,我记得之前还总结过这个方法

基本类型 调用Object.keys(),返回空数组,遇到 value值为 undefined 和 null 时会报错

var s = Object.assign(2)
console.log(s)
//  Number {2}
Object.keys(s)
// []

困惑。。

它是个没有键的对象?

可能我对这个Object.keys()方法认识不够全面?还是从侧面验证一下

这下就可以用到上面的第一点了,Object.assign 方法的目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

当我有多个源对象,且均不是对象,Object.assign 是否会依次将它们转成对象,逐一拷贝。虽然不知道转换成的对象的键是什么对象,但理论上这些转换后的对象都有相同的特点

  1. 只有一个属性,这个属性的值是我们传的参数,如上面代码的神秘属性的值是 2 
  2. 键无法通过 Object.keys 获取

所以,Object.assign  返回的对象的神秘键的值是最后一个参数的值(后面的属性覆盖前面的值)?

上代码

var p = Object(2,3,4)
console.log(p)
//Number {2}

。。。

 

a moment later...

好像这个 对象前面有个东西    Number

我想想,要定义一个 变量 a 的值是基本类型数字 2,有几种方法?

 

//第一种
var a = 2
//第二种
var a = new Number(2).valueOf()
//第三种
var a = Number(2)

所以Number对象特殊一点?没有键?回头找找Number实现的c++源码吧

另外,String对象和Number对象实现又有不同,和数组有点类似,直接代码表示一下就明白了


var arr = new String('a')      //不管传几个参数,最终字符串的值是第一个参数
Object.keys(arr)
//['0']

可能底层实现方式差异还挺大的,想了解具体原因还是得去源码找答案,这个后面再探讨,还是回到正题

正文部分

什么是NaN

这部分前面已经说过了,总而言之它表示一个值不是数字

NaN怎么产生

类型转换,转换为Number类型时,无法转换成Number会返回NaN

  • Number()方法

Number() 函数把对象的值转换为数字。

    • 如果是Boolean值,true和false值将分别被转换为1和0。
    • 如果是数字值,只是简单的传入和返回。
    • 如果是null值,返回0。
    • 如果是undefined,返回NaN。
    • 如果是字符串:

  a.  如果字符串中只包含数字时,将其转换为十进制数值,忽略前导0

  b. 如果字符串中包含有效浮点格式,如“1.1”,将其转换为对应的浮点数字,忽略前导0

  c. 如果字符串中包含有效的十六进制格式,如“0xf”,将其转换为相同大小的十进制数值

  d. 如果字符串为空,将其转换为0

  e. 如果字符串中包含除上述格式之外的字符,则将其转换为NaN

 

    • 如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依照前面的规则转换返回的字符串值。
  • parseInt()
    • parseInt()函数在转换字符串时,会忽略字符串前面的空格,知道找到第一个非空格字符。
    • 如果第一个字符不是数字或者负号,parseInt() 就会返回NaN,同样的,用parseInt() 转换空字符串也会返回NaN。
    • 如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符。
    • parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。

基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的值,当然,对二进制、八进制,甚至十进制(默认模式),都可以这样调用parseInt()方法。

  • 其他操作

判断是否为NaN

  • window.isNaN()

通过 Number() 方法尝试将参数转换成Number类型,如果成功返回false,如果失败返回true。

所以全局属性的 isNaN 只是判断传入的参数是否能转换成数字,并不是严格的判断是否等于NaN。

isNaN('2')
// false
isNaN('hhh')
//true
  • Number.isNaN()

判断传入的参数是否严格的等于NaN(也就是 ===)。

 

Number.isNaN('2')
//false
Number.isNaN('hhh')
//false
Number.isNaN(NaN)
//true

 

全局属性,用来判断Number() 方法返回值的类型。而Number的属性,则用来判断值是否为NaN

 

  • " == " 和 “===” 无法判断是否为 NaN
NaN === NaN
//false
NaN == NaN
//false

 

  • ES6  Object.is()

Object.is 用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。除了以下两点

    • NaN
    • +0 和 -0
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

再见 ✋!