Using Array.prototype.reduce() to Reduce Conceptual Boilerplate for Problems on Arrays译:使用Array.prototype.reduce()可以减少数组循环等繁琐问题
我们知道,for循环在javascript里面,是不精炼的。它会让你占用你的时间:缓存占用 、命名和逻辑错误。并且,写代码时,不用闭包循环钟的异步的过程代码会出现错误。见:Asynchronous Process inside a javascript for loop
这个标题已经告诉我们:你可以用reduce()方法去解决与数组相关的问题,而不是用for循环或者while循环。如果你希望继续读下去:首先要知道什么时“递归”和一些比较有用的数组方法Array.prototype.map()
或 Array.prototype.filter()。
实践大于真知。我们一起看看怎么习惯使用reduce()。需先提示:在学习之前确保你是来学习解决问题的,而不是匆匆一瞥。
Can I Reduce Any Array-related Problem?我能解决任何与数组相关的问题吗?
是的,可以!事实上,不仅仅能解决数组相关的问题,只要他是个问题,就可以解决。例如,创建一个很常见的链接地址标题:slug url,来自新闻、博客、甚至是问答论坛的大字标题。
function createSlug(str){ return str.split(" ").reduce(function(prev, next){ return prev.concat([next.toLowerCase()]);//That‘s the core of the functionality we want. }, []) .join("-"); }
自己测试一下在控制台,例如“Leo Finally Wins a Freaking Oscar!”:
leo-finally-wins-a-freaking-oscar!
But I Do NOT Understand Reduce At All!但是我根本不了解Reduce!!
好吧,不要害怕!在接下来的几分钟,你即将成为一个Reduce忍者。
每个JavaScript函数都有三件事你需要知道,理解函数是如何工作的:
- The input
- The output
- The execution context
是的,我可以看到你在新标签页中打开官方MDN文档!没关系,先读到这。我是认真的,这不是笑话。
Array.prototype.reduce()有两个参数:回调函数和初始值,都为输入参数。(初始值是很重要的。很多开发人员忘记提供初始值正确,最后搞砸了他们的代码)。
arr.reduce(function(){}, initialValue);//假设 arr 是一组随机数组。
现在,让我们仔细看看回调函数,也就是reduce()第一个参数,这个回调需要两个参数。在官方文档中,这两个的两个参数称为prev和next。就我个人而言,我不认为这两个参数的名称能代表这个参数本来的意义。
我把回调函数的第一个参数称为‘acc’,代表累计值(accumulated value);‘item’,代表当前被访问值。所以,我们的reduce()就像下面一样:
arr.reduce(function(acc, item){ /* here you have to complete the function */ }, initialValue);
我们前面提到过,reduce()接送一个函数作为累加器。我们来看看这些‘acc’和‘item’的值将会怎么变化。
var arr = [10, 20, 30, 60]; arr.reduce(function(acc, item){ console.log(acc, item); }, 0);
在浏览器或节点控制台执行上述会给你这个作为输出:
0 10 undefined 20 undefined 30 undefined 60
注意这些输出的数字,和数组[10, 20, 30, 60]的数字元素一样。实际上,reduce()把数组的元素打印出来了。
因此,我们可以推断出reduce()需要您的自定义回调并执行数组的每个元素。虽然这样做,它使当前项可用于自定义回调为‘item’参数。
但是acc呢?我们看到,除了第一行,当‘item’= 10,它是‘undefined’。在第一行,对应于第一个迭代iteration,它的值和初始值initialvalue,0,一样。
简而言之,我们的‘acc’是累加器,而不是正在累加中的值!但是,我们如何积累?让我们尝试执行:
var arr = [10, 20, 30, 60]; arr.reduce(function(acc, item){ console.log(acc, item); return acc; }, 0);
这一次,输出的变化:
0 10 0 20 0 30 0 60
正如您可以看到,‘acc’会保持不变的值。预计,我们不改变‘acc’在自定义回调的值。我们返回任何reduce()使得可用在给定迭代。
但是我们意识到什么,‘acc’为当前重复的值,将从自定义回调返回值从先前的重复工作。最终,重复工作结束后,返回的‘acc’的最终值将reduce()。
这使得只有一个重要的部分在我们的理解——执行上下文的价值,或者‘this’!所以,我们再次的方法我们的朋友,JS控制台和执行:
var arr = [10, 20, 30, 60]; arr.reduce(function(acc, item){ console.log(acc, item, this); return acc; }, 0);
如果在严格模式下,则用这个:
var arr = [10, 20, 30, 60]; arr.reduce(function(acc, item){ console.log(acc, item, this); return acc; }.bind(arr), 0);
我已经绑定arr数组本身,但是你可以将它设置为在您的环境中任何对象。
Understanding Reduce 理解reduce
我们来总结一下reduce函数的简单参考:
- reduce减少需要一个自定义回调作为它的第一个参数,和一些初始值作为第二个参数。
- 重要的是我们不要忘记第二个参数,初始值,我们显式地设置在使用它。
- 自定义回调函数的输入参数是累积值‘acc’;和数组的当前项,‘item’。
- 在当前迭代,在下一个迭代中‘acc’的值将返回值在回调。
- 使用reduce()的重要点是:正确的形成‘acc’,返回最终的函数调用。
Using Reduce
让我们开始一个简单的数组操作,找到数组中最大的值。为了简便起见,我假设这是一个整数数组中。
为了形成一个解决方案,我们需要考虑如何形成‘acc’的reduce让我们回调,遍历该数组。一个想法我觉得有用,是想到loop-invariants。我们要想出一个方方法,无论什么数组的大小或内容,‘acc’应该总是最大值。
我的数组是[20, 50, 5, 60]。经过两次迭代后,‘item’将是 5,‘acc’应该是max(20, 50) = 50。
var arr = [20, 50, 5, 60]; arr.reduce(function(acc, item){ return Math.max(acc, item); }, 0);
它可能容易重写如下,与函数式编程的原则:
var arr = [20, 50, 5, 60]; arr.reduce(Math.max, 0);