JavaScript 条件判断之 boolean 类型值的骚操作(!!)
前言
条件判断是我们在写代码时最常用的语法之一,如:
let n = 0
if (n) {}
let s = ''
if (s.length) {}
let v1 = undefined
if (v1) {}
// 通过 !! 运算符强转成 Boolean
if (!!n) {}
if (!!s) {}
if (!!v1) {}
有时候我们想要得到一个明确的 Boolean 来进行判断/处理逻辑,我们可以通过 !!
或者 !!+
运算符来强制转成 Boolean 类型,但是在不同的数据类型中,这两者的转换结果也会有所不同。
String
测试代码:
const strings = ['', ' ', 'a', '0', '1', '1a', '.', '.34', '0o123', '0b0101', '0xabc']
strings.forEach(element => console.log(element, !!element, !!+element))
打印结果如下表所示:
原始值 | !! | !!+ | 备注 |
---|---|---|---|
false | false | ||
true | false | ||
a | true | false | |
0 | true | false | |
1 | true | true | |
1a | true | false | |
. | true | false | |
.34 | true | true | |
0o123 | true | true | 八进制 |
0b0101 | true | true | 二进制 |
0xabc | true | true | 十六进制 |
结论:对于 String 数据类型,空字符串时两者转换结果都为 false,但是在字符串长度不为空时,
!!
的转换结果则永远为 true,!!+
在数字类型字符串且不等于'0'
时为 true,其他情况均为 false(数字类型字符串表示合法的十进制、二进制、八进制、十六进制字符串)
可以简单理解为:
!!
等价于 string.length > 0!!+
等价于 string.length > 0 && 是数字类型字符串 && string != ‘0’
Number
测试代码:
const numbers = [0, 1, 1.2, .3]
numbers.forEach(element => console.log(element, !!element, !!+element))
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
0 | false | false |
1 | true | true |
1.2 | true | true |
0.3 | true | true |
结论:对于 Number 类型数据两者转换结果保持一致,对于
0
则为false
,其他情况均为true
Boolean
测试代码:
const bools = [true, false]
bools.forEach(element => console.log(element, !!element, !!+element))
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
true | true | true |
false | false | false |
结论:对于 Boolean 类型数据的转换结果和原始值保持一致
Null
测试代码:
const v = null
console.log(v, !!v, !!+v)
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
null | false | false |
结论:对于 Null 类型数据两者转换结果均为
false
undefined
测试代码:
const v = undefined
console.log(v, !!v, !!+v)
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
undefined | false | false |
结论:对于 undefined 类型数据两者转换结果均为
false
NaN
测试代码:
const v = NaN
console.log(v, !!v, !!+v)
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
NaN | false | false |
结论:对于 NaN 类型数据两者转换结果均为
false
Array
测试代码:
const values = [[], [1], [1, 2], ['1'], ['1', '2'], ['a'], ['a', 'b']]
values.forEach(element => console.log(element, !!element, !!+element))
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
[] | true | false |
[1] | true | true |
[1, 2] | true | false |
[‘1’] | true | true |
[‘1’, ‘2’] | true | false |
[‘a’] | true | false |
[‘a’, ‘b’] | true | false |
结论:对于 Array 数据类型,
!!
的转换结果永远为true
,而!!+
的转换结果对于有且只有一个元素,并且该元素是 Number 类型或纯数字类型字符串时结果为true
,其他情况均为false
Function
测试代码:
const func = () => {}
console.log(func, !!func, !!+func)
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
() = {} | true | false |
结论:对于 Functionp 类型数据,
!!
的转换结果永远为true
,而!!+
的转换结果则永远为false
Object
测试代码:
const objs = [{}, { name: 'zhangsan' }]
objs.forEach(element => console.log(element, !!element, !!+element))
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
{} | true | false |
{name: ‘zhangsan’} | true | false |
结论:对于 Object 数据类型,
!!
的转换结果永远为true
,而!!+
的转换结果则永远为false
Symbol
测试代码:
const symbol = Symbol('Test')
console.log(symbol)
console.log(!!symbol)
console.log(!!+symbol)
打印结果如下表所示:
原始值 | !! | !!+ |
---|---|---|
Symbol(Test) | true | TypeError: Cannot convert a Symbol value to a number |
结论:对于 Symbol 数据类型,
!!
的转换结果永远为true
,而!!+
的转换会抛出异常
结果
对于 !!
和 !!+
这两个运算符的转换结果,汇总如下表:
数据类型 | !! | !!+ |
---|---|---|
String | 空字符串时为 false,其他情况为 true | 数字类型字符串且不等于 '0' 时为 true,其他情况为 false |
Number | 0 为 false, 其他情况均为 true | 0 为 false, 其他情况均为 true |
Boolean | 和旧值保持一致 | 和旧值保持一致 |
Null | false | false |
undefined | false | false |
NaN | false | false |
Array | true | 当数组只有一个元素且元素为 Number 或 数字类型字符串为 true,其他情况为 false |
Function | true | false |
Object | true | false |
Symbol | true | 抛异常(TypeError: Cannot convert a Symbol value to a number) |
注:
- 数字类型字符串表示由数字和单个点号组成的字符串(即合法的十进制、二进制、八进制、十六进制字符串),如:123、1.2、.34、0o12、0b01、0x123abc 均为合法格式;1a、1.2.3 这种则为不合法格式
!!
和!!+
两者转换只在 String/Array/Function/Object/Symbol 这几个数据类型中存在差异,其他数据类型的转换结果两者都保持一致!!+
是由双重逻辑非(!!)和一元加法(+)两种运算符组合而成,但是一元加法的优先级比逻辑非的优先级要高,所以会先进行一元加法的运算,再进行双重逻辑非的运算,理解了优先级,那么这个转换结果就取决于一元加法了,因为一元加法会尝试将数据转成 Number 类型,转换失败时会返回 NaN,具体可以参考文档说明
对于使用 !!
还是 !!+
你可以对照上表的结果结合项目实际情况考虑吧,可以简单理解为 !!
是不严格的,!!+
是更加严格的,我平时都是使用 !!
,只是在偶然间看到有人使用 !!+
。