> For the complete documentation index, see [llms.txt](https://chanshiyu.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chanshiyu.gitbook.io/blog/qian-duan/javascript/12-ke-xuan-lian-yu-kong-zhi-he-bing.md).

# 12 可选链与空值合并

## 可选链运算符

可选链运算符 `?.` 允许读取位于连接对象链深处的属性的值，而不必明确验证链中的每个引用是否有效。`?.` 运算符的功能类似于 `.` 链式运算符，不同之处在于，在引用为空 `nullish (null 或者 undefined)` 的情况下不会引起错误，该表达式短路返回值是 `undefined`。与函数调用一起使用时，如果给定的函数不存在，则返回 `undefined`。

语法：

```javascript
obj.val?.prop
obj.val?.[expr]
obj.func?.(args)
```

栗子：

```javascript
const nestedProp = obj.first?.second

// 等价于
const temp = obj.first
const nestedProp = temp === null || temp === undefined ? undefined : temp.second
```

实战：

```javascript
// 可选链和表达式
const nestedProp = obj?.['prop' + 'Name']

// 可选链不能用于赋值
const object = {}
object?.property = 1 // Uncaught SyntaxError: Invalid left-hand side in assignment

// 可选链访问数组元素
const arrayItem = arr?.[42]
```

## 空值合并运算符

空值合并运算符 `??` 是一个逻辑运算符，当左侧的操作数为 `null` 或者 `undefined` 时，返回其右侧操作数，否则返回左侧操作数。

与逻辑或运算符 `||` 不同，逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说，如果使用 `||` 来为某些变量设置默认值，可能会遇到意料之外的行为。比如为假值（例如 `''` 或 `0`）时。

```javascript
const qty = 0 ?? 42 // 0
const message = '' ?? 'hi!' // ''

const qty = 0 || 42 // 42
const message = '' || 'hi!' // 'hi!'
```

**空值合并运算符可以避免这种陷阱，其只在第一个操作数为 `null` 或 `undefined` 时（而不是其它假值）返回第二个操作数。**

短路：与 `OR` 和 `AND` 逻辑运算符相似，当左表达式不为 `null` 或 `undefined` 时，不会对右表达式进行求值。

```javascript
function A() {
  return undefined
}
function B() {
  return false
}
function C() {
  return 'foo'
}

A() ?? C() // A 和 C 都被执行

B() ?? C() // 只执行 B
```

不能与 `AND` 或 `OR` 运算符共用，因为空值合并运算符和其他逻辑运算符之间的运算优先级/运算顺序是未定义的，这种情况下会抛出 `SyntaxError`。

```javascript
null || undefined ?? 'foo' // 抛出 SyntaxError
true || undefined ?? 'foo' // 抛出 SyntaxError

// 但是如果使用括号来显式表明运算优先级，是没有问题的
(null || undefined ) ?? 'foo' // 返回 'foo'
```

需要注意的一点是，**空值合并运算符和 JavaScript 中的默认参数是有一点不一样的。因为默认参数只有在 `undefined` 的情况下才会进行默认值赋值操作，而参数为 `null` 时不会默认赋值。**

可选链运算符和空值合并运算符可以放在一起操作，确保值不存在的时候，有一个兜底的默认值可以给程序使用：

```javascript
const x = foo?.bar?.() ?? 'default'
```
