博客 / 詳情

返回

一看就會的柯理化

介紹

柯理化是一種關於函數的高階技術。
柯里化是一種函數的轉換,它是指將一個函數從可調用的 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

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.