关于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)。
其他的不重要,去阮一峰老师博客看一下就明白了,有两点 可以探讨一下
Object.assign方法的目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。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 是否会依次将它们转成对象,逐一拷贝。虽然不知道转换成的对象的键是什么对象,但理论上这些转换后的对象都有相同的特点
- 只有一个属性,这个属性的值是我们传的参数,如上面代码的神秘属性的值是 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
再见 ✋!