前端数据不可信,后端数据不可知
在前端我的项目开发与生产的过程中,“cannot read property of undefined”是一个常见的谬误。从不可知失去一个空数据问题在劫难逃。面对这种问题咱们该怎么办呢?
针对于特定的业务和数据,前端即便预知数据不存在,也无奈解决,任其解体即可。但大部分状况下,如果因为列表某条数据呈现谬误而导致整个页面无法访问,用户将对咱们零碎健壮性产生信任危机。
为了可能在对象 (JavaScript 中数组也是对象) 中平安的取值,须要验证对象中数据项是否存在,if 语句判断当然是没有问题的,然而如果数据门路较深,代码就过于冗余了,而常见的解决计划有如下几种。
短路运算符号嗅探
JavaScript 中咱们能够通过 && 来实现获取员工的地址邮编代码
const result = (staff.address && staff.address[0] && staff.address[0].zip) || ''
原理解析
这种解析形式和 JavaScript 异于其余语言的判断机制无关。大部分语言都仅有 true 和 false, JavaScript 有 truthy
概念,即在某些场景下会被推断为 true
。
当然以下数据会被解析为 false:
- null
- undefined
- NaN
- 0
- 空字符串
除此之外,都会被解析为 true,即便空数组, 空对象(注: Python 空字典,空列表,空元组均在判断中会被解析为 false)也不例外。
同时 && || 不仅仅返回 true 和 false,而是数据项。
运算符 | 阐明 |
---|---|
逻辑与,AND(&& ) |
若第一项可转换为 true ,则返回第二项;否则,返回第一我的项目。 |
逻辑或,OR | 若第一项可转换为 true ,则返回第一项;否则,返回第二我的项目。 |
逻辑非,NOT(! ) |
若以后项可转换为 true ,则返回 false ;否则,返回 true |
|| 单元保底值
既然能够通过 && 来对数据进行嗅探,那么咱们能够退一步,如果以后没有我的项目数据,利用 || 返回空对象或者空数组。
const EMPTY_OBJ = {}
const result = (((staff || EMPTY_OBJ).address || EMPTY_OBJ)[0] || EMPTY_OBJ).zip || ''
比照上一个计划,尽管相比上述代码更为简单。然而如果针对领有残缺数据的数据我的项目而言,对数据的拜访次数较少(. 的使用率), 而上一个计划针对欠缺数据的拜访会多不少。而大部分数据无疑是正确与残缺的。
try catch
该办法无需验证对象中数据项是否存在,而是通过错误处理间接解决。
let result = ''
try {result = staff.address[0].zip
} catch {// 谬误上报}
try catch 计划更适宜必要性数据缺失作为上报的状况。但如果产生了必要性内容数据缺失,前端界面解体反而是一件坏事。所以 try catch 不太适宜解决对象平安拜访这种问题,仅仅作为可选计划。
链判断运算符
上述解决形式都很苦楚,因而 ES2020 引入了“链判断运算符”(optional chaining operator)?.
,简化下面的写法。
const reuslt = staff?.address?.[0]?.zip || ''
简略来解释:
a?.b
// 等同于
a == null ? undefined : a.b
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
如果你想要具体理解,能够参考阮一峰 ECMAScript 6 入门 链判断运算符 一篇。
手写门路获取
某些状况下,咱们须要传递门路来动静获取数据, 如 ‘staff.address[0].zip’, 这里手写了一个解决代码。传入对象和门路,失去对象,对象 key 以及 value。
/**
* 依据门路来获取 对象外部属性
* @param obj 对象
* @param path 门路 a.b[1].c
*/
function getObjPropByPath(obj: Record<string, any>, path: string) {
let tempObj = obj
const keyArr = path.split('.').map(x => x.trim())
let i: number = 0
for (let len = keyArr.length; i <len - 1; ++i) {let key = keyArr[i]
// 简略判断是否是数组数据,如果 以 ] 结尾的话
const isFormArray = key.endsWith(']')
let index: number = 0
if (isFormArray) {const data = key.split('[') ?? []
key = data[0] ?? ''// 对于 parseInt('12]') => 12
index = parseInt(data[1], 10)
}
if (key in tempObj) {tempObj = tempObj[key]
if (isFormArray && Array.isArray(tempObj)) {tempObj = tempObj[index]
if (!tempObj) {return {}
}
}
} else {return {}
}
}
if (!tempObj) {return {}
}
return {
o: tempObj,
k: keyArr[i],
v: tempObj[keyArr[i]]
}
}
不过笔者写的计划较为毛糙,但 lodash 对象模块中也有该性能,感兴趣的能够参考其实现形式。lodash get
// 依据 object 对象的 path 门路获取值。如果解析 value 是 undefined 会以 defaultValue 取代。// _.get(object, path, [defaultValue])
var object = {'a': [{ 'b': { 'c': 3} }] };
_.get(object, 'a[0].b.c');
// => 3
_.get(object, ['a', '0', 'b', 'c']);
// => 3
_.get(object, 'a.b.c', 'default');
// => 'default'
其余
Null 判断运算符
当然,咱们大部分状况下应用 || 都没有问题,然而因为 falsy
的存在. || 对于 false 和 undefined 是一样的。然而某些状况下,false 是有意义的,true, false, undefined 均代表一种含意,这时候,咱们还须要对数据进行其余解决,应用 in 或者 hasOwnProperty 进行存在性判断。
针对于这种状况,ES2020 引入了一个新的 Null 判断运算符 ??
。它的行为相似||
,然而只有运算符左侧的值为 null
或 undefined
时,才会返回右侧的值。如
const result = staff.address && staff.address[0] && staff.address[0].zip ?? ''
激励一下
如果你感觉这篇文章不错,心愿能够给与我一些激励,在我的 github 博客下帮忙 star 一下。
博客地址
参考资料
ES6 入门教程