场景:我正在深层对象中搜索特定对象。我正在使用一个遍历孩子的递归函数,并询问他们是否正在搜索它们,或者我是否正在寻找他们的孩子或孙子等等。找到后,将返回找到的obj,否则返回false。基本上这个:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
我想知道,有什么比这简单吗?:
var result = this[i].find(match_id);
if (result) return result;
让我把结果存储在变量中(在每个级别上!)让我很烦,我只想检查它是否为false并返回结果。我也考虑过以下情况,但出于显而易见的原因,我更加不喜欢它。
if (this[i].find(match_id)) return this[i].find(match_id);
顺便说一下,我也在想,这种做法是否“递归”?它真的不是那么自称......
非常感谢。
[编辑]
在if语句中使用另一个函数check_find
(只有在找到时才返回true)还有另一种可能性。在一些非常复杂的情况下(例如,您不仅找到对象,而且还要更改它),这可能是最好的方法。还是我错了? d:
答案 0 :(得分:3)
虽然你所拥有的解决方案可能是“ best ”,但就搜索算法而言,我不一定会建议改变它(或者我会改变它以使用地图而不是算法),这个问题对我很有意思,特别是与JavaScript语言的功能属性有关,我想提供一些想法。
以下应该可以工作而不必在函数中显式声明变量,尽管它们被用作函数参数。它也很简洁,虽然有点简洁。
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
工作原理:
this.id == match_id
,如果是,请返回this
。map
(通过Array.prototype.map
)将this
转换为“找到的项目”数组,这些项目是使用find
方法的递归调用找到的。 (据说,其中一个递归调用将返回我们的答案。那些不会产生答案的将返回undefined
。)undefined
结果。undefined
。解决此问题的另一种尝试可能如下所示:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
工作原理:
buildObjArray
构建一个包含obj
和obj
所有孩子的单个大型1维数组。filter
根据条件中数组中的对象必须id
match_id
的条件。方法1 和方法2 虽然有趣,但它们具有性能劣势,即使在找到匹配的ID后,它们仍会继续搜索。他们没有意识到他们在搜索结束之前已经拥有了他们需要的东西,而且效率不高。
当然可以提高效率,现在我觉得这个真的很接近你感兴趣的东西。
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
工作原理:
find
函数包装在try
/ catch
块中,以便在找到项目后,我们可以throw
并停止执行。find
内创建了一个内部try
函数(IIFE),我们引用它来进行递归调用。this.id == match_id
,我们throw this
,请停止我们的搜索算法。find
。throw
块会捕获catch
,并返回found
对象。由于此算法能够在找到对象后停止执行,因此它的性能将接近您的算法,尽管它仍然具有try
/ catch
块的开销(在旧版本中)浏览器可能很昂贵)forEach
比典型的for
循环慢。这些都是非常小的性能损失。
最后,尽管此方法不符合您的请求范围,但如果可能在您的应用程序中,则多, 更好的性能,以及需要考虑的事项。我们依赖于映射到对象的id的映射。它看起来像这样:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
这样,根本不需要find
方法!
使用此方法在应用程序中的性能提升将是巨大的。如果可能的话,请认真考虑。
但是,每当您不再引用该对象时,请小心从地图中删除该对象。
delete map[obj.id];
这是防止内存泄漏的必要条件。
答案 1 :(得分:2)
没有其他明确的方法,将结果存储在变量中并不是很麻烦,实际上这就是变量的用途。
是的,这种方法是递归的:
if (this.id==match_id) return this
obj.find(match_id) { ... var result = this[i].find(match_id); }
答案 2 :(得分:1)
我没有看到任何理由,为什么存储变量会很糟糕。它不是副本,而是参考,因此效率很高。另外临时变量是唯一的方法,我现在可以看到(虽然我可能错了)。
考虑到这一点,我不认为方法check_find
会非常有意义(它可能基本上是相同的实现),所以如果你真的需要这个check_find
方法,我将它实现为
return this.find(match_id) !== false;
该方法是否递归很难说。 基本上,我会说是的,因为'find'的实现对于每个对象都是相同的,所以它与
几乎相同function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
绝对是递归的(函数自称)。
但是,如果你做了
onesingleobjectinmydeepobject.find = function(x) { return this; }
我不太确定,如果你仍然会称之为递归。