JS的柯里化與偏應用

在以前的文章中對函數式編程作了一個簡單的概述,在這篇文章中對一個你們喜聞樂見的話題——函數的柯里化進行一個總結。編程

⚠️注意: 柯里化和偏應用的概念常常被混用,在文中會有概念上的簡單區分數組

一些基本概念

一元函數、多元函數以及變參函數

這些概念仍是很好理解的,咱們的平常開發中老是伴隨着這些函數:app

  • 一元函數:只有一個參數的函數,形如
let log = (msg) => {console.log(msg)}
複製代碼
  • 多元函數:有多個參數的函數,形如
let add = (x,y) => {return x+y}
複製代碼
  • 變參函數:參數數量不肯定的函數,在ES6以前咱們經過 argements 獲取全部參數,如今咱們每每會使用 ...args 由於這樣咱們能夠直接使用數組方法進行操做,形如
function logAll(){
    // arguments 不是數組,只是一個類數組對象
    console.log(arguments); 
}
logAll(1,2,3); // [1,2,3]

function logAllByES6(...arg){
    // arg是數組,能夠執行數組方法
    console.log(arg.map((item)=>{return item*2})); 
}
logAllByES6(1,2,3) // [2,4,6]
複製代碼

柯里化

根據一個相對通用的定義,函數的柯里化指:函數式編程

把一個多參數函數轉換爲一個嵌套的一元函數的過程函數

有一個很是典型的例子是add函數優化

// 柯里化前
function add(x,y){
    return x+y;
}
add(1,2); // 3

// 柯里化後
function addCurried(x){
    return function(y){
        return x+y;
    }
}
addCurried(1)(2); //3
複製代碼

這樣作的好處在於咱們能夠經過這樣的方式獲得一系列新函數,從而讓咱們優化數據的處理過程ui

偏應用(部分應用)

偏應用又稱做部分應用,偏應用的概念和柯里化有所區別,可是又很相似spa

  • 柯里化將函數轉化爲嵌套的一元函數
  • 偏應用是爲一個多元函數預先提供部分參數,從而在調用時能夠省略這些參數

事實上有不少文章中的柯里化指的就是偏應用,好比:code

function add(x,y,z){
    return x+y+z;
}
add(1,2,3); // 6
// 柯里化
addcurried(1)(2)(3); // 6
// 偏應用
addPartial(1,2)(3);  // 6
addPartial(1)(2,3);  // 6
複製代碼

實現

實現的方式多種多樣,寫這篇文章的時候也參考了不少前輩的文章,有各類版本的實現,在ES6的加持下還出現了「一行代碼實現柯里化」這樣的騷操做對象

不過在這裏,我選擇了比較容易被你們接受的實現方法記錄在文中

如下實現方法參考了 Anto Aravinth 的實現,並作了一點小小的改動

let curry = (fn) => {
    return function curriedFn(...args){
        if(args.length < fn.length){
            return function(...args2){
                return curriedFn.apply(null,args.concat(args2));
            }
        }
        return fn.apply(null,args);
    }
}
let tipFunc = () => {console.log("經過柯里化,令 tipFunc 在指定事件後執行")}
let tipsTimer = curry(setTimeout)(tipFunc);
tipsTimer(10000); // 10s 後執行
tipsTimer(20000); // 20s 後執行
複製代碼
// 偏應用函數
let partial = (fn,...partialArgs)=>{
    let args = partialArgs;
    return function(...fullArgs){
        let arg = 0;
        for(let i = 0;i < args.length && arg < fullArgs.length;i++){
            if (args[i] === undefined){
                args[i] = fullArgs[arg++];
            }
        }
        return fn.apply(null,args);
    }
}
let timer10s = partial(setTimeout,undefined,10000);
partial(()=>{console.log("經過偏應用,爲setTimeout函數預先提供參數")});
複製代碼
相關文章
相關標籤/搜索