下划线的最大函数实现

时间:2014-11-09 10:28:58

标签: javascript performance underscore.js

我正在代码审查一些流行的js,我开始使用underscore.js。

现在我正在分析_.max函数:

  _.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
    value, computed;
if (iteratee == null && obj != null) {
  obj = obj.length === +obj.length ? obj : _.values(obj);
  for (var i = 0, length = obj.length; i < length; i++) {
    value = obj[i];
    if (value > result) {
      result = value;
    }
  }
} else {
  iteratee = _.iteratee(iteratee, context);
  _.each(obj, function(value, index, list) {
    computed = iteratee(value, index, list);
    if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
      result = value;
      lastComputed = computed;
    }
  });
}
return result;
};

我不明白为什么使用它

computed > lastComputed || computed === -Infinity && result === -Infinity

而不是

computed > lastComputed

在提供iteratee的if条件下

我认为如果只使用&#34;计算&gt;它会更高效(非常少) lastComputed&#34; 在任何情况下,-Infinity出现在集合中(一个,一些或所有元素)。

如果我错了,我想知道是什么。

谢谢, Pablo Benito

2 个答案:

答案 0 :(得分:2)

它可能会或可能不会表现得更好,但肯定会执行不同。显然,Underscore的作者希望它能够执行它的表现。

其执行方式不同的一种方式是(错名)iteratee返回-Infinity的单元素集合:

&#13;
&#13;
var yourMax = function(obj, iteratee, context) {
  var result = -Infinity, lastComputed = -Infinity,
      value, computed;
  if (iteratee == null && obj != null) {
    obj = obj.length === +obj.length ? obj : _.values(obj);
    for (var i = 0, length = obj.length; i < length; i++) {
      value = obj[i];
      if (value > result) {
        result = value;
      }
    }
  } else {
    iteratee = _.iteratee(iteratee, context);
    _.each(obj, function(value, index, list) {
      computed = iteratee(value, index, list);
      if (computed > lastComputed) {
        result = value;
        lastComputed = computed;
      }
    });
  }
  return result;
};

var result;
var entries = [{name: 'foo', value: 42}];
result = _.max(entries, function(entry){
  return entry.name === 'foo' ? -Infinity : entry.value;
});
snippet.log("_.max Result: " + result.name);

result = yourMax(entries, function(entry){
  return entry.name === 'foo' ? -Infinity : entry.value;
});
snippet.log("yourMax Result: " + result.name);
&#13;
<!-- Temporary snippet object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="http://jashkenas.github.io/underscore/underscore-min.js"></script>
&#13;
&#13;
&#13;

如果您想对其进行微观优化,那么不会改变其工作方式的更改就是在<{em} {/ 1}}回调中声明computed 而不是在包含范围内,因为它只在该回调中使用过。理论上,在解析_.each时,引擎必须首先查看绑定调用上下文的绑定对象,然后在那里找不到它,查看外部绑定对象以找到它。因此,通过将其移动到实际使用的位置,我们让引擎立即找到它而不是最初的错过:

computed

增益很小,但可以察觉:http://jsperf.com/move-variable-closer-to-use

enter image description here

答案 1 :(得分:0)

谢谢@ T.J。克劳德,现在我明白了它的区别。

if条件为:

( computed > lastComputed || computed === -Infinity && result === -Infinity )

你有一个包含一个或多个元素的集合,如下面的entries1:

var
    entries1 = [
        { prop1: -Infinity, prop2: 0 },
        { prop1: -Infinity, prop2: 1 },
        { prop1: -Infinity, prop2: 2 },
        { prop1: -Infinity, prop2: 3 } 
    ],
    entries2 = [
        { prop1: 0, prop2: 0 },
        { prop1: 0, prop2: 1 },
        { prop1: 0, prop2: 2 },
        { prop1: 0, prop2: 3 } 
    ],
    iteratee = function ( entry ) { return entry.prop1; };

所有返回-Infinity何时在iteratee函数中进行评估,正在讨论的条件:

( computed > lastComputed || computed === -Infinity && result === -Infinity )

,让至少返回一个元素,第一个元素。

我建议的条件:

( computed > lastComputed )

仅返回initialValue,表示-Infinity。

对于原始条件更好的是,条目2的行为是相同的, 我的意思是,返回第一个元素。所以,这是一个很好的解决方案

我只做一点观察,这是结果的初始化值,

_.max2 = function(obj, iteratee, context) {
    var
      resultInitialization = {},
      result = resultInitialization, lastComputed = -Infinity,
  value, computed;
if (iteratee == null && obj != null) {
  obj = obj.length === +obj.length ? obj : _.values(obj);
  for (var i = 0, length = obj.length; i < length; i++) {
    value = obj[i];
    if (value > result) {
      result = value;
    }
  }
} else {
  iteratee = _.iteratee(iteratee, context);
  _.each(obj, function(value, index, list) {
    computed = iteratee(value, index, list);
    if ( computed > lastComputed ) {
      result = value;
      lastComputed = computed;
    }
  });

  result = ( result !== resultInitialization ) ? result : _.values( obj )[ 0 ];
}
return result;

};

使用此版本,与原始版本一样,将返回entries1和entries2的第一个元素,并且当提供空列表时,它返回undefined,可能在语义上优于-Infinity。