简体   繁体   English

累加器 function 内部 reduce 被 Javascript 中的 function 包裹,使用闭包

[英]Accumulator function inside reduce is wrapped by a function in Javascript using closure

var arr = [{ name: "John", score: "8.8" }, { name: "John", score: "8.6" }, { name: "John", score: "9.0" }, { name: "John", score: "8.3" }, { name: "Tom", score: "7.9" }],
    avgScore = arr.reduce(function (sum, count) {
        return function (avg, person) {
            if (person.name === "John") {
                sum += +person.score;
                return sum / ++count;
            }
            return avg;
        };
    }(0, 0), 0);

console.log(avgScore);

I found this interesting code and I was wondering how closure exactly worked in Javascript.我发现了这个有趣的代码,我想知道闭包在 Javascript 中究竟是如何工作的。 I was taken aback by the fact that the function with the accumulator and the iterated element is wrapped by another function.我对带有累加器和迭代元素的 function 被另一个 function 包裹这一事实感到惊讶。 Isn't reduce supposed to accept a function with the the accumulator and the iterated element, then how come the reduce function still works despite the fact that the accumulator function with the iterated element with avg is wrapped by another function? Isn't reduce supposed to accept a function with the the accumulator and the iterated element, then how come the reduce function still works despite the fact that the accumulator function with the iterated element with avg is wrapped by another function?

Also, how come we call the function with a closure using (0,0), but in the second iteration we call it with the updated sum and count (sum, 1).另外,为什么我们用 (0,0) 的闭包来调用 function,但是在第二次迭代中,我们用更新的 sum 和 count (sum, 1) 调用它。 Shouldn't a closure use the arguments (0, 0) over and over again?闭包不应该一遍又一遍地使用 arguments (0, 0) 吗?

That is a very obfuscated way to use reduce .这是使用reduce的一种非常模糊的方式。 But it does work, however.但它确实有效。

The function (sum, count) is instantly invoked with (0,0) , returning the function (avg, person) , which is then used by the reduce for each element, starting with the accumulator 0 , and returning a new average for every iteration even though only the last value is actually used. function (sum, count)立即使用(0,0)调用,返回function (avg, person) ,然后由reduce用于每个元素,从累加器0开始,并为每个元素返回一个新的平均值即使只实际使用了最后一个值,也会进行迭代。 It works by updating the sum and count variables within the closure for every iteration.它通过为每次迭代更新闭包内的sumcount变量来工作。

A more readable way to calculate the average using reduce would be:使用reduce计算平均值的更易读的方法是:

const result = arr.reduce(function (acc, person) {
  if (person.name === "John") {
    return {
        sum: acc.sum + parseFloat(person.score),
        count: acc.count + 1
    }
  }
  return acc
}, { sum: 0, count: 0 })
console.log(result.sum / result.count)

But since the point is to just calculate the average score of a person, an even more readable and even shorter way would be:但由于重点只是计算一个人的平均分数,因此更具可读性甚至更短的方法是:

const johnsScores = arr.filter(person => person.name === 'John')
const total = johnsScores.reduce((acc, person) => acc + parseFloat(person.score), 0)
console.log(total / johnsScores.length)

Seems like a bit of a complex way to determine the average score of John.确定约翰的平均分数似乎有点复杂。 It uses the closure to be able to conditionally increment the value of count .它使用闭包能够有条件地增加count的值。 I've tried to explain it using logs of the running values.我试图使用运行值的日志来解释它。

Anyway the average determination could be hugely simplified (see second part of the snippet)无论如何,平均确定可以大大简化(见片段的第二部分)

 const arr = [ { name: "John", score: "8.8" }, { name: "John", score: "8.6" }, { name: "John", score: "9.0" }, { name: "John", score: "8.3" }, { name: "Tom", score: "7.9" } ]; // the outer function delivers the inner function // the outer function uses 0, 0 as input initially // The outer function does nothing with those values // and it returns not a value, but the inner function. // The inner function is run every iteration of reduce // and in there the initial closed over values are // manipulated. That inner function returns the actual // accumulator value (a running average) const avgScoreDemo = arr.reduce(function(sum, count) { let outerValues = `outer sum: ${sum}, outer count: ${count}`; return function(avg, person) { console.log(outerValues + ` avg: ${avg}, inner sum ${sum}, inner count: ${count}`); if (person.name === "John") { sum += +person.score; return sum / ++count; } return avg; }; }(0, 0), 0); // This can be simplified to const avgScore = arr.reduce((sum, person) => sum + (person.name === "John"? +person.score: 0), 0) / arr.filter(v => v.name === "John").length; console.log(avgScore); // or using filter/index value const avgScore2 = arr.filter( v => v.name === "John" ).reduce( (acc, person, i) => ({...acc, sum: acc.sum + +person.score, average: (acc.sum + +person.score) / ++i }), { average: 0, sum: 0 } ).average; console.log(avgScore2);
 .as-console-wrapper { top: 0; max-height: 100%;important; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM