Array.prototype.reduce在之前用的很少,在字节跳动面试的时候问到了这个问题,后面就去看了下 MDN,稍微对它理解了些nginx
reduce
方法将数组从左到右的每一个元素依次传入回调函数面试
👇是些经常使用到的地方shell
方法1 (这种方法是我没接触Array.prototype.reduce最经常使用的方法):编程
const str = '9kFZTQLbUWOjurz9IKRdeg28rYxULHWDUrIHxCY6tnHleoJ'
const obj = {}
str.split('').forEach(item => {
obj[item] ? obj[item]++ : obj[item] = 1
})
复制代码
方法2 (这个好玩一点点🤪):数组
const str = '9kFZTQLbUWOjurz9IKRdeg28rYxULHWDUrIHxCY6tnHleoJ'
const obj = {}
Array.from(str).reduce((accumulator, current) => {
current in accumulator ? accumulator[current]++ : accumulator[current] = 1
return accumulator
}, obj)
复制代码
嗯,方法2虽然步骤比方法1复杂些(其实并不复杂吼),可是是否是更好玩些😉?函数式编程
方法1 (经常使用):函数
const arr = [
{
"name": "a1111",
"age": 25
},
{
"name": "a1",
"age": 26
},
{
"name": "a11",
"age": 27
},
{
"name": "a",
"age": 29
},
{
"name": "a11",
"age": 29
},
{
"name": "a11",
"age": 26
},
{
"name": "a111",
"age": 25
},
{
"name": "a11",
"age": 26
},
{
"name": "a1",
"age": 26
},
{
"name": "a",
"age": 26
}
]
arr.filter(item => item.name.length === 3)
.filter(item => item.age > 26)
/* [ { "name": "a11", "age": 27 }, { "name": "a11", "age": 29 } ] */
复制代码
方法2 reduce方法(感谢@zhanggenming的指证,已修改):ui
const arr = [
{
"name": "a1111",
"age": 25
},
{
"name": "a1",
"age": 26
},
{
"name": "a11",
"age": 27
},
{
"name": "a",
"age": 29
},
{
"name": "a11",
"age": 29
},
{
"name": "a11",
"age": 26
},
{
"name": "a111",
"age": 25
},
{
"name": "a11",
"age": 26
},
{
"name": "a1",
"age": 26
},
{
"name": "a",
"age": 26
}
]
const filter1 = (arr) => arr.filter(item => item.name.length === 3)
const filter2 = (arr) => arr.filter(item => item.age > 26)
const fnArr = [filter1, filter2]
fnArr.reduce((accumulator, fn) => {
accumulator = fn(accumulator)
return accumulator
}, arr)
/* [ { "name": "a11", "age": 27 }, { "name": "a11", "age": 29 } ] */
复制代码
用了这个方法,那咱们试试看用for循环来实现Array.prortotype.reducethis
先看看reduce接收的参数:spa
arr.reduce(callback[, initialValue])
复制代码
reduce方法接收1个callback
的函数做为第一个参数,还有1个可选参数initialValue
。 同时callback
函数有最多4个参数
知道了这些,那咱们实现reduce方法就很简单了
Array.prototype.selfReduce = function() {
const ary = this
const { length } = ary
if (arguments.length === 0) {
throw new TypeError('undefined is not a function')
}
if (typeof arguments[0] !== 'function') {
throw new TypeError(arguments[0] + 'is not a function')
}
if (ary.length === 0 && arguments.length === 1) {
throw new TypeError('Reduce of empty array with no initial value')
}
const callback = arguments[0]
const startIndex = arguments.length >= 2 ? 0 : 1
let value = startIndex === 0 ? arguments[1] : ary[0]
for (let i = startIndex; i < length; i++) {
value = callback(value, ary[i], i, ary)
}
return value
}
复制代码
同时,reduce
还有个兄弟👬reduceRight
,reduceRight如其名,是将数组从右到左的将每一个元素传入callback
函数。那实现reduceRight
实现起来也就简单了。
Array.prototype.selfReduceRight = function () {
const ary = this
const { length } = ary
if (arguments.length === 0) {
throw new TypeError('undefined is not a function')
}
if (typeof arguments[0] !== 'function') {
throw new TypeError(arguments[0] + 'is not a function')
}
if (ary.length === 0 && arguments.length === 1) {
throw new TypeError('Reduce of empty array with no initial value')
}
const startIndex = arguments.length >= 2 ? length - 1 : length - 2
const callback = arguments[0]
let value = startIndex === 0 ? arguments[1] : ary[length - 1]
for (let i = startIndex; i >= 0; i--) {
value = callback(value, ary[i], i, ary)
}
return value
}
复制代码
穿插一点函数式编程(FP)里面的东西
compose
compose
的执行方向是从右向左执行的
下面是compose的实现(未处理异常状况)
const compose = function(...fns) {
return (val) => {
return fns.reduceRight((acc, fn) => {
return fn(acc);
}, val)
}
};
const add1 = x => x + 1;
const mult2 = y => y * 2;
const composeFn = compose(add1, mult2);
composeFn(5); // 11 = 5 * 2 + 1
复制代码
pipe
如同Unix里面的"|"同样,pipe
是从左向右执行的。下面是一个很简单的查看nginx进程的命令(写的有点挫😂)。
ps -ef | grep nginx
复制代码
下面是pipe的实现(未处理异常状况)
const pipe = function(...fns) {
return (val) => {
return fns.reduce((acc, fn) => {
return fn(acc);
}, val)
}
};
const add1 = x => x + 1;
const mult2 = y => y * 2;
const pipeFn = pipe(add1, mult2);
pipeFn(5); // 12 = (5 + 1) * 2
复制代码