【JS笔记】?? ?. ?: 运算符浅谈

125 阅读3分钟

0 引子

? 运算符一般有3个用法:条件运算(? :)、熔断(?.)、判断保护(??)。

1 条件运算(? :

条件运算也叫三目运算。

语法:表达式 ? 语句1 : 语句2

作用:如果“表达式”为真true,执行“语句1”;否则,执行“语句2”。

示例:

let a, b;
a = 5
a > 3 ? b = 10 : b = 0 // 运行结果 b === 10
// 上面这句等价于:
if (a > 3) b = 10
else b = 0

// 也可以这么用:
b = a > 3 ? 10 : 0 // 和 a > 3 ? b = 10 : b = 0 等价。 注意运算符的优先级

2 熔断(?.

语法:在属性调用时 ?. 的用法和 . 一样。

作用:保护属性连环调用过程,确保不会报错。

示例:

let family = {
  dad: {
    name: 'Tom',
    age: 30
  },
  mom: {
    name: 'Jerry'
  }
}

family.dad.name // => Tom
family.mom?.name // => Jerry      如果调用的属性是存在的, ?. 和 . 的结果没有区别
family.son.name // Error!!!       此时由于 family 中 son 属性不存在,会直接报错
family.son?.name // => undefined  此时 ?. 的作用显现,如果属性不存在就直接返回undefined

可以看到,?. 就像保险丝一样,保护连环调用的过程,当某属性不存在时,立即熔断,不再执行后续调用,直接返回 undefined

需要注意

  • ?. 只能保护当前位置的调用,所以哪里需要熔断保护,哪里就要写 ?.
  • ?. 保护第一层调用时能保护空值,但不能保护不存在(有点绕,具体看示例)。

示例:

// 某长调用链的保护
obj?.propA?.propB.propC?.propD // 哪里需要哪里搬。 此时如果 propB 不存在,就会报错!

// 如果 obj 本身不存在
obj?.propA // 依然会报错

let obj // 等效于 let obj = undefined
obj?.propA // => undefined  熔断保护有效

let obj = null
obj?.propA // => undefined  熔断保护有效。注意,返回值依然是 undefined,并不会返回 obj 的值 null

let obj = ''
obj?.propA // => undefined  熔断保护有效。和 null 的情况一样。

3 判断保护(??

语法:?? 的用法和 || 的用法完全一样,值/表达式1 ?? 值/表达式2。 作用:当“值/表达式1” 为 undefined 或 null 时,返回 “值/表达式2”;否则,返回“值/表达式1”。一定要注意??|| 的区别!

示例:

let a, b
a = b.push('Tom') // 这样会报错,因为此时的 b === undefined
a = (b ?? []).push('Tom') // 此时不会报错, a === ['Tom']

// 举一个简单的用例:
let arr = ['a', 'b', 'a', 'a', 'b']
let map = new Map() // 用哈希表统计 arr 中各个元素的数量
for (let i of arr) map.set(i, (map.get(i) ?? 0) + 1) // 如果不用 ?? 判断保护,map.get(i) 立刻报错
// 这里的 ?? 可以换成 || ,效果一样

认识到 ?? 的判断保护功能,再说说与 || 的区别:

  • ?? 仅对 undefined 和 null 生效。
  • || 对所有的空值都生效,包括 ''、0、undefined、null,甚至 false,类似于判非。

示例:

// ?? 的各种测试结果
undefined ?? 999   // => 999
null ?? 999        // => 999
0 ?? 999           // => 0
'' ?? 999          // => ''
false ?? 999       // => false

// || 的测试结果
undefined || 999   // => 999
null || 999        // => 999
0 || 999           // => 999
'' || 999          // => 999
false || 999       // => 999
1 < 0 || 999       // => 999
'0' || 999         // => '0'  注意,字符 '0' 不是空

此外,在使用 ??|| 时一定要注意操作符优先级的问题,记住 ??|| 的优先级很低很低!

4 总结

做个总结,? 的各种用法的本质都是在判断前序 值/表达式 的状态,判“非”、判“存在”、判“空”等等,理解内在逻辑,就能轻易掌握了!

笔记主要为自用,欢迎友好交流!