我主要使用React,并且经常发现当我编写依赖于组件状态的函数时,必须执行检查以查看状态是否已定义,然后再执行任何操作。
例如:我有一个函数,该函数使用.map()
循环遍历从数据库中获取的对象数组,并为数组中的每个对象生成jsx。我的组件的render()
函数中调用了此函数。第一次调用render()
时,初始数组为空。这会导致错误,因为,当然,数组的第一个索引是未定义的。
我一直在通过有条件检查来检查数组的值是否未定义来规避此问题。每次编写一个if语句的过程都有些笨拙,我想知道是否有更好的方法来执行此检查或完全避免检查。
答案 0 :(得分:4)
在使用地图之前检查数组:
arr && arr.map()
OR
arr && arr.length && arr.map() // if you want to map only if not empty array
OR
我们甚至可以这样使用(如devserkan所述):
(arr || []).map()
根据您的评论:
我希望有像C#(arr?.map())这样的安全导航操作符
是的,显然。仍在提议中的JavaScript中将其称为optional chaining。如果接受,则可以这样使用:
arr?.map()
您可以在staging 1中看到它,您可以使用babel preset stage1
但是很明显,除了检查数组的长度,将无法满足您的要求:
这会导致错误,因为,当然,数组的第一个索引未定义。
所以,我建议您使用:
arr && arr.length && arr.map()
答案 1 :(得分:0)
您在这里实际需要的称为可选链接:
obj?.a?.b?.c // no error if a, b, or c don't exist or are undefined/null
?.
是存在运算符,它使您可以安全地访问属性,并且在缺少属性时也不会抛出该属性。但是,可选链接尚不是JavaScript的一部分,但已提出see State 1 of TC39。
但是,使用代理和Maybe
类,您可以实现可选链接,并在链接失败时返回默认值。
wrap()
函数用于包装要在其上应用可选链接的对象。在内部,wrap
在对象周围创建代理,并使用Maybe
包装器管理缺失值。
在链的末尾,您可以通过将getOrElse(default)
与默认值链接来解开值,该默认值在链无效时返回:
const obj = {
a: 1,
b: {
c: [4, 1, 2]
},
c: () => 'yes'
};
console.log(wrap(obj).a.getOrElse(null)) // returns 1
console.log(wrap(obj).a.b.c.d.e.f.getOrElse(null)) // returns null
console.log(wrap(obj).b.c.getOrElse([])) // returns [4, 1, 2]
console.log(wrap(obj).b.c[0].getOrElse(null)) // returns 4
console.log(wrap(obj).b.c[100].getOrElse(-1)) // returns -1
console.log(wrap(obj).c.getOrElse(() => 'no')()) // returns 'yes'
console.log(wrap(obj).d.getOrElse(() => 'no')()) // returns 'no'
wrap(obj).noArray.getOrElse([1]).forEach(v => console.log(v)) // Shows 1
wrap(obj).b.c.getOrElse([]).forEach(v => console.log(v)) // Shows 4, 1, 2
完整的示例:
class Maybe {
constructor(value) {
this.__value = value;
}
static of(value){
if (value instanceof Maybe) return value;
return new Maybe(value);
}
getOrElse(elseVal) {
return this.isNothing() ? elseVal : this.__value;
}
isNothing() {
return this.__value === null || this.__value === undefined;
}
map(fn) {
return this.isNothing()
? Maybe.of(null)
: Maybe.of(fn(this.__value));
}
}
function wrap(obj) {
function fix(object, property) {
const value = object[property];
return typeof value === 'function' ? value.bind(object) : value;
}
return new Proxy(Maybe.of(obj), {
get: function(target, property) {
if (property in target) {
return fix(target, property);
} else {
return wrap(target.map(val => fix(val, property)));
}
}
});
}
const obj = { a: 1, b: { c: [4, 1, 2] }, c: () => 'yes' };
console.log(wrap(obj).a.getOrElse(null))
console.log(wrap(obj).a.b.c.d.e.f.getOrElse(null))
console.log(wrap(obj).b.c.getOrElse([]))
console.log(wrap(obj).b.c[0].getOrElse(null))
console.log(wrap(obj).b.c[100].getOrElse(-1))
console.log(wrap(obj).c.getOrElse(() => 'no')())
console.log(wrap(obj).d.getOrElse(() => 'no')())
wrap(obj).noArray.getOrElse([1]).forEach(v => console.log(v)) // Shows 1
wrap(obj).b.c.getOrElse([]).forEach(v => console.log(v)) // Shows 4, 1, 2