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