小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
在我们日常开发过程中,代码编写过程中稍不留神取值时、接口返回的数据不符合预期时就会在控制台看到cannot read property of undefined报错
导致报错的代码:
const obj = {
name: 'nordon',
info: {
age: ''
}
};
if(obj.msg.age === 18) { // Uncaught TypeError: Cannot read properties of undefined (reading 'age')
console.log('年龄18岁')
}
上述代码在取值的过程中便会报错,因为obj.msg是undefined,导致取值age属性的时候是不存在的,因此在我们开发过程中,需要保证在取值过程中需要对每一次取值的结果进行验证,只有每一次取值结果符合期望才可以执行取值操作
常见处理方式:
&&短路运算符进行可访问性嗅探:
if(obj.msg && obj.msg.age === 18) {}
|| 单元设置默认保底值:
if((obj.msg || {}).age === 18) {}
try catch捕获异常:
try {
if(obj.msg.age === 18) {}
} catch {
// todo ...
}
上述&& 和 || 两种种方式针对简单的数据结构是可以的,比较简单直观,二try catch的方式对代码的侵入性较高,不易过于频繁使用,而我们的取值操作是一件非常高频的事情,不可能每一次取值都要加try catch来进行容错处理
若是存在深层嵌套结构时:
const obj = {
user: {
info: {
hobbys: {
sports: [
{
value: "basketball",
label: "篮球",
},
{
value: "football",
label: "足球",
},
],
},
},
},
};
此时若是使用&& 和 || 的方式进行代码健壮性处理:
if(
obj &&
obj.user &&
obj.user.info &&
obj.user.info.hobbys &&
obj.user.info.hobbys.sports &&
obj.user.info.hobbys.sports[1] &&
obj.user.info.hobbys.sports[1].value === 'basketball'
) {
// todo...
}
看着上述密密麻麻的判断,是不是心情顿时就不好了,因此&& 和 || 的方式将会使代码变得十分臃肿,不是很合适,可以手动封装一个根据传入的参数获取值
手动封装函数:
/**
* keys: 数组,按照层递关系组成
* source: 数据源
*/
const getValue = (keys, source) => {
return keys.reduce((nextObj, key) => {
return nextObj && nextObj[key] ? nextObj[key] : null;
}, source);
};
使用方式:
getValue(["user", "info", "hobbys", "sports", [1]], obj); // { value: 'football', label: '足球' }
getValue(["user", "info", "hobbys", [1]], obj); // null
Optional chaining(可选链操作符):
可选链操作符(
?.)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.操作符的功能类似于.链式操作符,不同之处在于,在引用为空(nullish ) (null或者undefined) 的情况下不会引起错误,该表达式短路返回值是undefined。与函数调用一起使用时,如果给定的函数不存在,则返回undefined。
使用语法:
const obj = {
foo: {
bar: {
baz: 42,
},
},
};
const baz = obj?.foo?.bar?.baz; // 42
const safe = obj?.qux?.baz; // undefined
在babel中使用:
下载:
npm install --save-dev @babel/plugin-proposal-optional-chaining
在配置文件中配置,例如babel.config.js
{
"plugins": ["@babel/plugin-proposal-optional-chaining"]
}
然后就可以愉快的在项目中使用了