我正在代码审查一些流行的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
。
我认为如果只使用&#34;计算&gt;它会更高效(非常少) lastComputed&#34; 在任何情况下,-Infinity出现在集合中(一个,一些或所有元素)。
如果我错了,我想知道是什么。
谢谢, Pablo Benito
答案 0 :(得分:2)
它可能会或可能不会表现得更好,但肯定会执行不同。显然,Underscore的作者希望它能够执行它的表现。
其执行方式不同的一种方式是(错名)iteratee
返回-Infinity
的单元素集合:
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;
如果您想对其进行微观优化,那么不会改变其工作方式的更改就是在<{em} {/ 1}}回调中声明computed
而不是在包含范围内,因为它只在该回调中使用过。理论上,在解析_.each
时,引擎必须首先查看绑定调用上下文的绑定对象,然后在那里找不到它,查看外部绑定对象以找到它。因此,通过将其移动到实际使用的位置,我们让引擎立即找到它而不是最初的错过:
computed
增益很小,但可以察觉:http://jsperf.com/move-variable-closer-to-use
答案 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。