RORO:JavaScript 中的優雅模式

簡評:RORO 即函數的參數和返回值都為單個對象,這樣可以幫助我們簡化很多的問題。

接收一個對象,返回一個對象(RORO)

我大部分的函數都只接收一個 object 類型的參數,同時 return 或 resolve 一個 object 類型的返回值。

我發現這是非常的棒,這主要是感謝 ES2015 中引入的 Destructuring 特性,讓對象取值變得非常的方便。

對於 destructuring 的介紹可以參考這個視頻:

https://link.zhihu.com/?target=https%3A//www.zhihu.com/video/951448153598251008

我列出幾個使用 RORO 的優點:

  • 參數命名

  • 簡化默認參數

  • 豐富你的返回值

  • 函數組合更簡單

參數命名

假設我們有一個函數用於返回指定角色的用戶列表,我們需要傳入

role withContactInfoincludeInactive
這三個參數來獲取具體的用戶。

傳統寫法:

// 定義 findUsersByRole 函數function findUsersByRole ( role, withContactInfo, includeInactive) {...}// 函數調用findUsersByRole( 'admin', true, true)

調用時可以看到後面的兩個參數的含義比較模糊,我們無法知道 true true 具體指的是什麼?

我們看看把這個函數改成只接收單個對象會發生什麼:

// 定義 findUsersByRole 函數function findUsersByRole ({ role, withContactInfo, includeInactive}) {...}// 調用部分findUsersByRole({ role: 'admin', withContactInfo: true, includeInactive: true})// 參數順序不需要關心findUsersByRole({ withContactInfo: true, role: 'admin', includeInactive: true})// 可以在不影響舊代碼的情況下新增參數findUsersByRole({ withContactInfo: true, role: 'admin', includeInactive: true, newParames: true})

可以看到函數定義部分和傳統的寫法幾乎沒有區別,僅僅是用 {} 將參數括起來。

調用部分可以清晰的看到具體參數功能,並且調用時完全不用關心參數的順序,只需要關心參數的值。到以後如果需要給函數擴展功能添加參數時也可以不需要破壞老舊代碼。

如果需要所有的參數都是可選的,例如:

findUsersByRole()

為了上面這段代碼可以正常調用,我們需要為參數對象設置一個默認值,例如:

function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) {...}

解構我們傳入的參數有一個好處,可以保證參數的不變性。當在函數中修改解構後的參數不會影響傳入的原生數據。例如:

const options = { role: 'Admin', includeInactive: true}findUsersByRole(options)function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) { role = role.toLowerCase() console.log(role) // 'admin' ...}console.log(options.role) // 'Admin' 沒有改變

簡化默認參數

傳統寫法:

function findUsersByRole ( role, withContactInfo = true, includeInactive) {...}

試想一下如果我們調用 findUsersByRole 函數時只想設置 includeInactive 這個參數為 true,會怎麼樣:

// 實際調用代碼findUsersByRole( 'Admin', undefined, true)

由於參數的順序問題,我們需要在 withContactInfo 的位置傳一個 undefined 。如果使用 RORO 就能夠很好的處理這個問題:

function findUsersByRole ({ role, withContactInfo = true, includeInactive} = {}) {...}findUsersByRole( role: 'Admin', includeInactive = true)

必要參數驗證

下面這段代碼非常的常見於檢驗傳入的參數:

function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) { if (role == null) {  throw Error(...) } ...}
NOTE: == 用於同時檢測 null 和 undefined

在每個函數前都加一個檢驗過於繁瑣,我們可以使用默認參數來處理這個問題。

先定義一個檢驗參數的函數:

function requiredParam (param) { const requiredParamError = new Error( `Required parameter, "${param}" is missing.` ) // preserve original stack trace if (typeof Error.captureStackTrace === ‘function’) { Error.captureStackTrace( requiredParamError, requiredParam ) } throw requiredParamError}

現在我們在定義參數的時候可以這麼寫了:

// 定義 findUsersByRole 函數function findUsersByRole ({ role = requiredParam('role'), withContactInfo, includeInactive} = {}) {...}findUsersByRole() // Error: Required parameter, “role” is missing.

當調用 findUsersByRole 參數時如果沒有 role 這個參數會報錯。這是一個非常棒的小技巧。

更豐富的返回值

JavaScript 的 function 只能返回一個值,如果這個值是 object 類型那就能夠包含更多的信息。

函數組合更加簡單

我們使用 pipe 函數將多個函數進行組合,pipe 函數如下:

function pipe(...fns) { return param => fns.reduce( (result, fn) => fn(result), param )}

該函數可以傳入一組函數,並返回一個函數,該函數可以依次(從左向右)調用函數列表中的函數,並依次將函數的返回結果作為下個函數的參數,這裡有個限制是列表中的函數只能接收一個參數,如果使用 RORO 就可以完美解決這個問題。

舉個例子,我們已經有一個 saveUer 函數,這個函數可以分成 3 個函數,分別是 validate, normalize 和 persist:

function saveUser(userInfo) { return pipe( validate, normalize, persist )(userInfo)}function validate({ id, firstName, lastName, email = requiredParam(), username = requiredParam(), pass = requiredParam(), address, ...rest}) { // do some validation return { id, firstName, lastName, email, username, pass, address, ...rest }}function normalize({ email, username, ...rest}) { // do some normalizing return { email, username, ...rest }}async function persist({ upsert = true, ...info}) { // save userInfo to the DB return { operation, status, saved: info }}

原文:Elegant patterns in modern JavaScript: RORO(https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/elegant-patterns-in-modern-javascript-roro-be01e7669cbd)

RORO:JavaScript 中的優雅模式


分享到:


相關文章: