點燈坊

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

ECMAScript 之讀取 Property

Sam Xiao's Avatar 2020-03-27

讀取 Object 的 Property 為處理 Object 必備功能,本文整理出 5 種讀取 Property 方式。

Version

macOS Catalina 10.15.4
VS Code 1.43.2
Quokka 1.0.284
ECMAScript 2015
Ramda 0.27.0

Dot Operator

let obj = {
  title: 'FP in JavaScript'
}

obj.title // ?

最傳統方式是來自 Java 的 . 讀取 property。

get000

[] Operator

let obj = {
  title: 'FP in JavaScript'
}

obj['title'] // ?

ECMAScript 的獨門武器,讓我們可以使用 [] 讀取 property。

get001

import { pipe, clone } from 'ramda'
import { stringify } from 'wink-fp'

let obj = {
  title: 'FP in JavaScript'
}

let key = 'title'
obj[key] // ?

[] 最大優勢是可以使用 variable,這是 . 所無法的。

get002

Object Destructuring

let obj = {
  title: 'FP in JavaScript'
}

let title = obj.title
title // ?

實務上常會定義與 property 相同的 variable 方便後續使用。

get003

import { pipe, clone } from 'ramda'
import { stringify } from 'wink-fp'

let obj = {
  title: 'FP in JavaScript'
}

let { title } = obj
title // ?

既然 variable 與 property 名稱相同,在 ES6 可使用 object destructuring 一次解決。

get004

Ramda

import { prop } from 'ramda'

let obj = {
  title: 'FP in JavaScript'
}

prop('title')(obj) // ?

以上都是使用 operator 讀取 property,這並不方便 FP 使用,Ramda 提供了 prop()

get005

import { propOr } from 'ramda'

let obj = {
  title: 'FP in JavaScript'
}

propOr('N/A')('title')(obj) // ?
propOr('N/A')('titled')(obj) // ?

Ramda 另外提供了 propOr(),若 property 不存在將回傳預設值避免 undefined 的 runtime 錯誤。

get006

import { map, prop } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let f = k => map(prop(k))

f('title')(data)  // ?

prop() 的功用在哪呢 ? 它可使 callback 能 point-free,這是 operator 所不能的。

get007

import { prop, map, compose } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let f = compose(map, prop)

f('title')(data)  // ?

既然 prop() 是 function,因此可使用 function composition,這也是 operator 所不能的。

get008

Lens

import { lensProp, view } from 'ramda'

let obj = {
  title: 'FP in JavaScript'
}

let titleLens = lensProp('title')

view(titleLens)(obj) // ?

FP 會使用 lens 取代 key,藉此達到 immutable。

prop() 是透過 key 讀取 property,view() 則是透過 lens 讀取 property。

get009

import { map, view, lensProp, compose } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let titleLens = lensProp('title')

let f = l => map(view(l))

f(titleLens)(data)  // ?

原本 f() 要傳入 key 給 prop(),現則改傳 lens 給 view()

get010

import { map, view, lensProp, compose } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let titleLens = lensProp('title')

let f = compose(map, view)

f(titleLens)(data)  // ?

由於 view() 也是 function,因此也可使用 function composition。

get011

Conclusion

  • ECMAScript 本身提供了 3 種 operator 讀取 property,唯 operator 適合 imperative,並不適合 FP
  • Ramda 提供了 prop()view() 讀取 property,唯 prop() 要搭配 key,而 view() 要使用 lens

Reference

Ramda, prop()
Ramda, propOr()
Ramda, map()
Ramda, compose()
Ramda, lensProp()
Ramda, view()