先记住这个答案
在 if、while、!、&&、|| 这些需要真假的地方,JavaScript 会把值转成布尔值。会被转成 false 的只有这些:false、0、-0、0n、NaN、空字符串 ''、null、undefined,浏览器里另外还有 document.all。这份名单是固定的,不在上面的一律是真值。面试最容易被追问的是反例:[]、{}、'0'、'false'、new Boolean(false) 全都是真值。
- 假值是固定名单,不在名单上的都是真值
- 所有对象都是真值,空数组、空对象也不例外
- 字符串只有空串是假值,
'0'和' '是真值 if (x)与x == false规则不同
名单之外的全是真值
背名单不难,难的是面对具体值时别凭感觉。下面这些值看起来「空」或者「假」,实际都是真值。
const falsy = [false, 0, -0, 0n, NaN, '', null, undefined]
console.log(falsy.every((v) => !v))
const looksFalsy = ['0', 'false', ' ', [], {}, -1, new Boolean(false)]
console.log(looksFalsy.map((v) => Boolean(v)).join(' '))
console.log(Boolean([]), [] == false) // 同一个值,两种判断结果相反查看输出与解释
true
true true true true true true true
true true字符串的规则很简单:长度为 0 才是假值。'0'、'false'、一个空格,长度都不是 0,所以都是真值。对象更直接,只要是对象就是真值,引擎根本不看里面装了什么。这个判断是查表完成的,不会调用对象的任何方法,所以哪怕 new Boolean(false) 内部存的是 false,它也是真值。
为什么 [] 是真值却 == false
最后一行的 Boolean([]) 是 true,但 [] == false 也是 true,看起来自相矛盾。原因是这两处用的规则不一样。
if ([]) 用的是 ToBoolean,查表,对象就是真。[] == false 用的是宽松相等的规则,它不会把两边转成布尔值,而是把布尔值转成数字:false 变成 0;数组转原始值变成 '',再转数字也是 0。最后比较的是 0 == 0。
所以写代码时不要用 == true、== false 去判断真假,想判断真假就直接 if (x),想判断具体值就用 ===。
容易答错的地方
- 漏掉
-0、0n和NaN - 很多人只背「0、空串、
null、undefined、false」五个。-0和0在大部分场景表现一样,0n是BigInt的零,NaN是运算失败的结果,这三个在列完整名单时经常被漏掉。 - 以为空数组、空对象是假值
- 在 Python 里空列表是假值,所以有这种直觉很正常。JavaScript 里判断数组是否为空要写
arr.length === 0,判断对象是否为空要用Object.keys(obj).length === 0。 - 用
if (count)判断数值是否存在 count为 0 时会走进 else,很多「数量为 0 时不显示」的 bug 就是这样来的。只想排除null和undefined时,用count != null或??。
面试官还会怎么问?
!!x 和 Boolean(x) 有区别吗?
没有。两者都执行 ToBoolean,结果完全一样。!! 更短,Boolean 语义更清楚,也可以直接传给 filter,比如 arr.filter(Boolean) 能一次去掉所有假值。
document.all 为什么是假值?
早年的代码常写 if (document.all) 来判断是不是老 IE。后来其他浏览器也实现了 document.all 以兼容依赖它的页面,但又不想让这些页面误以为自己在 IE 里,于是规范规定它在 ToBoolean 时为假,typeof 返回 'undefined'。
React 里 {count && <List />} 为什么会渲染出一个 0?
&& 左边为假时返回左边的值本身。count 是 0 时表达式的结果就是 0,React 会把数字 0 渲染出来(false、null、undefined 才不会渲染)。改成 count > 0 && ... 或三元表达式。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。