3

JavaScript 中大概有这几种 “类型”

  • undefined

  • null

  • string

  • boolean

  • number

  • object

  • function

之所以在 “类型” 上加了双引号,是因为严格来说,null 的类型是 object。但本文讨论的主题包括了关于 null 的类型转换,它和 object 不同,所以单独列出来了。这点请根据上下文强行区分一下,不是bug、不是bug、不是bug。

有趣的例子

'0' == 0;  // true
0   == ''; // true
'0' == ''; // false

这里展示的是相等比较的非传递性。即,如果有 a == b, b == c,并不代表 a == c。

如果说

'1' == true;
'0' == false;

这两个是常识,那对于一些特殊的字符串,你能正确判断吗?

'001' == true;
'002' == false; // 注意!
'0x0' == false;
'\n'  == false;

非严格相等(==)

当使用 == 比较,并且两侧的类型不同时,会触发隐式类型转换。标准中定义的转换规则很长,其核心就是,1、类型不同时,尽量转成 数字 比较;2、特例。(类型相同就按该类型的值判断,不特殊说明了,难道 '123' 还能等于 '456' 不成?)

关于特例,其实也就是 null 和 undefined 。也就是说,以下几个表达式是永远为 true 的。

undefined == null;
null      == undefined;
NaN       != NaN;    // 注意!
null      != 非null; // 注意!null 与非 null 值永远是不等的

所以也不需要再去深追为什么 undefined 和 null 是相等的,因为按照“规则”已经无法解释了,这是一种“约定”。OK,剩下的情况是转数字。

  • null 的数值是 0

  • undefined 的数值是 NaN

  • true 是 1,false 是 0

  • 至于字符串,嗯,有点复杂

  • object:调用 toString 或 valueOf 后转成基本类型,再转数字

第一条,在这里没用,因为涉及到 null 的比较时,除非 null == null,其他情况都是不等的。即

null != 0;

第二条,在这里没用,因为涉及到 NaN 的比较时,都是不等。

第三条,有点用,很有用!

1 == true; // 对
2 == true; // 错!

第四条,麻烦。80% 情况下,对于 '123' 转成 123,'abc' 转成 NaN,'' 转成 0,这三条记住就可以了。

'123' == 123;
''    == 0;
'abc'; // 转成数值是 NaN

另外的情况,由于字符串的组合形式各种各样,很难三言两语总结完,大致有:

  • 空白字符串转成 0,'\n' == 0

  • 十六进制、八进制正常转换,'0xff' == 255

  • 数字字符串会忽略首尾空白字符,正常转,'\n123\n' == 123

  • 数字字符和其他字符混合时,转成 NaN,如 '123abc'

现在回头去看“有趣的例子”,是不是一目了然。(嗯,不谢)

条件判断

暂且把 if - else 和 condition ? a : b; 中的判断情况称为条件判断吧。(场景还有取反、for、while 等)

等于 true 的值一定是 truthy,但等于 false 的值不一定是 falsy 。

new Boolean(false) == false;    // 这个没问题吧?
new Boolean(false) ? 'a' : 'b'; // 表达式的值是 'a'!

只能说,条件判断中对真假的判定和相等判断是不一样的。 显然,相等判断是基于数字比较的,而条件判断是基于布尔值。

关于布尔值的转换规则:

  • null, undefined, NaN 都是 false

  • 字符串,仅当空字符串('')时为 false,其他都是 true

  • object,都是 true (仅 null 除外)

我见过其他一些相等判断的技巧:

!!x == true;
+x  == 123;

取反操作会把变量强转成 boolean;一元 + 会把变量强转成 number。

也见过一些用的不太合适的地方(?):

if (!!x) // ...

在条件判断中本身会做强转的操作,为了可读性?不见得有提高。

严格相等(===)

严格相等的逻辑相对简单粗暴,如果类型不同,就不考虑隐式转换了,直接为假。如果类型相同:

  • 布尔、数字、字符串就看字面值是否相等

  • object,看引用是否相同

主要是考虑什么时候用 ==,什么时候用 === 。

很简单,只有在要求类型相同时,才用 ===,否则用 ==

有些场景的确只有非严格相等才能做,比如

var b = new Boolean(false);
b === false   // 竟然是错的
b == false    // 对
b ? 'a' : 'b' // 竟然是 'a'

结论就是当涉及到对象比较时,稍微斟酌一下。而对于像 typeof x 它一定会返回 string,这时用 == 比较就更合适一些。

小结

涉及到 == 比较,并且需要隐式转换时,会转成 number。

涉及到条件判断,自然是转成 boolean。

涉及到二元 +、- 等数值计算时,从左至右优先转成 number,除非是遇到 string,则做字符串拼接操作。


名一
2.9k 声望523 粉丝