学习总结篇,以能否造轮子衡量学习效果。本篇是在看到
koa-compose
、redux
里均有reduce
的影子,才计划输出的
reduce 实现
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
reducer
函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
reducer
函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
实现一个reduce
Array.prototype.reduce1 = function (callback, initValue) {
// 调用的array
let curArr = this
// 上一次的返回值
let returnValue
// 起点
let startIndex
// 空数组-并且有初始值
if(curArr.length === 0) {
return initValue
}
// 判断是否有传第二个参数(初始值)
returnValue = typeof initValue === 'undefined' ? curArr[0] : initValue
// 初始迭代位置
startIndex = typeof initValue === 'undefined' ? 1 : 0
for (let index = startIndex; index < curArr.length; index++) {
// 依次执行callback
returnValue = callback(returnValue, curArr[index], index, curArr)
}
return returnValue
}
console.log('reduce', [1,2,3].reduce1((pre, cur) => pre + cur, 1))
// 7
reduce实现promise顺序执行
想到顺序执行promise
, 当然是可以用async await
实现。本文主要谈的是reduce
的实现。
runQueuePromise
方法将会被一个每一项都返回一个 Promise 的数组调用,并且依次执行数组中的每一个 Promise
const f1 = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('f1 running')
resolve(1)
})
})
const f2 = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('f2 running')
resolve(1)
})
})
const fnArray = [f1, f2]
function runQueuePromise(arr, initValue) {
return arr.reduce((preChain, curFunction) => preChain.then(curFunction), Promise.resolve(initValue))
}
runQueuePromise(fnArray, 'initvalue')
reduce实现pipe
pipe
,是把一些小功能拼筹在一起,组成我们自己需要的功能。
例:
const fn1 = x => x + 1
const fn2 = x => x + 2
我们想让一个参数通过第一个函数之后再通过第二个函数,最直接最简单的方法是:
f2(f1(1)) // 4
写一个简单的pipe函数,它返回一个新的函数,来达到我们上面的目的
pipe 的实现:pipe(f1, f2, f3) 是一个 curry 化函数,它返回一个新的函数,这个新的函数将会完成 (...args) => f3(f2(f1(...args))) 的调用。
const fn1 = x => x + 1
const fn2 = x => x + 2
const pipe = (...funcs) => input => funcs.reduce(
(f1, f2) => f2(f1),
input
)
const newPipe = pipe(fn1, fn2)
console.log('pipe:', newPipe(1))
reduce实现compose
compose
和前面提到的 pipe
一样,就是执行一连串任务(方法),比如:
let funcs = [fn1, fn2, fn3, fn4]
let composeFunc = compose(...funcs)
composeFunc(args)
fn1(fn2(fn3(fn4(args))))
// compose
fn1(fn2(fn3(fn4(args))))
// pipe
fn4(fn3(fn2(fn1(args))))
唯一的不同就是执行顺序
1. 面向过程的实现:递归
用到了闭包,使用闭包变量储存结果 result
和函数数组长度以及遍历索引,并利用递归思想,进行结果的累加计算。
function compose(...args) {
// 函数数组长度。闭包变量函数数组长度以及遍历索引
let len = args.length
// 需要执行的函数个数:总数 - 1(第一次进来,已执行一个)
let total = len - 1
// 闭包变量储存结果 result
let result
return function fn(...args1) {
// 从右往左执行函数
result = args[total].apply(this, args1)
// 函数数组执行完毕
if (total <= 0) {
total = len - 1
return total
}
total--
return fn.call(null, result)
}
}
2. redux版本的实现
充分利用了reduce方法,个人最喜欢的写法
// Redux 版本
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
3. koa-compose版本的实现
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
至此,reduce
的用处还真不少。
PS:欢迎交流学习,不足之处,尽请指出。