如何检查深层嵌套的属性

6
我经常发现在映射数组之前,需要构建长链以检查它是否已定义:
this.props.photos &&
this.props.photos.activePhotos &&
this.props.photos.activePhotos.map(...

如果我删除this.props.photos &&this.props.photos.activePhotos.length &&,则如果photosactivePhotos未定义,则整个应用程序将崩溃。
有没有一种方法可以检查这些props,而不必检查我的最终项的每个父对象/数组?

如果photos可以是未定义或数组,则length是多余的。 - Estus Flask
检查 this.props.photos.activePhotos.length 有什么目的? - guest271314
6个回答

9

2020年1月更新

根据TC39提案可选链不久将在JavaScript标准中推出(目前处于阶段4)。

使用的语法如下:

const active = this.props?.photos?.activePhotos

或者在您的情况下,可以采用以下方式:
(this.props?.photos?.activePhotos || []).map(...

在此期间,您可能想要查看Typescript或js编译器,以尝试最新的ES功能。


旧答案

另一种选择是在解构props时为其使用默认值:

  const { photos = {} } = this.props
  const { activePhotos = [] } = photos
  activePhotos.map(/*   */)

在这种情况下,如果photos未定义,则会被替换为空对象。试图从中获取activePhotos将给您一个空数组,使您可以在任何情况下对其进行映射。

在那种情况下为什么要使用 const - guest271314
2
const is the attribute you should give by default to your variables unless you plan on modifying them, in which case you will want to use let. I won't mention the terrible use of var. Since it does not seem like the OP modifies his props (he should not anyway) I used const - Treycos

4

1
我看到两种可能的方法,根据嵌套的级别不同。
#1. 如果你有很多嵌套的属性级别:
你可以使用lodash.get
以下是如何仅在activePhotos存在时呈现它们的方法:
// Please note how do we pass default `[]` as third parameter
// in order to not break the `.map` function
_.get(this.props, 'photos.activePhotos', []).map(...)

如果您只想检查深度嵌套的pros,那么您可以使用lodash.has方法:

// Will return `true` / `false`
_.has(this.props, 'photos.activePhotos')

#2. 如果嵌套级别不超过2-3级:

只需使用本地的ES6解构赋值+默认值特性即可。

const { photos = {} } = this.props
const { activePhotos = [] } = photos

// Now you can safely map over the `activePhotos`
activePhotos.map(...)

0
有没有一种方法可以在不检查我的最终项的每个父对象/数组的情况下检查这些道具?
一般来说,没有。
不清楚为什么该问题的代码要检查潜在的长度。
如果目标是减少代码长度,可以使用JSON.stringify()和RegExp
if (/"activePhotos":\[.*\]/.test(JSON.stringify(this.props))) // do stuff

或者如果首选方法是使用 AND 运算符

/"activePhotos":\[.*\]/.test(JSON.stringify(this.props)) && // do stuff

注意,此处未检查“每个父对象”,因为期望“activePhotos”仅是“photos”属性的一部分,我们可以忽略检查“photos”属性以满足要求。 - guest271314

0

undefsafe 是一个足够好用的库。还有很多其他的库可供使用。

以下是如何使用它的简单示例:

var object = {
  a: {
    b: {
      c: 1,
      d: [1,2,3],
      e: 'remy'
    }
  }
};

console.log(undefsafe(object, 'a.b.e')); // "remy"
console.log(undefsafe(object, 'a.b.not.found')); // undefined

-1

这里是一种使用默认值返回的可选链式函数式方法。该方法使用了Maybe单子和Proxy

使用wrap()函数来包装对象,以便您可以安全地访问任何属性。在内部,wrap创建一个代理来管理缺失值,并使用Maybe包装器。在链的末尾,通过将getOrElse(default)与默认值链接起来,您可以解包值,当属性访问失败时返回默认值:

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)) // 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)) // out-of-bounds index: returns -1
console.log(wrap(obj).c.getOrElse(() => 'no')()) // returns 'yes'
console.log(wrap(obj).d.getOrElse(() => 'no')()) // returns 'no'

请参阅此博客文章此链接,了解有关Maybe单子和代理使用的更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接