前言
本文主要從 3W (what, how, why) 角度出發(fā)通俗易懂的解釋一下 什么是函數(shù)柯里化,以及怎么用三行代碼來實(shí)現(xiàn) add(1)(2)(3)
這個(gè)很常見的面試題。
什么是函數(shù)柯里化(curry)
函數(shù)柯里化(curry)是函數(shù)式編程里面的概念。curry的概念很簡單:只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)。
簡單點(diǎn)來說就是:每次調(diào)用函數(shù)時(shí),它只接受一部分參數(shù),并返回一個(gè)函數(shù),直到傳遞所有參數(shù)為止。
舉個(gè)??
將下面接受兩個(gè)參數(shù)的函數(shù)改為接受一個(gè)參數(shù)的函數(shù)。
const add = (x, y) => x + y;
add(1, 2);
改成每次只接受一個(gè)參數(shù)的函數(shù)
const add = x => y => x + y;
add(1)(2);
柯里化,不可變數(shù)據(jù)類型,純函數(shù)等都是函數(shù)式編程中的概念。在React中這些概念很常見,因?yàn)镽eact中很多涉及到函數(shù)式編程的概念。想要具體了解什么是函數(shù)式編程,可以查看 JS函數(shù)式編程指南
add(1)(2)(3)
我們可以自己先嘗試寫一個(gè)add(1)(2)(3)
const add = x => y => z => x + y + z;
console.log(add(1)(2)(3));
看起來并不是那么難,但是如果面試官的要求是實(shí)現(xiàn)一個(gè)add 函數(shù),同時(shí)支持下面這幾種的用法呢
add(1, 2, 3);
add(1, 2)(3);
add(1)(2, 3);
如果還是按照上面的這種思路,我們是不是要寫很多種呢...
我們當(dāng)然可以自己實(shí)現(xiàn)一個(gè)工具函數(shù)專門來生成 柯里化 函數(shù)。
主要思路是什么呢,要判斷當(dāng)前傳入函數(shù)的參數(shù)個(gè)數(shù) (args.length) 是否大于等于原函數(shù)所需參數(shù)個(gè)數(shù) (fn.length) ,如果是,則執(zhí)行當(dāng)前函數(shù);如果是小于,則返回一個(gè)函數(shù)。
const curry = (fn, ...args) =>
// 函數(shù)的參數(shù)個(gè)數(shù)可以直接通過函數(shù)數(shù)的.length屬性來訪問
args.length >= fn.length // 這個(gè)判斷很關(guān)鍵!!!
// 傳入的參數(shù)大于等于原始函數(shù)fn的參數(shù)個(gè)數(shù),則直接執(zhí)行該函數(shù)
? fn(...args)
/**
* 傳入的參數(shù)小于原始函數(shù)fn的參數(shù)個(gè)數(shù)時(shí)
* 則繼續(xù)對(duì)當(dāng)前函數(shù)進(jìn)行柯里化,返回一個(gè)接受所有參數(shù)(當(dāng)前參數(shù)和剩余參數(shù)) 的函數(shù)
*/
: (..._args) => curry(fn, ...args, ..._args);
function add1(x, y, z) {
return x + y + z;
}
const add = curry(add1);
console.log(add(1, 2, 3));
console.log(add(1)(2)(3));
console.log(add(1, 2)(3));
console.log(add(1)(2, 3));
Ramda
Ramda 中的函數(shù)所有都支持柯里化。也就是說,所有的多參數(shù)函數(shù),默認(rèn)都可以使用單參數(shù)函數(shù)。
還是舉上面的例子
const addThreeNumbers = (x, y, z) => x + y + z;
const curriedAddaddThreeNumbers = R.curry(addThreeNumbers);
const f = curriedAddaddThreeNumbers(1, 2);
console.log(f(3));
大名鼎鼎的 lodash 中也提供了 柯里化 函數(shù) ,那么它和Ramda
有什么區(qū)別呢
lodash
是一個(gè)很強(qiáng)大的工具函數(shù)庫,比如 節(jié)流,防抖,深拷貝等等,只要引入 lodash ,我們就可以直接使用。
Ramda
是一個(gè)函數(shù)式編程的理念的函數(shù)庫。
柯里化有什么作用
主要有3個(gè)作用: 參數(shù)復(fù)用、提前返回和 延遲執(zhí)行
我們來簡單的解釋一下:
參數(shù)復(fù)用:拿上面 f
這個(gè)函數(shù)舉例,只要傳入一個(gè)參數(shù) z
,執(zhí)行,計(jì)算結(jié)果就是 1 + 2 + z
的結(jié)果,1 和 2 這兩個(gè)參數(shù)就直接可以復(fù)用了。
提前返回 和 延遲執(zhí)行 也很好理解,因?yàn)槊看握{(diào)用函數(shù)時(shí),它只接受一部分參數(shù),并返回一個(gè)函數(shù)(提前返回),直到(延遲執(zhí)行)傳遞所有參數(shù)為止。