如何在JavaScript中深入查找一个对象并对结果进行处理

73 阅读3分钟

有时候,我们会遇到一个令人羡慕的情况,就是必须在JavaScript中深入搜索一个对象。我们怎样才能做到这一点并对结果采取一些行动呢?

我们可以利用递归回调函数的优势来使这个问题得到很好的解决。

一个样本问题

假设我们有以下对象,我们想对id = 7 对应的对象采取一些行动。

const obj = {
  some: {
    props: {
      drilling: {
        pretty: {
          deep: [
            {
              id: 7,
              name: 'Daffodil',
            },
          ],
        },
      },
    },
    more: {
      props: {
        leading: {
          to: 'nowhere',
        },
      },
    },
  },
};

我们的样本问题有一个重要的限制:所需对象的当前路径,obj.some.props.drilling.pretty.deep[0] ,并不保证永远是路径。我们需要一个强大的方法来潜入任何级别的深处,找到这个对象。

该方法

我们将创建一个递归函数,它需要三个参数。

  • 我们要遍历的对象
  • 一个matcher ,当我们找到正确的对象时,该函数将被评估为true
  • 一个回调函数,一旦我们找到该对象,它将处理该对象。
function deepFindCallback(obj, matcher, cb) {
  // Magic here
}

deepFindCallback 函数中,我们将做以下工作。

  • 在该对象上调用matcher 函数,看看我们是否找到了匹配的对象
  • 如果我们找到匹配的对象,就对其调用回调函数cb
  • 如果我们没有找到匹配的对象,则对当前对象的所有子对象递归地调用 deepFindCallback
function deepFindCallback(obj, matcher, cb) {
  // Call the matcher function
  // If match, call the callback
  // If not match, recursively call deepFindCallback
}

填充函数

根据我们的方法,我们只需要10行左右就可以完成我们的函数了。

function deepFindCallback(obj, matcher, cb) {
  // Call the matcher function
  if (matcher(obj)) {
    // If match, call the callback
    cb(obj);
  }
  // If not match, recursively call deepFindCallback
  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      deepFindCallback(obj[key], matcher, cb);
    }
  }
}

让我们试试吧我们的例子指出,我们想找到与id = 7 相关的对象,所以我们可以使用下面的matcher 函数。

const matcher = (obj) => obj.id === 7;

为了简单起见,我们的回调函数console.log ,与这个id 相关的name

const cb = (obj) => {
  console.log(obj.name);
};

综上所述,这就是我们得到的结果。

const obj = {
  some: {
    props: {
      drilling: {
        pretty: {
          deep: [
            {
              id: 7,
              name: 'Daffodil',
            },
          ],
        },
      },
    },
    more: {
      props: {
        leading: {
          to: 'nowhere',
        },
      },
    },
  },
};

function deepFindCallback(obj, matcher, cb) {
  if (matcher(obj)) {
    cb(obj);
  }
  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      deepFindCallback(obj[key], matcher, cb);
    }
  }
}

const matcher = (obj) => obj.id === 7;

const cb = (obj) => {
  console.log(obj.name);
};

deepFindCallback(obj, matcher, cb);
// "Daffodil"

正如预期的那样,我们记录了"Daffodil" ,这是与有id = 7 的对象相关的名称。

提前停止

在上面的例子中,我们的递归函数在找到我们要找的对象时不会停止。如果我们要寻找多个出现的对象,这可能是我们想要的;但是,我们经常只寻找一个出现的对象。如果是这种情况,我们可以调整我们的函数以包含一些关于是否找到该对象的上下文。

function deepFindCallback(obj, matcher, cb) {
  let found = false;
  finder(obj, matcher, cb);
  function finder(obj, matcher, cb) {
    if (found) {
      return;
    }
    if (matcher(obj)) {
      cb(obj);
      found = true;
      return;
    }
    for (let key in obj) {
      if (typeof obj[key] === 'object') {
        finder(obj[key], matcher, cb);
      }
    }
  }
}

在这种情况下,我们添加一个found 变量,然后在我们的deepFindCallback 中添加一个新的函数。创建这个新函数是因为我们需要found ,使其处于我们的递归函数的范围之外。在递归函数中,如果我们的对象已经被找到,我们就跳出,试图尽可能少地执行递归。

递归和回调是你的朋友

我发现,JavaScript中的递归和回调函数往往是一个强大的组合。在这种情况下,这种组合可以帮助我们任意地深入到一个对象,并对找到的对象采取我们想要的任何行动。