NaN和Number了。NaNNaN的全称是Not-A-Number,不是数字。在JavaScript中,整数和浮点数统称为Number类型。特点1.typeof是一个数字。说不是数字,而是typeof的值是数字,这是口是心非。ES6之后,Number也有了静态属性NaNtypeofNaN//numbertypeofNumber.NaN//number特性2我不等于我自己,我否定我自己,就是这样。说白了就是有+0和-0NaN==NaN//falseNumber.NaN==NaN//falseNaN===NaN//falseNumber.NaN===NaN//false+0==-0//trueObject.is(+0,-0)//fasleNaN的描述信息是一个值,在新的ES标准中是不可配置和可枚举的。也就是说不能删除、删除、改写,也不能改写配置。deleteNaN//falseNaN=1//1NaN==1//falsedeleteNumber.NaN//falseNumber.NaN=1//1Number.NaN==1//false我们尝试重写:使用Reflect.defineProperty代替Object.defineProperty,因为前者可以准确的告诉你是否成功,而后者返回定义的对象。constsuccess=Reflect.getOwnPropertyDescriptor(window,'NaN'),{writable:true,configurable:true,})console.log(success)//falseReflect.getOwnPropertyDescriptor(window,'NaN')//{value:NaN,writable:false,enumerable:false,configurable:false}结果就是改写不了,别打他的小脑袋。常见场景计算,类型转换是典型场景//直接数字初始化print(isNaN(Number("zz123")))//true//数字操作print(isNaN(0/0))//trueprint(isNaN(1*"zz123"))//trueprint(Math.sqrt(-1))//trueisNaNisNaN()是一个全局方法。其实质就是检查toNumber的返回值,如果是NaN则返回true,否则返回false。可以简化为语义:Number.isNaN=function(val){returnObject.is(Number(val),NaN);}toNumber方法,大致逻辑如下:le15:ToNumberConversionsArgumentTypeResultUndefinedReturnNaN。NullReturn+0𝔽.BooleanIfargumentistrue,return1𝔽.Ifargumentisfalse,return+0𝔽.NumberReturnargument(noconversion).StringReturn!StringToNumber(argument).SymbolThrowaTypeErrorexception.BigInt在对象转换时抛出TypeErrorexception1.让primValue是?ToPrimitive(参数,数字)。2.返回?ToNumber(原始值)。简单的翻译就是先获取原始类型的值,然后将其转换为Number。取原值也会根据条件执行不同的方法。调用Symbol.toPrimitive是最优先的。如果有条件决定是先调用valueOf还是toString对象,这里比较有意思。看下面的例子,valueOf的返回值可以直接影响到isNaN的值。letprint=console.log;varperson={age:10,name:"tom",valueOf(){returnthis.name}}print(isNaN(person))//trueletprint=console.log;varperson={age:10,name:"tom",valueOf(){returnthis.age}}print(isNaN(person))//false常规例子:letprint=console.log;print(isNaN("123"))//falseprint(isNaN('zz123'))//trueprint(isNaN(NaN))//trueisNaN可以删除,但不能枚举:deleteisNaN//truetypeof//undefineddisNaN=1//1isNaN==1//true属性描述信息:Number.isNaN判断一个值是否为数字,值是否等于NaN.ES标准的描述:IfType(number)isnotNumber,returnfalse.IfnumberisNaN,returntrue.Otherwise,returnfalse.都可以语义表示为:Number.isNaN=function(val){if(typeofval!=="number"){returnfalse}returnObject.is(val,NaN);}demo:letprint=console.log;print(Number.isNaN(NaN))//trueprint(Number.isNaN("123"))//falseisNaN和Number.isNaN的区别Number.isNaN是严格判断的,必须严格等于NaN。是南吗?值isNaN由内部toNumber转换结果决定。Number转换的返回值是否为NaNNumber.isNaN是ES6的语法,当然存在一定的兼容性问题。Object.isES6标准新方法用于判断两个值是否属于同一个值,可以准确判断NaN。letprint=console.log;print(Object.is(NaN,NaN));//trueprint(Object.is("123",NaN))//false严格判断NaN总结四种,2种ES6,2种ES5的。Number.isNaN(NaN)//trueNumber.isNaN(1)//falseObject.is(ES6)functionisNaNVal(val){returnObject.is(val,NaN);}isNaNVal(NaN)//trueisNaNVal(1)//false比较自己的最简单方法之一(ES5)。functionisNaNVal(val){returnval!==val;}isNaNVal(NaN)//trueiisNaNVal(1)//falsetypeof+NaN(ES5)这个是MDN推荐的shim,一些兼容低版本的库都是这样实现的,也是ES标准精确表达式函数isNaNVal(val){returntypeofval==='number'&&isNaN(val)}综合垫片if(!("isNaN"inNumber)){Number.isNaN=function(val){returntypeofval==='number'&&isNaN(val)}}深挖数组的indexOf和includes三信酱提到的include可以识别NaN的50个JS进阶知识点,让我们继续一探究竟。vararr=[NaN];arr.indexOf(NaN)//-1arr.includes(NaN)//trueincludes下面我们深入规范看看:ES标准的Array.prototype.includes比较相等的值,调用内部SameValueZero(x,y)方法,会检查value的第一个值是否为数字,如果是数字,则调用Number::sameValueZero(x,y),其具体比较步骤:1.IfxisNandyisNaN,返回true.2.Ifxis+0??andyis-0??,returntrue.3.Ifxis-0??andyis+0??,returntrue.4.IfxisthesameNumbervalueeasy,returntrue.5.Returnfalse.它先比较NaN,所以可以查NaN,这里补充一个信息,当比较+0和-0相等时,要区分+0和-0,就得用Object中的Array.prototype.indexOf值比较.isindexOfES标准调用IsStrictlyEqual(searchElement,elementK),如果检查第一个值是一个数字,调用Number::equal(x,y)。其比较逻辑1.IfxisNaN,返回false.2.IfyisNaN,返回false.3.IfxisthesameNumbervalueeasy,返回true.4.Ifxis+0𝔽andyis-0𝔽,返回true.5.Ifxis-0𝔽andyis+0𝔽,returntrue.6.Returnfalse.可以看出,如果有任何一个是NaN,则直接返回false,一定不能严格检查NaN.Number::sameValueZero和Number::sameValue的区别在上一章有讲过。Array.prototype.includes比较实用的值是Number::sameValueZero,突出了Zero。什么是零?是0,也就是说0被特殊对待。相应的,还有一个Number::sameValue方法。一起看一下:可以看出Number::sameValueZero不区分+0-0,而Number::sameValue区分。Object.is(+0,-0)//false,区分+0,-0[-0].includes(+0)//true,不区分+0,-0BigInt::sameValue和BigInt:samgeValueZero图片可以看出,除了Number,BigInt也有类似的对比。这两个方法的主要应用场景是Object.is和Array.prototype.includes。再猜下面的结果:Object.is(BigInt(+0),BigInt(-0))Object.is(-0n,0n)Object.is(-0,0)[BigInt(+0)].includes(BigInt(-0))321结果,不是如你所愿:Object.is(BigInt(+0),BigInt(-0))//trueObject.is(-0n,0n)//trueObject.is(-0,0)//false[BigInt(+0)].includes(BigInt(-0))//false哈哈,更详细BigInt::equal(x,y):核心解释:BigInt::sameValue(x,y)调用BigInt::equal(x,y)BigInt::equal(x,y)1.If?(x)=?(y),返回true;否则返回假。而R(x)是从Number或BigIntx到数学值的转换,表示为“x的数学值”或R(x)。+0F和-0F的数学值为0简单结论:Number区分+0,-0BitInt不区分BigInt::sameValue和BigInt:samgeValueZero有什么区别?用一张图解释更好:没有区别,更合理的解释是什么?总结indexOf是ES5甚至更早的产物,includes是ES6的产物。先进的产品向更合理化的方向发展是合理的。至于为什么不升级indexOf,历史包袱,之前的代码无法让它产生意想不到的效果。
