點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Optional Chaining Operator 判斷 Property 是否存在

Sam Xiao's Avatar 2020-12-22

ECMAScript 2020 支援了 ?. Optional Chaining Operator,至此 ECMAScript 終於有專屬的 Operator 用於 Nested Object,不必再借用 && 與 Falsy Value。

Version

ECMAScript 2020

Object

let book = {
  title: 'FP in JavaScript',
  price: {
    amazon: 100
  }
}

book.price.amazon // ?
book.fee.amazon // ?

當 Object 的 property 為 Object,若 property 不存在,會出現 undefined 的 runtime error,這在 ECMAScript 經常出現。

chain000

let book = {
  title: 'FP in JavaScript',
  price: {
    amazon: 100
  }
}

book.price && book.price.amazon // ?
book.fee && book.fee.amazon // ?

傳統會先使用 && 判斷 book.fee property 是否存在,若存在才讀取該 property。

fee property 根本不存在,則 && 右側不會執行,直接 short-circuiting 回傳 undefined

&& 其實是判斷 truthy value,而 1、non-empty string 與 true 皆是 truthy value,因此可能誤判,並不是最完美解法。

chain001

let book = {
  title: 'FP in JavaScript',
  price: {
    amazon: 100
  }
}

book.price?.amazon // ?
book.fee?.amazon // ?

ES2020 支援了 ?.,若 book.fee property 存在才會讀取 book.fee.amazon,否則直接回傳 undefined

?. 只判斷 nullundefined 而非 truthy value,因此不用擔心誤判。

chain002

Expression

let book = {
  title: 'FP in JavaScript',
  price: {
    amazon: 100
  }
}

book.price?.['amazon'] // ?
book.fee?.['amazon'] // ?

?. 也可以在 [] 使用 expression,因此可使用 variable。

chain003

Array

let book = {
  title: 'FP in JavaScript',
  price: [100]
}

book.price?.[0] // ?
book.fee?.[0] // ?

若 Object 的 property 為 Array,且不確定 property 是否存在,也可使用 ?. 判斷。

chain004

Function

let book = {
  title: 'FP in JavaScript',
  price: 100,
  getDescription: function() {
    return `${this.title} : ${this.price}`
  }
}

book.getDescription?.() // ?
book.showDescription?.() // ?

若不確定 getDescription() 是否存在於 book Object,可在 () 執行前加上 ?. 判斷,若存在才執行。

chain005

?. with ??

let book = {
  title: 'FP in JavaScript',
  price: {
    amazon: 100
  }
}

book.price?.amazon ?? 'N/A' // ?
book.fee?.amazon ?? 'N/A' // ?

?. 雖然好用,但可能回傳 undefined,因此可搭配 ?? 提供預設值。

chain006

Error.response

<template>
  <div>
    <button @click="onClick">Error.response</button>
    <div>{{ message }}</div>
  </div>
</template>

<script>
import axios from 'axios'

let fetchAPI = () => axios.get('http://localhost:3000/')

let onClick = function() {
  fetchAPI()
    .then(x => x.data)
    .then(x => this.message = x)
    .catch(e => console.log(e.response?.status??'404'))
}

export default {
  name:'App',
  data: () => ({
    message: ''
  }),
  methods: {
    onClick
  }
}
</script>

14 行

fetchAPI()
  .then(x => x.data)
  .then(x => this.message = x)
  .catch(e => console.log(e.response?.status??'404'))

若要讀取 HTTP status code,可在 .catch() 讀取 x.response.status,但問題是 response 並不是永遠存在,如斷網時,error object 並沒有 response Object。

  • 可使用 ?. 判斷 response Object 是否存在,若存在則回傳 status property
  • response Object 不存在為 undefined,可用 ?? 提供預設值 404

Summary

object.value?.prop
object.value?.[expr]
object.value?.[index]
object.func?.(args)

?. 可用於檢查 Object、Array 或 Function 是否存在。

Conclusion

  • ?. 為 ES2020 標準,目前 browser 都已支援可安心使用
  • Node 在 14 以上支援 ?.,若要在 Quokka 使用必須搭配 Node 14 以上
  • ?. 支援 short-circuiting,如 ?. 左側是 nullundefined,則 ?. 右側將不會執行
  • ?.?? 在實務上經常搭配使用,因為 ?. 可能回傳 undefined?? 剛好替 undefined 提供預設值
  • ?. 實務上最常用在 Error.reponse,因為 response Object 並不是永遠存在,並搭配 ?? 提供預設值

Reference

MDN, Optional Chaining
V8, Optional Chaining