如何处理可能未定义的数据上的调用函数?

IT技术 javascript reactjs
2021-03-27 02:26:25

我主要使用 React 并且经常发现,当我编写一个依赖于组件状态的函数时,我必须在执行任何操作之前执行检查以查看该状态是否已定义。

例如:我有一个函数,用于.map()遍历从数据库中获取的对象数组,并为数组中的每个对象生成 jsx。这个函数是在render()我的组件函数中调用的第一次render()调用,初始数组为空。这会导致错误,因为当然,数组的第一个索引未定义。

我一直在通过进行条件检查以查看数组的值是否未定义来规避这一点。每次编写 if 语句的过程都感觉有点笨拙,我想知道是否有更好的方法来执行此检查或完全避免它的方法。

2个回答

在使用 map 之前检查数组:

arr && arr.map()

或者,

arr && arr.length && arr.map() // if you want to map only if not empty array

或者,

我们甚至可以这样使用(正如devserkan所评论的):

(arr || []).map()

根据您的评论:

我希望有一个像 C# 一样的安全导航操作符 (arr?.map())

是的,很明显。在 JavaScript 中称为可选链,目前仍在提案中。如果它被接受,那么你可以像这样使用:

arr?.map()

您可以在阶段 1 中看到它,您可以使用babel preset stage1


但显然,除了检查数组长度外,您的要求将无法满足:

这会导致错误,因为当然,数组的第一个索引未定义。

所以,我建议你使用:

arr && arr.length && arr.map()
我希望有一个像 C# 一样的安全导航操作符 ( arr?.map())
2021-06-09 02:26:25
哇,可选链接很好。对于这个问题( arr || [] ).map呢?
2021-06-13 02:26:25

你在这里真正需要的是所谓的可选链接

obj?.a?.b?.c // no error if a, b, or c don't exist or are undefined/null

?.是存在的运营商,它可以让你安全地访问属性,如果缺少该属性不会抛出。然而,可选链还不是 JavaScript 的一部分,但已被提出并处于第 3 阶段,请参阅 TC39 的状态 3

但是,使用代理和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

2019 年更新:可选链提案现已进入第 3 阶段!
2021-05-25 02:26:25