浅谈柯里化

curry柯里化
首先我们先来看一个问题,如果实现一个add函数,可实现下面的功能

add(1,2,3) // 6 add(1)(2)(3) // 6 add(1,2)(3) // 6 add(1,2,3,4)(5)(6,7) // 28

当然了,所需要用到的知识点便是柯里化。
首先看下柯里化定义:
用于缓存函数参数的一种方式;给函数分步传递参数,每次传递部分参数,并返回一个更具体的函数接收剩下的参数,这中间可嵌套多层这样的接收部分参数的函数,直至返回最后结果。
初看上面这行字,可能不太好理解,我大概总结了下,如何去实现或者说去理解柯里化,大致是下面两点。
【浅谈柯里化】1、如何进行参数的缓存
2、在何时执行原函数
当收集到所需要的参数时,便去执行目标函数,得到结果,否则继续收集参数。
这里就不展开讨论闭包、call、apply、bind这些概念了。
举个例子
function add(a,b,c) { console.info(a, b, c, '==此函数需要传递三个参数执行==) return a + b + c }

不难看出上面的函数执行需要三个参数,因此我们在调用test时,需要传递a,b,c否则函数执行异常;这时候回到上面的两点,如何进行参数的缓存以及何时执行原函数去得到目标结果。
接下来看看如何去实现
固定参数个数的柯里化
/** * 传入固定参数长度的柯里化 * @params fn 原函数 * @paramsargs 初始化参数 */ function curry(fn, ...args) { const len = fn.length return function() { // 参数收集 const allArgs = [...args, ...arguments] // 达到目标参数个数后,执行原函数 if (allArgs.length >= len) { return fn.apply(null, allArgs) } return curry.call(null, fn, ...allArgs) } }function add(a,b,c) { return [...arguments].reduce((pre, cur) => pre += cur, 0) }const test = curry(add)console.info(test(1,2,3)) // 6 console.info(test(1,2)(3)) // 6 console.info(test(1)(2)(3)) // 6test = null // 防止内存泄漏

参数不固定 1、当传入空的参数时,执行原函数
/** * 传入参数不固定,参数为空时执行函数的柯里化 * @params fn 原函数 * @paramsargs 初始化参数 */ function _curry(fn, ...args) { let allArgs = [...args] return function next(..._args) { allArgs = [...allArgs, ..._args] if (_args.length === 0) { return fn.apply(null, allArgs) } return next } }function _add() { return [...arguments].reduce((pre, cur) => pre += cur, 0) }const _test = _curry(_add)_test(1,2) _test(3) console.info(_test()) // 6_test(4) console.info(_test()) // 10_test = null // 防止内存泄漏

2、直接返回 这里需要用到一个小知识点,在解析函数的原始值时,会用到函数的隐式转换toString,因此我们的执行原函数节点在于toString
/** * 传入参数不固定柯里化 * @params fn 原函数 * @paramsargs 初始化参数 */ function _curry(fn, ...args) { let allArgs = [...args] function next(..._args) { allArgs = [...allArgs, ..._args] return_curry.call(null, fn, ...allArgs) } next.toString = function() { return fn.apply(null, allArgs) } return next }function _add() { return [...arguments].reduce((pre, cur) => pre += cur, 0) }const _test = _curry(_add)_test(1, 2) // 2_test(4)(5)(6) // 15 console.info(_test()) // 10_test = null // 防止内存泄漏

上述便是柯里化用于延迟执行的几种用法
参数复用 能够理解上述用法后,柯里化用于参数复用就轻而易举了,如下例
/** * Object.prototype.toString.call(obj) === '[object ***]' */ function typeOf(value) { return function(obj) { const toString = Object.prototype.toString const map = { '[object Boolean]': 'boolean', '[object String]': 'string', '[object Number]': 'number', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regExp', '[object Undefined]': 'undefined', '[object Null]': 'null', '[object Object]': 'object' } return map[toString.call(obj)] === value } }const isNumber = typeOf('number') const isString = typeOf('string') isNumber(0) // true isString('a') // true

    推荐阅读