Introduction

Ke Lihua is a high-level technique about functions.
Currying is a kind of function conversion, which refers to converting a function from callable f(a, b, c) to callable f(a)(b)(c).
Currying does not call functions. It just converts the function.
The simplest example

// 原本的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

Why should Ke Lihua

The use of Ke Lihua is definitely not for pretending, or not just for pretending.

Ke Lihua can delay execution to achieve the purpose of parameter reuse. (Personal understanding of dynamic functions and lazy functions has nothing to do with Ke Lihua)

Originally sum(1,2) was executed immediately, now you can first fn=sum(1) and then fn(2), which can control the execution of the function

The first transfer of parameters is not executed, let's ambush it first.

When the function is executed later, we reuse the parameters we passed for the first time. This is called delayed execution and parameter reuse. This can greatly simplify our code.

What does a function that can be put into use look like?

Although we can write the function we want based on Ke Lihua’s ideas, such as the previous curry_sum

But we'd better write a more general curation function, which accepts a fn and returns a wrapped function curry_fn to complete the parameter reuse and delayed execution functions we need.

Refer to Lodash here, let’s see what a curry function looks like

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

}

We give its test case

// 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)
})

achieve

We implement a curry function in index.js

Several key points need to be paid attention to when implementing

  • Use closures to save parameters
  • The real execution of fn is when the passed parameters reach the number of parameters accepted by fn
  • Here we need to consider the direction of this. We use the apply function, and at the same time, we cannot use the arrow function here. Apply is invalid in the arrow function, and it points to the this at the time of definition. The node is an empty object.
  • A use of recursion
//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
}

Let's run a test case to prove that the function is ok.
image.png

Ke Lihua practical examples

Look at two examples of Ke Lihua to deepen your understanding

Package request

For example, the api looks like this 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)

Simplify processing data

The data received in the background often needs to be processed
For example, raw data const data = [{name:'kevin'}, {name:'daisy'}]
We need to process into ['kevin','daisy']
Every time I call the Map method here, the code is actually bloated

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

In fact, we can write like this, which also makes use of Ke Lihua

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中的===改为>== 大家可以好好想一想原因,
*/

to sum up

Ke Lihua is actually quite commonly used. It is not necessary to use the curry function, and we can write very beautiful and efficient code by understanding its ideas.
It is always an application of closures.

Reference article

Ke Lihua
JavaScript topic function currying
lodash_curry
in-depth application of higher-order functions
https://segmentfault.com/a/1190000018180159


Runningfyy
1.3k 声望661 粉丝