内容简介:在平时的项目里,相信不少小伙伴应该都会碰到为了避免上面的情况,有的小伙伴就会直接用 && 的方式,也就是如下形式的代码来避免程序报错嵌套的层次越深,需要的代码也就越多。
在平时的项目里,相信不少小伙伴应该都会碰到 Uncaught TypeError: Cannot read property 'name' of undefined 这种错误吧?按照正常的简约写法,直接获取嵌套属性的值,一旦中间甚至刚开始的某个值就是 undefined,控制台会报错,渲染模板也会直接失败。那么,有没有方法可以避免这种情况的发生呢?
最笨的逐级&&
为了避免上面的情况,有的小伙伴就会直接用 && 的方式,也就是如下形式的代码来避免程序报错
if (list && list[index] && list[index].content && list[index].content.length > 0) { // 其他代码 } 复制代码
嵌套的层次越深,需要的代码也就越多。
而且这样处理的话,程序确实是不会报错了,但是总觉得有点麻烦,要是我因为
记性不好
(其实就是懒)少写了一部分的话,有时候不就跟没写一样吗?
angular的 ?. 操作符
angular的?. 操作符算得上是一个骚操作了,只要在 模板中加上 ?. 操作符,就可以避免因为 null 或者 undefined 导致的模板渲染错误了。
The safe navigation operator ( ?. ) and null property paths
The Angular safe navigation operator (?.) is a fluent and convenient way to guard against null and undefined values in property paths. Here it is, protecting against a view render failure if the currentHero is null.
翻译过来大概意思就是:
安全导航操作符(?.)和 null 属性路径angular的安全导航操作符(?.)是一种流畅且方便的方法,可以防止属性路径中的 null 和 undefined 值,如果 currentHero 为 null,则保护视图防止其呈现失败。
当使用 ?. 操作符的时候, The current hero's name is {{currentHero?.name}}
,即使currentHero 的值是 null 或者 undefined,该部分也只是显示为空白,而不会报错。
如果是常规写法, The current hero's name is {{currentHero.name}}
,js就会跑出 reference error
, TypeError: Cannot read property 'name' of null in [null].
angular还有很多强大的地方,我在这里就不一一列举了。要不是公司的项目规模太小,其实我还是挺想尝试使用 angular 来进行项目开发的。
不得不说,这种处理方式挺优雅的,和平时的写法并没有太大的差异,只需要多加一个 ? 就可以解决问题了。在 vue 和 react,我们暂时是用不上了,那就从函数方面入手吧。
简单粗暴的 reduce
先来看看一开始的需求吧,获取嵌套数据中的某个值,既然是嵌套数据,那么获取数据的时候其实也是一层一层展开的,那么我们就可以在展开的过程中把 undefined 或 null 设置成一个空对象,这么一来就算是 undefined 或 null,获取之后的值也不会抛出 Error TypeError
,而只会获取到一个空值。
先简单介绍一下 数组的 reduce 函数的用法吧,
Array.prototype.reduce = function(iterator, initValue) { } // iterator 迭代函数,(accumulator, currentValue, currentIndex, array) // accumulator 累加器,即函数上一次调用的返回值。第一次的时候为 initialValue || arr[0] // currentValue 数组中函数正在处理的的值。第一次的时候initialValue || arr[1] // currentIndex 数组中函数正在处理的的索引 // array 函数调用的数组 // initValue 虽然不传也行,但是如果数组类型和返回类型不同,我建议最好还是传一个默认值,避免报错 复制代码
函数的形式大概定义成:
// @params data 原始值,第一个值 // @params keys 之后的键名,以 , 隔开,省事,分开写的话还得多写不少引号 function getSafeReduce(data: any, keys: string | number): any 复制代码
初步阶段
为了防止传入的值为空,一定要对 data 和 keys 的值进行判断。
首先判断 data 是否有值,如果不存在,应赋予一个默认值。
其次判断 keys 是否为合法参数,主要判断是否为数字或者字符串,若不符合条件直接返回一个空对象。之后要对 keys 进行 split 操作,因为这个 字符串 才有的方法,为了避免输入数字时报错,还要将 keys 转为 字符串,这个方式就比较多了。
String(keys) '' + keys `${keys}`
想用哪种就用哪种吧,我个人还是比较推荐后两种。
function isExist(data) { return data !== undefined && data !== null; } function isNothing(data) { return !isExist(data); } function isVaild(data) { return typeof data === 'string' || typeof data === 'number'; } function getSafeReduce(data, keys) { if (isNothing(data)) { return defaultValue; } return isVaild(keys) && `${keys}`.split(",").reduce((item, key,) => { return item[key] || {} }, data) || {}; } // 测试 getSafeReduce(null, 's,1') 复制代码
进阶阶段
之前的代码其实已经可以达到一定的效果了,但是如果某一部分出现 undefined 或 null 的话,返回的结果就是 空对象,我们一般是想要返回一个null,或者是空字符串之类的,总之应该是自定义的,那么函数定义就应该稍微改变一下,增加一个参数来设置返回默认值了。
// @params data 原始值,第一个值 // @params keys 之后的键名,以 , 隔开,省事,分开写的话还得多写不少引号 // @params defaultValue: any,结果为空时的返回值 function getSafeReduce(data: any, keys: string | number, defaultValue: any): any 复制代码
数组的 reduce 方法第三个参数是数组的当前索引index,第四个参数是数组本身arr,我们可以通过 index === arr.length - 1
来判断是否为最终目标值。如果目标值不存在,则设置为默认值。
function getSafeReduce(data, keys, defaultValue,) { if (isNothing(data)) { return defaultValue; } return isVaild(keys) && `${keys}`.split(",").reduce((item, key, i, arr) => { let result = item[key.trim()]; if (isNothing(result)) { result = (i === arr.length - 1) ? defaultValue : {}; } return result; }, data); } 复制代码
简化阶段
上一步的 if 语句还可以再进行简化,也就是 reduce 函数可以改为:
let result = item[key.trim()]; result = isExist(result) ? result : ((i === arr.length - 1) ? defaultValue : {}); return result; 复制代码
上面的代码其实还可以再简化一下,最终就成为了:
function getSafeReduce(data, keys, defaultValue,) { if (isNothing(data)) { return defaultValue; } return isVaild(keys) && `${keys}`.split(",").reduce((item, key, i, arr) => (isExist(item[key.trim()]) ? item[key.trim()] : (i === arr.length - 1) ? defaultValue : {}), data); } 复制代码
总结
简化的代码不一定就是最好的,从可读性和可维护性考虑的话,倒数第二段代码我觉得应该算是达到一个相对平衡的代码。
不带调试的完整代码如下:
function isExist(data) { return data !== undefined && data !== null; } function isNothing(data) { return !isExist(data); } function isVaild(data) { return typeof data === 'string' || typeof data === 'number'; } function getSafeReduce(data, keys, defaultValue,) { if (isNothing(data)) { return defaultValue; } data = isExist(data) ? data : {}; return isVaild(keys) && `${keys}`.split(",").reduce((item, key, i, arr) => { let result = item[key.trim()]; result = isExist(result) ? result : ((i === arr.length - 1) ? defaultValue : {}); return result; }, data); } 复制代码
如果是发布到生产的话,上面代码已经可以达到要求了,但是如果是本地开发进行调试的话,我们既想让模板正常渲染,又想知道哪里出问题了,那就需要再增加一个调试的选项了。
带调试的完整代码如下:
function isExist(data) { return data !== undefined && data !== null; } function isNothing(data) { return !isExist(data); } function isVaild(data) { return typeof data === 'string' || typeof data === 'number'; } function getSafeReduce(data, keys, defaultValue, debug) { if (isNothing(data)) { debug && (console.error('传入的第一个参数为空', data)); return defaultValue; } data = isExist(data) ? data : {}; return isVaild(keys) && `${keys}`.split(",").reduce((item, key, i, arr) => { let result = item[key.trim()]; if (isNothing(result)) { result = (i === arr.length - 1) ? defaultValue : {}; debug && (console.error(item, `当前数据不存在键:${key}`)); } return result; }, data); } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
文明之光 (第三册)
吴军 / 人民邮电出版社 / 2015-1-1 / 59
【《文明之光》系列荣获由中宣部、中国图书评论学会和中央电视台联合推选的2014“中国好书”奖】 吴军博士从对人类文明产生了重大影响却在过去被忽略的历史故事里,选择了有意思的几十个片段特写,以人文和科技、经济结合的视角,有机地展现了一幅人类文明发展的宏大画卷。 《文明之光 》系列大致按照从地球诞生到近现代的顺序讲述了人类文明进程的各个阶段,每个章节相对独立,全景式地展现了人类文明发展历程......一起来看看 《文明之光 (第三册)》 这本书的介绍吧!
XML、JSON 在线转换
在线XML、JSON转换工具
正则表达式在线测试
正则表达式在线测试