假如你刚接触JavaScript可能你还没有听说过.map()
,.reduce()
,.filter()
。或者者听说过,看过别人用过但是自己在实际项目中没有用过。在国内很多开发项目都是需要考虑IE8的兼容,为了兼容很多JavaScript好用的方法和技巧都被埋没了。但是我发现近几年开始,很多开发项目已经完全抛弃了IE这个魔鬼了。假如你不需要兼容“石器时代”的IE浏览器了,那就要开始熟习一下这几个方法来解决数组。
注意这遍文章说的的3个方法其实在很多其余语言都可以使用到,由于这几个方法和使用概念在很多其余语言都是存在的。
让我用一个简单的例子告诉你如何使用这个方法。如果你现在有多对象的数组数据 - 每一个对象代表着一个员工的信息。现在你想要的最终结果就是取出所有员工的唯一ID值。
// 员工数据var employees = [ { id: 20, name: 'Captain Piett' }, { id: 24, name: 'General Veers' }, { id: 56, name: 'Admiral Ozzel' }, { id: 88, name: 'Commander Jerjerrod' }];// 你想要的结果[20, 24, 56, 88]
其实要实现这个结果有很多数组解决方式。传统的解决方法就是先定义一个空数组,而后使用.forEach()
,.for(...of)
,或者者是最简单的.for()
来组装ID到你定义的数组里面。
我们来比照一下传统的解决方式和.map()
的区别。
使用.forEach()
:
var employeeIds = [];employees.forEach(function (employee) { employeeIds.push(officer.id);});
注意使用传统的方式,我们必需有一个预约义的空数组变量才行。但是假如是.map()
就会更简单了。
var employeeIds = employees.map(function (employee) { return employee.id});
甚至我们可以用更简洁的方式,使用箭头方法(但是需要ES6支持,Babel,或者者TypeScript)。
const employeeIds = employees.map(employee => employee.id);
所以.map()
究竟是怎样运作的呢?这个方法有两个参数,第一是回调方法,第二是可选内容(会在回调方法中做为this
)。数组里的每个数值/对象会被循环进入到回调方法
里面,而后返回新的数值/对象
到结果数组里面。
注意结果数组的长度永远都会和被循环的数组的长度一致。
与.map()
相识,.reduce()
也是循环一个回调方法,数组里面的每一个元素对回进入回调方法。区别是回调方法返回的值会被传递到下一个回调方法,如此类推(等同于一个累加器)。
.reduce()
里的累加值可以是任何属性的值,包括integer
,string
,object
等等。这个累加值会被实力化或者者传递到下一个回调方法。
来上代码,做个简单的例子!如果你有一个飞机师的数组,数组里面有每个飞机师的工龄。
var pilots = [ { id: 10, name: "Poe Dameron", years: 14, }, { id: 2, name: "Temmin 'Snap' Wexley", years: 30, }, { id: 41, name: "Tallissan Lintra", years: 16, }, { id: 99, name: "Ello Asty", years: 22, }];
现在我们需要知道所有飞机师累计的总工龄。使用.reduce()
就是比吃饭还简单的事情。
var totalYears = pilots.reduce(function (accumulator, pilot) { return accumulator + pilot.years;}, 0);
注意我这里第二个参数我传了0。第二个参数是一个累加值的初始值。当然假如场景需要这个初始值也可以传入一个变量或者者你需要的值。循环了数组里的每一个元素后,reduce方法会返回最终累加后的值(在我们这个例子中就是82
)。
例子里面的
acc
和accumulator
就是累加值变量
假如是使用ES6箭头写法,我们可以写的更加优雅简洁。一行即可以搞掂的事情!
const totalYears = pilots.reduce((acc, pilot) => acc + pilot.years, 0);
现在假如我们需要找到哪一位是最有经验的飞机师。这种情况我们一样可以使用.reduce()
。
var mostExpPilot = pilots.reduce(function (oldest, pilot) { return (oldest.years || 0) > pilot.years ? oldest : pilot;}, {});
这里我把accumulator
变量改为oldest
代表飞机师里面的老司机。这时候reduce里面的回调方法比照每一个飞机师,每一次飞机师的值进入这个回调方法,工龄更高的就会覆盖oldest
变量。最终循环后得到的oldest
就是工龄最高的飞机师。
通过这几个例子,你可以看到使用.reduce()
可以简单又优雅的在一个数组里面获取到单个最终值或者者对象。
假如你现在的场景是需要在一个数组里面过滤一部分的数据,这个时候.filter()
就是你的最好的朋友了。
我们用回飞机师的数据,并且加入了所属航空公司的值:
var pilots = [ { id: 2, name: "Wedge Antilles", faction: "Rebels", }, { id: 8, name: "Ciena Ree", faction: "Empire", }, { id: 40, name: "Iden Versio", faction: "Empire", }, { id: 66, name: "Thane Kyrell", faction: "Rebels", }];
加入现在我们想分别挑选出Rebels
和Empire
两个航空公司的飞机师,使用.filter()
就是轻而易举的事情!
var rebels = pilots.filter(function (pilot) { return pilot.faction === "Rebels";});var empire = pilots.filter(function (pilot) { return pilot.faction === "Empire";});
就这么简单,假如使用箭头方法(ES6)就更加优雅了:
const rebels = pilots.filter(pilot => pilot.faction === "Rebels");const empire = pilots.filter(pilot => pilot.faction === "Empire");
其实原理很简单,只需你的回调方法返回的是true
,这个值或者者对象就会在新的数组里面了。假如返回的是false
就会被过滤掉了。
既然我们刚刚学到的三个函数都是可以用于数组的,并且.map()
和.filter()
都是返回数组的。那我们即可以串联起来使用。不说多了上代码试试!
我们用一个有趣一点的数据实验一下,如果现在我们有一个星球大战
里面的人物
的数组。每个字段的定义如下:
Id
: 人物唯一IDname
: 人物名字pilotingScore
: 飞行能力指数shootingScore
: 射击能力指数isForceUser
: 能否拥有隔空操控能力
我们的目标:获取拥有隔空操控能力的飞行员的总飞行能力指数
。我们先分开一步一步实现这个目标!
var jediPersonnel = personnel.filter(function (person) { return person.isForceUser;});// 结果集: [{...}, {...}, {...}] (Luke, Ezra and Caleb)
var jediScores = jediPersonnel.map(function (jedi) { return jedi.pilotingScore + jedi.shootingScore;});// 结果: [154, 110, 156]
.reduce()
)获取总飞行能力指数了。var totalJediScore = jediScores.reduce(function (acc, score) { return acc + score;}, 0);// 结果: 420
这里分开实现方式可以达到我们的目标,但是其实我们可以串联起来,可以写的更加简洁又优雅!我们来玩玩更好玩的吧!
var totalJediScore = personnel .filter(function (person) { return person.isForceUser; }) .map(function (jedi) { return jedi.pilotingScore + jedi.shootingScore; }) .reduce(function (acc, score) { return acc + score; }, 0);
这样写是不是很优雅!都被这段代码给美到了!??
假如我们使用箭头写法ES6,就更加优雅了!
const totalJediScore = personnel .filter(person => person.isForceUser) .map(jedi => jedi.pilotingScore + jedi.shootingScore) .reduce((acc, score) => acc + score, 0);
哇!代码原来可以写的那么优雅的么?!想不到吧?
其实我们只要要使用
.reduce()
即可以得到我们的目标结果了,以上例子做为教学例子,所以使用了3个我们学到的函数。我们来看看只用
.reduce()
怎样实现的,来我们一起来刷新一下三观吧!
const totalJediScore = personnel.reduce((acc, person) => person.isForceUser ? acc + person.pilotingScore + person.shootingScore : acc, 0);
不敢想象吧?一行就搞定一个功能不是梦!
其实我一开始写前台的时候也是一顿撸,来个数组都是撸个for循环,处理一切数组解决问题。但是近几年我开始步入前后台开发,API接口对接。发现数据解决越来越多,假如还是像以前那样什么都用for循环来解决数据,那其实数据解决的代码就会越来越臃肿越来越复杂凌乱。所以我开始抛弃了.forEach()
。开始做一个优雅的程序员!
为什么使用.map()
,.filter()
,.reduce()
写代码更优雅,更美观呢?我们用一个实战例子来比照一下吧。
假设现在我们对接一个接口,返回的数组里面有两个字段name:人的名称
和title:对应的职位
。
var data = [ { name: "Jan Dodonna", title: "General", }, { name: "Gial Ackbar", title: "Admiral", },]
产品经理给到你的需求是只要要展现这些人的职位称呼。
当然这个时候有少量前台就会说“我只是个小小的前台,后台给我解决吧”。但是,这个接口其实是一个通用的接口,就是获取这些员工的资料的,是在多个地方使用的。假如每一个页面由于需要展现的不一样而要写多一个接口给你,你觉得这样好吗?做为一个优秀的前台工程师???,这种小case你自己即可以很优雅的解决好了。而且,在一个优秀的团队,后台的确是要考虑接口通用性的,这种为了你的方便而给他们带来更臃肿的接口是不可接受的。所以前台这个时候就是要重组数据了。
假设现在产品给你的需求是员工列表中,要支持只展现员工职称和员工信息的两种显示项。这个时候我们就要编写一个数据组装方法来跟进展现要求来改变数据格式。
由于这个“骚“需求,我们使用.forEach()
来重组数据就相比照较麻烦了,而且代码也会变得臃肿。
我们忽略了组装数据的方法,直接就当作我们已经写好了一个组装数据的方法为formatElement
。假如我们用forEach
,那首先就需要定义一个空数组来接收结果。
var results = [];data.forEach(function (element) { var formatted = formatElement(element); results.push(formatted);});
所以我们需要两个方法才能实现这个数据结果,但是为什么要写的那么臃肿呢?由于forEach
并没有返回值,单单就给你跑个循环,还需要自己push
值到预约义的变量里面。其实一个方法即可以完成了,而且重点是一行代码就完事了。
来使用我们新学的技巧,用.map()
来实现就非常简单优雅了。
var results = data.map(formatElement);
你学会了吗?学会了就去尝试用.map()
,.reduce()
,.filter()
来替换你传统的for
循环吧!我保证你的代码会越来越简洁,可读性更高。
假如你喜欢我的这遍文章,记得继续关注我的博客,下一遍文章我们开学习怎样在JavaScript中使用.some()
和.find()
。
坚持做一个优雅的程序员,坚持每天敲代码!