在全部后 ES6 时代的数组方法中,我以为最难理解的就是Array.reduce()
。html
从表面上看,它彷佛是一个简单无趣的方法,并无太大做用。 可是在不起眼的外表之下,Array.reduce()
其实是对开发人员工具包的强大而灵活的补充。前端
今天,咱们就来研究一下经过Array.reduce()
能够完成的一些有意思的事情。数组
大部分现代的数组方法都返回一个新的数组,而 Array.reduce()
更加灵活。它能够返回任意值,它的功能就是将一个数组的内容聚合成单个值。浏览器
这个值能够是数字、字符串,甚至能够是对象或新数组。这就是一直难住个人部分,我没想到它这么灵活!bash
Array.reduce()
接受两个参数:一个是对数组每一个元素执行的回调方法,一个是初始值。微信
这个回调接收4个参数,前两个参数是:accumulator
是当前聚合值,current
是数组循环时的当前元素。不管你返回什么值,都将做为累加器提供给循环中的下一个元素。初始值将做为第一次循环的累加器。函数
var myNewArray = [].reduce(function (accumulator, current) {
return accumulator;
}, starting);
复制代码
让咱们来看几个实际例子。工具
假设你想把一组数字加在一块儿。使用Array.forEach()
大概能够这么作:性能
var total = 0;
[1, 2, 3].forEach(function (num) {
total += num;
});
复制代码
这是Array.reduce()
用得最多的例子了。我发现* accumulator *这个单词让人困惑,因此在示例中我改成sum
,由于这里就是求和的意思。ui
var total = [1, 2, 3].reduce(function (sum, current) {
return sum + current;
}, 0);
复制代码
这里传入0
做为初始值。
在回调里,将当前值加入到 sum
,第一轮循环时它的值是初始值0
,而后变成1
(初始值0
加上当前元素值1
),而后变成3
(累加值 1
加上当前元素值 2
),以此类推
假设有一个wizards
数组:
var wizards = [
{
name: 'Harry Potter',
house: 'Gryfindor'
},
{
name: 'Cedric Diggory',
house: 'Hufflepuff'
},
{
name: 'Tonks',
house: 'Hufflepuff'
},
{
name: 'Ronald Weasley',
house: 'Gryfindor'
},
{
name: 'Hermione Granger',
house: 'Gryfindor'
}
];
复制代码
你想建立一个仅包含住在 Hufflepuff 的巫师名字的新数组。一个可行的方法是使用Array.filter()
方法获取 house
属性为Hufflepuff
的 wizards
。而后用Array.map()
方法建立一个只包含过滤后对象的name
属性的新数组。
var hufflepuff = wizards.filter(function (wizard) {
return wizard.house === 'Hufflepuff';
}).map(function (wizard) {
return wizard.name;
});
复制代码
使用Array.reduce()
方法,咱们能够用一步获得一样的结果,提升了性能。传递一个空数组[]
做为初始值。每次循环时判断wizard.house
是否为Hufflepuff
。若是是,就加入到newArr
中(即accumulator
),不然啥也不作。
不管判断条件是否成立,最后都返回 newArr
做为下一次循环的accumulator
。
var hufflepuff = wizards.reduce(function (newArr, wizard) {
if (wizard.house === 'Hufflepuff') {
newArr.push(wizard.name);
}
return newArr;
}, []);
复制代码
那么,若是想建立一个由住在 Hufflepuff 的巫师组成的无序列表要怎么作呢?此次不是给Array.reduce()
传一个空数组做为初始值了,而是一个名为 html
的空字符串''
。
若是wizard.house
等于 Hufflepuff
,咱们就将wizard.name
用列表项li
包裹起来,再拼接到html
字符串里。而后返回html
做为下一次循环的accumulator
。
var hufflepuffList = wizards.reduce(function (html, wizard) {
if (wizard.house === 'Hufflepuff') {
html += '<li>' + wizard.name + '</li>';
}
return html;
}, '');
复制代码
在Array.reduce()
先后添加无序列表的开始和结束标记,就能够把它插入到 DOM 中了。
var hufflepuffList = '<ul>' + wizards.reduce(function (html, wizard) {
if (wizard.house === 'Hufflepuff') {
html += '<li>' + wizard.name + '</li>';
}
return html;
}, '') + '</ul>';
复制代码
lodash 有个 groupBy()
方法,能够将数组元素按照某个标准分组。
假设你有一个数字数组。
若是你想把numbers
数组中的元素按照整数部分的值分组,用 lodash 能够这样作:
var numbers = [6.1, 4.2, 6.3];
// 返回 {'4': [4.2], '6': [6.1, 6.3]}
_.groupBy(numbers, Math.floor);
复制代码
若是你有一个单词数组,你想根据 words
中的单词长度分组,你能够这样作:
var words = ['one', 'two', 'three'];
// 返回 {'3': ['one', 'two'], '5': ['three']}
_.groupBy(words, 'length');
复制代码
Array.reduce()
实现 groupBy()
函数你能够用Array.reduce()
方法实现一样的功能。
咱们来建立一个工具函数groupBy()
,接受数组和分组条件做为参数。在groupBy()
内部,在数组上执行Array.reduce()
,传一个空对象{}
做为初始值,而后返回结果。
var groupBy = function (arr, criteria) {
return arr.reduce(function (obj, item) {
// 省略代码
}, {});
};
复制代码
在 Array.reduce()
回调函数内部,咱们会判断criteria
是函数仍是 item
的属性。而后获取当前item
的值。
若是obj
中还不存在这个属性,则建立它,并将一个空数组赋值给它。最后,将item
添加到 key
的数组中,再返回该对象做为下一次循环的accumulator
。
var groupBy = function (arr, criteria) {
return arr.reduce(function (obj, item) {
// 判断criteria是函数仍是属性名
var key = typeof criteria === 'function' ? criteria(item) : item[criteria];
// 若是属性不存在,则建立一个
if (!obj.hasOwnProperty(key)) {
obj[key] = [];
}
// 将元素加入数组
obj[key].push(item);
// 返回这个对象
return obj;
}, {});
};
复制代码
还记得前面的wizards
数组吗?
var wizards = [
{
name: 'Harry Potter',
house: 'Gryfindor'
},
{
name: 'Cedric Diggory',
house: 'Hufflepuff'
},
{
name: 'Tonks',
house: 'Hufflepuff'
},
{
name: 'Ronald Weasley',
house: 'Gryfindor'
},
{
name: 'Hermione Granger',
house: 'Gryfindor'
}
];
复制代码
若是还有另外一份数据,每一个巫师得到的的积分对象:
var points = {
HarryPotter: 500,
CedricDiggory: 750,
RonaldWeasley: 100,
HermioneGranger: 1270
};
复制代码
假设你想把两份数据合并到一个数组,也就是把 points
数值添加到每一个巫师对象上。你会怎么作?
Array.reduce()
方法特别适合!
var wizardsWithPoints = wizards.reduce(function (arr, wizard) {
// 移除巫师名字中的空格,用来获取对应的 points
var key = wizard.name.replace(' ', '');
// 若是wizard有points,则加上它,不然设置为0
if (points[key]) {
wizard.points = points[key];
} else {
wizard.points = 0;
}
// 把wizard对象加入到新数组里
arr.push(wizard);
// 返回这个数组
return arr;
}, []);
复制代码
其实这里用Array.map
也很方便实现。
若是你想合并两个来源的数据到一个对象中,也就是巫师的名字做为属性名,house 和 points 做为属性值,要怎么作呢?一样, Array.reduce()
很合适。
var wizardsAsAnObject = wizards.reduce(function (obj, wizard) {
// 移除巫师名字中的空格,用来获取对应的 points
var key = wizard.name.replace(' ', '');
// 若是wizard有points,则加上它,不然设置为0
if (points[key]) {
wizard.points = points[key];
} else {
wizard.points = 0;
}
// 删除 name 属性
delete wizard.name;
// 把 wizard 数据添加到新对象中
obj[key] = wizard;
// 返回该对象
return obj;
}, {});
复制代码
Array.reduce()
真香Array.reduce()
方法从我曾经认为不堪大用的东西,变成我最喜欢的 JavaScript 方法。那么,你应该使用它吗?何时能够用?
Array.reduce()
方法有着良好的浏览器支持。全部的现代浏览器都支持,包括 IE9 及以上。移动端浏览器也在很早以前就支持了。若是你还须要支持更老的浏览器,你能够添加一个 polyfill 来支持到 IE6。
Array.reduce()
最大的槽点可能就是对于历来没接触过的人来讲有点费解。组合使用Array.filter()
和Array.map()
执行起来更慢,而且包含多余的步骤,可是更容易阅读,从方法名能够明显看出它要作的事情。
尽管如此,有时候Array.reduce()
也可让复杂的事情看起来更简单。 groupBy()
工具函数就是个很好的例子。
最后,它应该成为你的工具箱里的另外一个工具,一个使用得当就威力无穷的工具。