本文是关于JavaScript的陷阱和最佳实践中的一部分。
===而不是===== && ==在JavaScript中,有二种判断值是否相等的等性运算符:
===ES5 11.9.6描述了===的具体算法。我们以x === y为例:
x和y的类型不同,返回falseUndefined或Null,返回trueNumber
x或y为NaN,返回falsex和y的数值相等,返回truex为+0且y为-0,返回true,反之亦然falseString,那么很明显,必须两者包含相同的字符串才会返回true,否则返回falseNumber,与String类型情况一样,必须两者同时为true或false才会返回true,否则返回falsex和y两者都指向同一个对象时,返回true,否则返回falseNaN === NaN // false
+0 === -0 // true
var foo = function() {}
foo === foo // true注意:
NaN并不等于它自己,即NaN === NaN返回false。所以我们必须使用其它方法来判断某个值是否为NaN。
==ES5 11.9.3描述了==的具体算法。我们以x == y为例:
x和y的类型相同,则使用===算法比对x为null且y为undefined,则返回true,反之亦然Number类型,另外一个是String类型,那么需要先把String类型的转换为Number,然后再执行==Boolean类型,另外一个是非Boolean类型,那么需要先把Boolean类型转换为Number类型,然后再执行==String或是Number类型,另外一个是Object类型,那么需要先把Object类型转换为primitive的类型,然后再执行==false在比较时,该运算符还遵守下列规则:
null和undefined相等null和undefined转换成其他值NaN,等号将返回false,非等号将返回truetrue,否则两个运算数不相等即使两个数都是
NaN,等号仍然返回false,因为根据规则,NaN不等于NaN。
undefined == null // true
false == 0 // true
true == 1 // true
true == '1' // true
true == 'foo' // false
'' == 0 //true
' ' == 0 // true
'foo' == new String('foo') // true
[] == 0 // true
'abc' == new String('abc') // true==带来的问题从上面的分析,我们可以看到如果使用==,可能会带来一些问题:
==做判等时,如果两者类型不同,则需要做大量的类型转换,而这些类型转换规则并不是一眼就能看出来的,如果你稍不留意,结果就可能与你想像的大不相同==可以允许任意的类型做判断,所以一些类型错误就有可能难以发现比如:
true == 1 // true
true == 2 // false 与常识相反
true == '1' // true
true == '2' // false 与常识相反
0 == '' // true
'\r\n\t 111 \t' == 111 // truetrue == 2为false是因为true首先会被转化为1,然后再执行1 == 2,所以返回false。
大家可能会觉得最后一个例子'\r\n\t 111 \t' == 111很奇怪,为什么它会返回true?那是因为'\r\n\t 111 \t'在执行==前,必须先转换为Number,而这样会删除掉数值前后的空白符。
==我们经常给JavaScript初学者的其中一个建议就是尽量使用===而不是==,毫无疑问,这是完全正确的,特别是对一个初学者来说。但是有些情况下,我们可能会觉得使用==更合乎常理,但是他们其实不是看上去那样美的。
虽然你的代码只写了一次,但是它会被你和其他人读多次,所以代码的可读性是非常重要的。
undefined和null比较如果你看完了上面的分析,那么你就会知道,使用==时,undefined和null之间是互等的,但是与其它值判等时都是false。所以,我们经常会用下在的代码来判断一个值是否为undefined或null:
if (foo == null) {
...
}乍一看,这样判断代码简短,但是可读性很弱,无法完全表达作者原有的意思。如果一个JavaScript初学者看到这段代码,他可能会以为你只是在检测null,但是如果是一个经验丰富的前端工程师的话,他可能会认为你写错,其实你是想用===。如下所示:
if (foo === undefined || foo === null) {
...
}如果你还想更精简一点,那么可以这样写:
if (!foo) {
....
}在JavaScript中,
false,null,0,'',undefined和NaN都被当做false值。
在处理与后端交互的Ajax请求返回结果时,我们经常需要去处理返回的以字符串形式展现的数值,如id: '111111'。这个时候,我们可能经常会这样去做比较:
if (id == 111111) {
....
}这个时候,我们是去检查id是111111或是'111111'。
但是,如果别人来看你的代码时,他会认为id是Number类型的。那么,为什么不直接告诉读你代码的人它是我们需要的是Number类型的呢?如果不是,我们需要先进行显示的类型转换。
if (Number(id) == 111111) {
....
}由于==支持类型不同的值的比较的,所以你也用它来比较对象和原生数据类型值。
true == new Boolean(true)如果你看到下面的这段代码,你会认为写这段代码的人是想做什么?
str == 'foo'str可能是一个String类型的包装对象,你是想让一个对象和字符串进行比较吗?如果是,你得了解这个对象是如何转换为原生数据类型的。str先转换为字符串吗?如果是,你可以让你的代码更清楚点String(str) == 'foo'str是一个String类型的包装对象,你想得到对象的原始值,然后再做比较?str.valueOf() == 'foo'比如,当我们以函数的形式调用String构造函数时,它会自动完成类型转换,如下代码所示:
if (String(foo) == 'foo') {
...
}你知道String(foo)返回的肯定会是String类型,所以这里使用==是没有任何问题的,因为我们可以保证这里不可能会有任何的类型转换的。
但是,我们仍然可以不用这样做:
==,并没有给我们带来任何收益,那么我们为什么不使用更简单的===呢?===是判断是否相等最简单的方法,那是因为它并不会对它的操作数进行类型转换。虽然性能测试结果在所有的JavaScript引擎中不尽相同,但对大多数浏览器来说,===与==性能差不多,甚至更快。虽然许多种情况下,看似使用==不伤大雅,但是不使用==已经是一条不成文的规则,不仅仅是对新手而已。
代码中少一些花哨,那么就更容易理解和维护。