6

一看就会的柯理化

 3 years ago
source link: https://segmentfault.com/a/1190000040003062
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

柯理化是一种关于函数的高阶技术。
柯里化是一种函数的转换,它是指将一个函数从可调用的 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK