reduce
是ES5中新引入的一个API。
假如你还不知道reduce的用法,请先阅读下MDN文档中关于reduce的介绍。(不得不说,MDN文档太强大了,里面列举了很多有用的方法)
本文将介绍借助reduce函数,利用其能够遍历到数组的每一个元素,并且次遍历都可以使用上次遍历结果的特性,实现的一些功能。
1.累和/累积
let arr = [1, 2, 3, 4, 5]
console.log(arr.reduce((prev, cur) => prev + cur)) // 15
// 可以实现另类的阶乘
console.log(arr.reduce((prev, cur) => prev * cur)) // 120
2.求最大值/最小值
let arr = [1, 2, 3, 4, 5]
console.log(arr.reduce((prev, cur) => Math.max(prev, cur))); // 5
console.log(arr.reduce((prev, cur) => Math.min(prev, cur))); // 1
3. 数组去重
当reduce
接收两个参数时,即reduce(fn, init), init
将作为fn
的第一个参数prev
传入。
这里,将一个空数组[]
作为去重后的新数组,通过做判断,如果该容器内已经存在某元素,就啥也不做;反之,如果该容器内还没有一个元素,就将其推入容器。
let arr = [1, 2, 3, 1, 1, 2, 3, 3, 4, 3, 4, 5]
let res = arr.reduce((prev, cur)=>{
!prev.includes(cur) && prev.push(cur)
return prev
}, [])
console.log(res) // [ 1, 2, 3, 4, 5 ]
4.实现map函数
map
函数接收一个函数作为参数,作为参数的函数接收三个参数值,分别是遍历数组的每一项元素,元素的索引和数组本身。这三个参数刚好和reduce
函数接收的第一个函数参数的第2、3、4个参数是对应的。
实现思路是,将每次遍历的元素,作为传入的函数的参数,并将函数执行的结果放入新的数组中。
let arr = [1, 2, 3]
Array.prototype._map = function(cb) {
if(typeof cb === 'function') {
// this: 调用_map方法的当前数组对象
let arr = this;
return arr.reduce((prev, item, index, array) => {
prev.push(cb(item, index, array))
return prev
}, [])
} else {
throw new Error(cb + ' is not function')
}
}
let res = arr._map(n => n*2)
console.log(res) // [ 2, 4, 6 ]
5.实现filter函数
实现filter
的思路和实现map
是一致的,只不过后者是一股脑的把执行结果全放入数组中,而filter
需要做一个判断:如果filter
函数传入的参数(参数是一个函数)执行后有返回值,即经过了检验,才将遍历的当前元素放入数组中,如果没有返回值,就忽略。
let arr = [1, 2, 3, 4, 5];
Array.prototype._filter = function(cb) {
if(typeof cb === 'function') {
let arr = this;
return arr.reduce((prev, item, index, array) => {
cb(item, index, array) ? prev.push(item) : null
return prev
}, [])
} else {
throw new Error(cb + ' is not function')
}
}
let res = arr._filter(n => n>2)
console.log(res) // [ 3, 4, 5 ]
6.实现compose
compose
是函数式编程的核心思想,简单说就是将若干个函数组合成一个函数来执行,并且每个函数执行的结果都能作为下一个函数的参数。这也是使用reduce
实现compose
的思路。
假设有两个函数,作用分别是将字符串转为大写,在字符串末尾追加感叹号:
function toUpperCase(str) {
return str.toUpperCase();
}
function add(str) {
return str += '!'
}
一般情况下,会这样使用:
var str = 'hello world'
var res = toUpperCase(str)
res = add(res)
console.log(res); // HELLO WORLD!
使用compose
后,效果是这样的,执行fn
,相当于依次执行了toUpperCase
和add
:
var fn = compose(add, toUpperCase)
console.log(fn(str));// HELLO WORLD!
接下来实现一下compose
:
function compose() {
let args = [].slice.call(arguments)
return function (x) {
// 因为compose()接收的函数参数,是从右往走顺次执行的,
// 所以这里使用reduceRight, 用法和reduce一致,只不过是从右往左遍历数组。
return args.reduceRight((prev, cur) => {
return cur(prev)
}, x)
}
}
7.数组扁平化
数组扁平化,针对的是多维数组,将其扁平、展开,成为一维数组。
let arr = [1, 2, '3js', [4, 5, [6], [7, 8, [9, 10, 11], null, 'abc'], {age: 12}, [13, 14]], '[]'];
function flatten(arr) {
if(Array.isArray(arr)) {
return arr.reduce((prev, cur) => {
// 如果遍历的当前项是数组,再迭代展平
return Array.isArray(cur) ? prev.concat(flatten(cur)) : prev.concat(cur)
}, [])
} else {
throw new Error(arr + ' is not array')
}
}
console.log(flatten(arr));
结束
当然,除了以上几种,reduce
还有更多种神奇的应用,等待着各位小伙伴的发现和使用。