【你應該瞭解的】函數柯里化

團隊:skFeTeam  本文做者:蔡旭光git

一道面試題

已知一個add方法。github

function add(a, b, c) {  
  return a + b + c;
}
add(1,2,3)   // 6
複製代碼

實現一個sum方法,知足以下條件:面試

sum(1)(2,3)   // 6
sum(1)(2)(3)   // 6
複製代碼

該題其實就運用了函數柯里化的思想,咱們能夠將add方法經過函數柯里化轉化爲sum方法。編程

先來看看什麼是函數柯里化。閉包

什麼是函數柯里化

維基百科中這樣定義柯里化:app

計算機科學中,柯里化(英語:Currying),又譯爲卡瑞化加里化,是把接受多個參數函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。函數式編程

用大白話來講就是:只傳遞給函數一部分參數來調用它,讓它返回一個新函數去處理剩下的參數。函數

舉個例子理解柯里化:

假設你有一間商店,促銷季,你想給普通顧客全部商品九折優惠。ui

function setDiscount(price, discount) {  
    return price * discount;
}
複製代碼

當一個普通顧客買了一件價格爲100元的商品,你計算價格能夠:this

const price = setDiscount(100, 0.9);
複製代碼

你會發現,每次計算一個商品價格,都須要輸入價格和折扣,很麻煩。

const price1 = setDiscount(500, 0.9);
const price2 = setDiscount(1000, 0.9);
const price3 = setDiscount(2000, 0.9);
複製代碼

咱們能夠將函數setDiscount柯里化,避免每次都輸入折扣。

function currySetDiscount(discount) {  
    return function(price) {    
        return discount * price;  
    };
}

const setNinetyPercent = currySetDiscount(0.9);
複製代碼

如今咱們能夠這樣計算普通顧客購買商品時的價格:

const price = setNinetyPercent(500);
複製代碼

一樣的,針對VIP顧客,全部商品打八折,能夠這樣計算:

const setEightyPercent = currySetDiscount(0.8);

const price = setEightyPercent(500);
複製代碼

函數柯里化的這種做用,能夠理解爲參數複用,延遲執行,減小了代碼冗餘,增長了代碼可讀性

函數柯里化的簡單實現

回到開始的那道題,咱們來寫一個curry函數。

function curry(fn, args) {  
    let len = fn.length;  // 待柯里化的函數的參數長度
    let tempArgs = args || [];  
    return function() {    
        tempArgs = tempArgs.concat([...arguments])    
        if (tempArgs.length < len) {      
            return curry.call(this, fn, tempArgs);    
        } else {      
            return fn.apply(this, tempArgs);    
        }  
    };
}
複製代碼

原理:用閉包保存參數,當參數數量和原函數參數數量一致時,執行函數

能夠這樣來柯里化add,生成sum方法:

var sum = curry(add);

sum(1)(2,3)   // 6
sum(1)(2)(3)   // 6
複製代碼

函數柯里化的應用

延遲計算

咱們經常使用的bind函數。

let obj = {
  name: 'jack'
}
let showName = function() {
    console.log(this.name)
}
let showJackName = showName.bind(obj);

showJackName(); // jack
複製代碼

這裏bind用來改變函數執行時的上下文,可是函數自己並不執行,因此本質上是延遲計算。

咱們看下bind的模擬實現,本質上就是一種柯里化。

function myBind(){
  var self = this;
  // 第一個對象參數
  var context = Array.prototype.shift.call(arguments);
  // 其他參數
  var bindArgs = Array.prototype.slice.call(arguments);
  // 臨時函數
  var fTemp = function(){};
  function fn(){
    // 合併綁定參數以及調用時參數
    var args = bindArgs.concat(Array.prototype.slice.call(arguments));
    // 原函數執行(this指向給定對象)
    self.apply(context, args);
  }
  // 臨時函數prototype指向原函數prototype
  fTemp.prototype = self.prototype;
  // 新函數prototype設爲臨時函數的實例對象(當原函數使用New建立實例)
  fn.prototype = new fTemp();
  return fn;
}
複製代碼

總結

函數柯里化是函數式編程(一種編程範式)中的一個最基本的運算,它生於函數式編程,也主要服務於函數式編程(函數的組合前提是須要單參數的函數)。咱們平常開發中其實無需特地區分是否使用函數柯里化,閉包,高階函數等都必定程度上與函數柯里化有殊途同歸之妙。

想了解skFeTeam更多的分享文章,能夠點這裏,謝謝~

相關文章
相關標籤/搜索