深層屬性,輕鬆提取

Enix發表於2019-02-20

深層屬性,輕鬆提取

面臨的問題

假設有這樣一個物件,表示的是 使用者是否啟用了回覆通知的設定

const settings = {
    notification: {
    	reply: {
            active: {
            	true
            }
        }
        // ...其他設定項
    }
    // ...其他設定項
}
複製程式碼

當開發者想要提取 active 的值,最直接的方法是這麼做

const isNotificationReplyActive = settings.notification.reply.active
複製程式碼

但這是不安全的,因為 JavaScript 中通常使用 nullundefined 分別表示未定義或未宣告的值

typeof someVar === 'undefined' // 未宣告
let someVar = null // 已宣告,未定義
複製程式碼

實際開發過程中可能因為比如節省資源的考慮,當使用者未進行過設定時,它的 notification 或者更深的某一級的值是 nullundefined,而非物件。

// 比如當未設定回覆通知時,它是這樣的
const settings = {
    notification: {
    	// 沒有 reply
    }
}

// 這種情況下, settings.notification.reply 的值是 undefined
// JS 中試圖獲取 undefined 上的 key 時會觸發 TypeError
const isNotificationReplyActive = settings.notification.reply.active // TypeError!
複製程式碼

於是開發者採取了這樣的措施

const isNotificationReplyActive = settings
    && settings.notification
    && settings.notification.reply
    && settings.notification.reply.active
// 或者
try {
    const isNotificationReplyActive = settings.notification.reply.active
} catch (err) {
    // 錯誤處理
}
複製程式碼

經驗豐富的開發者都知道,這樣做的缺點很多,在此就不展開了。

於是一些工具函式誕生了,比如 lodash 的 _.get

import _ from 'lodash'
const isNotificationReplyActive = _.get(settings, 'notification.reply.active')
複製程式碼

雖然它保證了開發者在提取屬性的過程中不會因為遇到 undefinednull 之類的值而丟擲 TypeError ,但缺點也很明顯——

  1. 屬性的路徑被寫成了字串,開發者無法獲得 IDE/編輯器 的自動補全與智慧糾錯。
  2. 不能使用便捷的解構語法—— const { notification: { reply: { active } } } = settings

簡直是一夜回到解放前。

解決方法 —— safe-touch

現在讓我們來回顧一下本文開頭的那張圖——它即是本文的主角、上述所有問題的解決方案。

// 引入
import safeTouch from 'safe-touch'

const settings = { /* ... */ }
// 包裹要提取的物件
const touched = safeTouch(settings)

// 把它當作函式呼叫,可以獲得原始值
touched() === settings // true

// 亦可以直接獲取 settings 上存在的屬性,同樣通過呼叫取得屬性值
// 在現代化的 IDE/編輯器 中,這一過程可以給出智慧提示與自動補全
touched.notification.reply.active() // 若依本文開頭給出的例子,值為 true

// 可以安全地獲取並不存在的屬性,返回 undefined ,不會丟擲 TypeError
touched.something.does.not.exist[Math.random()]() // undefined

// 支援解構
const { notification: { reply: { active, notExistingKey } } } = touched
active() // true
notExistingKey() // undefined
複製程式碼

怎麼做到的

其實不難,核心功能通過 ES6 的 Proxy 就可以實現。再結合 TypeScript 的型別判斷(初學 TS,看了好久文件),生成 index.d.ts 供智慧化的 IDE/編輯器 使用,就有了自動補全與智慧提示。

短小的原始碼 repo (歡迎 Star / issue)

可以從 npm 獲取

相關文章