介绍

柯理化是一种关于函数的高阶技术。
柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。
柯里化不会调用函数。它只是对函数进行转换。
一个最简单的例子

// 原本的sum函数以及应用
function sum(a, b) {
  return a + b;
}
sum(1,2) // 3
// 柯理化后的sum函数以及应用
function curry_sum(a) {
  return function(b){
     return a+b
  }
}
sum(1)(2) // 3
// 或者 fn=sum(1)  fn(2)  ==>3

为什么要柯理化

柯理化的使用肯定不是为了装逼,或者说不仅仅为了装逼。

柯理化可以延迟执行,达到参数复用的目的。(个人理解动态函数和惰性函数和柯理化没啥关系)

原先是sum(1,2)立即执行,现在可以先fn=sum(1) 然后再fn(2) 这个就对于函数的执行可控了

第一次传参并不执行,我们先埋伏它一波。

后面函数执行的时候复用的我们第一次传的参,这个就叫延迟执行,参数复用。这样可以大幅度简化我们的代码。

一个能投入使用的柯理化函数长啥样

虽然我们可以根据柯理化的思想,写出我们想要的函数,比如之前的curry_sum

但是我们最好能写个更通用的柯理化函数,它接受一个fn,返回一个包装后的函数curry_fn,完成我们所需要的参数复用和延迟执行功能。

这里参考Lodash我们看一个curry函数长啥样

/**
 * 参数
 *  func: 用来柯里化(curry)的函数。
 *  [arity=func.length] : 需要提供给 func 的参数数量。
 *
 *  返回
 * (Function): 返回新的柯里化(curry)函数。
 */
function curry(func, [arity=func.length]){

}

我们给出它的测试用例

// curry.test.js
const{curry} = require('./index')

test('curry',()=>{
    const add = (a,b,c)=>{
        return a+b+c
    }
    const sayhi = function(){
        return this.name
    }
    const curry_add = curry(add)
    const curry_sayhi = curry(sayhi)
    const obj = {name:'fyy'}
    expect(curry_sayhi.call(obj)).toBe('fyy')
    expect(curry_add(2)(3)(4)).toBe(9)
    expect(curry_add(1)(2,3)).toBe(6)
    expect(curry_add(1,2)(2)).toBe(5)
    expect(curry_add(1,2,1)).toBe(4)
})

实现

我们在index.js里面实现一个curry函数

实现的时候需要注意几个关键点

  • 利用闭包保存参数
  • fn真正执行是传的参数达到fn接受的参数个数时
  • 这里要考虑this的指向,我们用apply函数,同时不能在这使用箭头函数,箭头函数里面apply无效,而且是指向定义时的this,node里面也就是空对象
  • 递归的一个使用
//index.js
const curry = function(fn){
    return function curried(...args){ //这里不能用箭头函数
        if(fn.length===args.length){
            return fn.apply(this,args)
        }else{
            return function(...args1){
                return curried.apply(this,args.concat(args1))
            }
        }
    }
}
module.exports = {
    curry
}

我们跑一下测试用例,证明函数没有问题。
image.png

柯理化实用例子

看两个柯理化的例子,加深一下理解

封装请求

比如api长这样 ajax(method,url,params)

// 我们可以在serve.js里封装一个post
const post = url=> data => ajax('post',url,params)

// 在module1api.js里面封装一些地址
import {post} from 'serve.js'
const getList = post('www.xxx.com/api/xxx')

// 在需要调接口的地方,我们只用传递参数就行了,调用方式和地址已经提前穿过了

// 第一处地方
import{getList} from 'module1api.js'
const data = getList(params)
// 第二处地方
import{getList} from 'module1api.js'
const data = getList(params)
// 第N处地方...
import{getList} from 'module1api.js'
const data = getList(params)

简化处理数据

拿到后台的数据 经常要处理
比如原始数据 const data = [{name: 'kevin'}, {name: 'daisy'}]
我们需要处理成['kevin','daisy']
每次在这都调用Map方法实际上代码很臃肿

var _data = data.map(function (item) {
    return item.name;
})

我们其实可以这么写,这也是利用了柯理化

const prop = curry(function (key, obj) {
        return obj[key]
});

var _data = person.map(val=>prop('name')(val))
// var _data = person.map(prop('name')) 
/*
*如果想直接这么调的话,curry需要改一下,
把fn.length===args.length中的===改为>== 大家可以好好想一想原因,
*/

总结

柯理化其实挺常用的,不一定非要用curry函数,理解它的思想我们就能写出很优美很高效的代码。
总归就是闭包的一种应用。

参考文章

柯理化
JavaScript专题之函数柯里化
lodash_curry
深入高阶函数应用之柯里化
https://segmentfault.com/a/1190000018180159


Runningfyy
1.3k 声望661 粉丝