假设有这样一个对象,表示的是 用户是否启用了回复通知的设置git
const settings = { notification: { reply: { active: { true } } // ...其余设置项 } // ...其余设置项 }
当开发者想要提取 active
的值,最直接的方法是这么作github
const isNotificationReplyActive = settings.notification.reply.active
但这是不安全的,由于 JavaScript 中一般使用 null
或 undefined
分别表示未定义或未声明的值npm
typeof someVar === 'undefined' // 未声明 let someVar = null // 已声明,未定义
实际开发过程当中可能由于好比节省资源的考虑,当用户未进行过设置时,它的 notification 或者更深的某一级的值是 null
或 undefined
,而非对象。安全
// 好比当未设置回复通知时,它是这样的 const settings = { notification: { // 没有 reply } } // 这种状况下, settings.notification.reply 的值是 undefined // JS 中试图获取 undefined 上的 key 时会触发 TypeError const isNotificationReplyActive = settings.notification.reply.active // TypeError!
因而开发者采起了这样的措施dom
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')
虽然它保证了开发者在提取属性的过程当中不会由于遇到 undefined
或 null
之类的值而抛出 TypeError ,但缺点也很明显——工具
const { notification: { reply: { active } } } = settings
简直是一晚上回到解放前。spa
如今让咱们来回顾一下本文开头的那张图——它便是本文的主角、上述全部问题的解决方案。code
// 引入 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)