javascript:只返回if if not false

时间:2012-09-26 23:16:30

标签: javascript object

场景:我正在深层对象中搜索特定对象。我正在使用一个遍历孩子的递归函数,并询问他们是否正在搜索它们,或者我是否正在寻找他们的孩子或孙子等等。找到后,将返回找到的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:

3 个答案:

答案 0 :(得分:3)

虽然你所拥有的解决方案可能是“ best ”,但就搜索算法而言,我不一定会建议改变它(或者我会改变它以使用地图而不是算法),这个问题对我很有意思,特别是与JavaScript语言的功能属性有关,我想提供一些想法。

方法1

以下应该可以工作而不必在函数中显式声明变量,尽管它们被用作函数参数。它也很简洁,虽然有点简洁。

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];
};​

工作原理:

  1. 我们测试是否this.id == match_id,如果是,请返回this
  2. 我们使用map(通过Array.prototype.map)将this转换为“找到的项目”数组,这些项目是使用find方法的递归调用找到的。 (据说,其中一个递归调用将返回我们的答案。那些不会产生答案的将返回undefined。)
  3. 我们会过滤“找到的项目”数组,以便删除数组中的所有undefined结果。
  4. 我们返回数组中的第一个项目,并将其命名为quits。
    • 如果数组中没有第一项,则会返回undefined
  5. 方法2

    解决此问题的另一种尝试可能如下所示:

    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];
    };
    

    工作原理:

    1. buildObjArray构建一个包含objobj所有孩子的单个大型1维数组。
    2. 然后我们filter根据条件中数组中的对象必须id match_id的条件。
    3. 我们返回第一场比赛。
    4. 方法1 方法2 虽然有趣,但它们具有性能劣势,即使在找到匹配的ID后,它们仍会继续搜索。他们没有意识到他们在搜索结束之前已经拥有了他们需要的东西,而且效率不高。

      方法3

      当然可以提高效率,现在我觉得这个真的很接近你感兴趣的东西。

      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;
          }
      };​
      

      工作原理:

      1. 我们将整个find函数包装在try / catch块中,以便在找到项目后,我们可以throw并停止执行。
      2. 我们在find内创建了一个内部try函数(IIFE),我们引用它来进行递归调用。
      3. 如果this.id == match_id,我们throw this,请停止我们的搜索算法。
      4. 如果不匹配,我们会递归调用每个孩子的find
      5. 如果匹配,我们的throw块会捕获catch,并返回found对象。
      6. 由于此算法能够在找到对象后停止执行,因此它的性能将接近您的算法,尽管它仍然具有try / catch块的开销(在旧版本中)浏览器可能很昂贵)forEach比典型的for循环慢。这些都是非常小的性能损失。

        方法4

        最后,尽管此方法不符合您的请求范围,但如果可能在您的应用程序中,则 更好的性能,以及需要考虑的事项。我们依赖于映射到对象的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; }

我不太确定,如果你仍然会称之为递归。