面临的问题
假设有这样一个对象,表示的是 用户是否启用了回复通知的设置
const settings = {
notification: {
reply: {
active: {
true
}
}
// ...其他设置项
}
// ...其他设置项
}
当开发者想要提取 active
的值,最直接的方法是这么做
const isNotificationReplyActive = settings.notification.reply.active
但这是不安全的,因为 JavaScript 中通常使用 null
或 undefined
分别表示未定义或未声明的值
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!
于是开发者采取了这样的措施
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 ,但缺点也很明显——
- 属性的路径被写成了字符串,开发者无法获得 IDE/编辑器 的自动补全与智能纠错。
- 不能使用便捷的解构语法——
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)