有时候,我们会遇到一个令人羡慕的情况,就是必须在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中的递归和回调函数往往是一个强大的组合。在这种情况下,这种组合可以帮助我们任意地深入到一个对象,并对找到的对象采取我们想要的任何行动。