更優雅的方式讀取 Property

實務上我們常需要讀取 Object 的 Property,但我們並不確定 Object 是否有該 Property,最嚴謹的做法必須先檢查 Property 是否存在才能讀取,否則在 Runtime 可能會得到 Cannot read property xxx of undefined 的錯誤訊息。

Version


VS Code 1.33.0
Quokka 1.0.205
Ramda 0.26.1

ECMAScript


1
2
3
4
5
6
7
8
9
import { defaultTo, path } from 'ramda';

let pathOr = (val, arr) => obj => defaultTo(val, path(arr, obj));
let getStudentName = pathOr('No name', ['student', 'name']);

console.log(getStudentName({}));
console.log(getStudentName({ student1: { name: 'Sam' } }));
console.log(getStudentName({ student: { name1: 'Sam' } }));
console.log(getStudentName({ student: { name: 'Sam' } }));

若 object 具有 nested property,而我們又想讀取 object.student.name 的值。

由於 ECMAScript 是 dynamic language,並不保證 object 具有 studentname property,若只是 const getStudentName = obj => obj.student.name; ,只要傳入的 object 沒有 studentname property,在 runtime 就會出現 Cannot read property xxx of undefined 的錯誤訊息。

實作 pathOr(),第一個參數傳入 default value,第二個參數傳入 property 名稱 array,第三個參數傳入 object,最後使用 && 對每一層 property 做檢查,最後在加上 || 提供 default value。

pathor000

pathOr()


1
2
3
4
5
6
7
8
import { pathOr } from 'ramda';

let getStudentName = pathOr('No name', ['student', 'name']);

console.log(getStudentName({}));
console.log(getStudentName({ student1: { name: 'Sam' } }));
console.log(getStudentName({ student: { name1: 'Sam' } }));
console.log(getStudentName({ student: { name: 'Sam' } }));

使用 &&|| 的寫法固然可行,若 nested property 的層數更深,而每次只要讀取 property 就必須這樣寫,不只 code 很冗長,還可能不小心少寫一層 property 沒檢查。

事實上 Ramda 已經提供 pathOr(),可直接使用。

pathOr()
a -> [Idx] -> {a} -> a
Idx = String | Int
針對 object 的 nested property 取值,若 property 不存在,則回傳 default value

a:若 nested property 不存在,則提供 default value 回傳

[Idx]:以 array 描述 nested property

{a}:data 為 object

a:回傳 property 的 value 或 default value

pathOr() 寫法遠比 &&|| 語意更清楚,也不用擔心會少寫一層 propery 沒檢查。

Ramda 另外也提供了 propOr(),適用於只有一層的 property

pathor001

Conclusion


  • 由於 ECMAScript 是 dynamic language,因此很多都必須在 runtime 檢查
  • 若要讀取 object 的 property,對於 property 的檢查是必要的,因為無法確定該 object 是否有 property,建議使用 Ramda 的 propOr()pathOr() 取代用 &&|| 檢查,不只語意更清楚,還可避免 property 檢查不完全

Reference


Ramda, propOr()
Ramda, pathOr()