在javascript中,null>=0 为真,null==0却为假,null的值详解
在javascript中,null>=0为真,null==0却为假,null的值详解
1.前言
今天看见朋友们在讨论一个问题,说null到底和0是不是相等的。
听到这里,自己赶紧去写个Demo试一下。
<!DOCTYPEhtml> <htmllang="en"> <head> <metacharset="UTF-8"> <title>MR_LP:3206064928</title> </head> <body> </body> <scripttype="text/javascript"> console.log(null>0);//false console.log(null<0);//false console.log(null>=0);//true console.log(null<=0);//true console.log(null==0);//false console.log(null===0);//false </script> </html>
什么情况?
为什么console.log(null<=0);和console.log(null>=0);这两条的判断是true呢?
2.查阅资料
如果想明确,这个问题具体是怎么回事,那么我们需要重新来回顾一下我们的ECMAScriptLanguageSpecification(HTMLversion),翻译过来就是ECMAScript语言规范(HTML版本)。
2.1内部相等性运算算法
首先我们来看一下ES3关于内部相等性运算的算法实现。
11.9.3TheAbstractEqualityComparisonAlgorithm Thecomparisonx==y,wherexandyarevalues,producestrueorfalse.Suchacomparisonisperformedasfollows: 1.IfType(x)isdifferentfromType(y),Gotostep14. 2.IfType(x)isUndefined,returntrue. 3.IfType(x)isNull,returntrue. 4.IfType(x)isnotNumber,gotostep11. 5.IfxisNaN,returnfalse. 6.IfyisNaN,returnfalse. 7.Ifxisthesamenumbervalueasy,returntrue. 8.Ifxis+0andyis-0,returntrue. 9.Ifxis-0andyis+0,returntrue. 10.Returnfalse. 11.IfType(x)isString,thenreturntrueifxandyareexactlythesamesequenceofcharacters(samelengthandsamecharactersincorrespondingpositions).Otherwise,returnfalse. 12.IfType(x)isBoolean,returntrueifxandyarebothtrueorbothfalse.Otherwise,returnfalse. 13.Returntrueifxandyrefertothesameobjectoriftheyrefertoobjectsjoinedtoeachother(see13.1.2).Otherwise,returnfalse. 14.Ifxisnullandyisundefined,returntrue. 15.Ifxisundefinedandyisnull,returntrue. 16.IfType(x)isNumberandType(y)isString,returntheresultofthecomparisonx==ToNumber(y). 17.IfType(x)isStringandType(y)isNumber,returntheresultofthecomparisonToNumber(x)==y. 18.IfType(x)isBoolean,returntheresultofthecomparisonToNumber(x)==y. 19.IfType(y)isBoolean,returntheresultofthecomparisonx==ToNumber(y). 20.IfType(x)iseitherStringorNumberandType(y)isObject,returntheresultofthecomparisonx==ToPrimitive(y). 21.IfType(x)isObjectandType(y)iseitherStringorNumber,returntheresultofthecomparisonToPrimitive(x)==y. 22.Returnfalse.
2.2内部关系运算算法
接下来我们再来看一下ES3关于内部关系运算的算法实现。
11.8.5TheAbstractRelationalComparisonAlgorithm Thecomparisonx<y,wherexandyarevalues,producestrue,false,orundefined(whichindicatesthatatleastoneoperandisNaN).Suchacomparisonisperformedasfollows: 1.CallToPrimitive(x,hintNumber). 2.CallToPrimitive(y,hintNumber). 3.IfType(Result(1))isStringandType(Result(2))isString,gotostep16.(Notethatthisstepdiffersfromstep7inthealgorithmfortheadditionoperator**+ *inusing*andinsteadofor.) 4.CallToNumber(Result(1)). 5.CallToNumber(Result(2)). 6.IfResult(4)isNaN,returnundefined. 7.IfResult(5)isNaN,returnundefined. 8.IfResult(4)andResult(5)arethesamenumbervalue,returnfalse. 9.IfResult(4)is+0andResult(5)is-0,returnfalse. 10.IfResult(4)is-0andResult(5)is+0,returnfalse. 11.IfResult(4)is+∞,returnfalse. 12.IfResult(5)is+∞,returntrue. 13.IfResult(5)is-∞,returnfalse. 14.IfResult(4)is-∞,returntrue. 15.IfthemathematicalvalueofResult(4)islessthanthemathematicalvalueofResult(5)—notethatthesemathematicalvaluesarebothfiniteandnotbothzero—returntrue.Otherwise,returnfalse. 16.IfResult(2)isaprefixofResult(1),returnfalse.(Astringvaluepisaprefixofstringvalueqifqcanbetheresultofconcatenatingpandsomeotherstring*r*.Notethatanystringisaprefixofitself,becausermaybetheemptystring.) 17.IfResult(1)isaprefixofResult(2),returntrue. 18.LetkbethesmallestnonnegativeintegersuchthatthecharacteratpositionkwithinResult(1)isdifferentfromthecharacteratpositionkwithinResult(2).(Theremustbesuchak,forneitherstringisaprefixoftheother.) 19.LetmbetheintegerthatisthecodepointvalueforthecharacteratpositionkwithinResult(1). 20.LetnbetheintegerthatisthecodepointvalueforthecharacteratpositionkwithinResult(2). 21.Ifm<n,returntrue.Otherwise,returnfalse.
2.3ES3的运算符
2.3.1ES3的“>”运算符:
TheGreater-thanOperator(>) TheproductionRelationalExpression: RelationalExpression>ShiftExpressionisevaluatedasfollows: 1.EvaluateRelationalExpression. 2.CallGetValue(Result(1)). 3.EvaluateShiftExpression. 4.CallGetValue(Result(3)). 5.PerformthecomparisonResult(4)<Result(2). 6.IfResult(5)isundefined,returnfalse.Otherwise,returnResult(5).
2.3.2ES3的”>=”运算符:
TheGreater-than-or-equalOperator(>=) TheproductionRelationalExpression: RelationalExpression>=ShiftExpressionisevaluatedasfollows: 1.EvaluateRelationalExpression. 2.CallGetValue(Result(1)). 3.EvaluateShiftExpression. 4.CallGetValue(Result(3)). 5.PerformthecomparisonResult(2)<Result(4).(see11.8.5). 6.IfResult(5)istrueorundefined,returnfalse.Otherwise,returntrue.
2.3.3ES3的“==”运算符:
TheEqualsOperator(==) TheproductionEqualityExpression: EqualityExpression==RelationalExpressionisevaluatedas follows: 1.EvaluateEqualityExpression. 2.CallGetValue(Result(1)). 3.EvaluateRelationalExpression. 4.CallGetValue(Result(3)). 5.PerformthecomparisonResult(4)==Result(2).(see11.9.3). 6.ReturnResult(5).
3.根据资料得出的内容
着重看一下,上面特意加粗的地方,我们可以明确下面三件事。
- 关系运算符和相等运算符并不是一个类别的.
- 关系运算符,在设计上,总是需要运算元尝试转为一个number.而相等运算符在设计上,则没有这方面的考虑.
- 最重要的一点,不要把拿a>b,a==b的结果想当然的去和a>=b建立联系.正确的符合最初设计思想的关系是a>b与a>=b是一组.a==b和其他相等运算符才是一组.比如a===b,a!=b,a!==b.
那么我们就可以反过来看这个问题了。
null>0//null尝试转型为number,则为0.所以结果为false, null>=0//null尝试转为number,则为0,结果为true. null==0//null在设计上,在此处不尝试转型.所以结果为false.
这里引用一下Franky大大的话。
a>=b运算符只是简单的去对a<b的结果取反.我以为这是一个设计上的失误的另一个理由是undefined,在标准中,被单拎出来.细心的你,也一定发现了这一点.对于undefined的设计, undefined>0 ,undefined<0,undefined==0的结果是符合设计上,逻辑的一致性的.而null是被遗漏的东西.直到今天早上.我重新翻阅了ES3,5.相关章节.才恍然大悟自己没有从根本上理解到这个问题.
虽然前面的例子,我catch到了BE当初的设计思想.但是从全局的角度来看.从关系运算符到相等运算符,尤其是相等运算符的设计上.真的十分混乱不堪.BE在信中提到,他对==的现状也很无奈.甚至用愚蠢这个词来形容自己当初的实现(当然他还提到,当初只是为了在10天内设计出js,并跑过qa的测试用例).即使如此,但是他仍然表示null==0这个结果是他想要的.
好吧,到了这里,我也有种无力感.我认为纵观JavaScript,对关系运算和相等运算的设计.除了混乱,我想不出还有什么词来形容它们更恰当.这一点从,我们生产环境代码中,大量的类型检查,和防御性代码的的存在,就可以证明这一点.
同时Franky大大还举了另外一个例子。
functioncase1(a){ if(a==null){ .... } } functioncase2(a){ if(a==undefined){ ... } } //上面两组完全等价,这就是一种不明确表述. //我们永远不知道代码编写者的目的到底是同时匹配null和undefined还是只匹配其中某一个 functioncase3(a){ if(a===null||a===undefined){ ... } } //case3才是最好的表述.我们明确知道代码编写者的意图. //即使很多人可能认为这个代码很愚蠢.但我坚定的认为这才是好代码.
最后,不得不提到,我发出null>=0这封信后,AndreaGiammarchi表示了对我之前看法的支持,他同我最初的看法一样,认为null>=0的结果应该为false.并建议在ES7中的严格模式中,修改这个结果.虽然同样遭到DavidBruant的反对. 好吧为他和我的这个错误看法,默哀一分钟…
4.后记
所以写代码,写规范,都应该明确表述.即使表述的很罗嗦,但不会引起歧义或怀疑.这才是一份好的标准.文档,代码.而避免歧义,和各种混乱不堪的规则,是一门语言最应遵守的设计原则.
最后也希望大家在日常的开发中,能够少遇坑。