前言
本文主要從 3W (what, how, why) 角度出發(fā)通俗易懂的解釋一下 什么是函數(shù)柯里化尉咕,以及怎么用三行代碼來實現(xiàn) add(1)(2)(3)
這個很常見的面試題呜袁。
什么是函數(shù)柯里化(curry)
函數(shù)柯里化(curry)是函數(shù)式編程里面的概念。curry的概念很簡單:只傳遞給函數(shù)一部分參數(shù)來調(diào)用它根吁,讓它返回一個函數(shù)去處理剩下的參數(shù)锤岸。
簡單點來說就是:每次調(diào)用函數(shù)時擦秽,它只接受一部分參數(shù),并返回一個函數(shù)暮的,直到傳遞所有參數(shù)為止笙以。
舉個??
將下面接受兩個參數(shù)的函數(shù)改為接受一個參數(shù)的函數(shù)。
const add = (x, y) => x + y;
add(1, 2);
改成每次只接受一個參數(shù)的函數(shù)
const add = x => y => x + y;
add(1)(2);
柯里化冻辩,不可變數(shù)據(jù)類型猖腕,純函數(shù)等都是函數(shù)式編程中的概念拆祈。在React中這些概念很常見,因為React中很多涉及到函數(shù)式編程的概念谈息。想要具體了解什么是函數(shù)式編程缘屹,可以查看 JS函數(shù)式編程指南
add(1)(2)(3)
我們可以自己先嘗試寫一個add(1)(2)(3)
const add = x => y => z => x + y + z;
console.log(add(1)(2)(3));
看起來并不是那么難,但是如果面試官的要求是實現(xiàn)一個add 函數(shù)侠仇,同時支持下面這幾種的用法呢
add(1, 2, 3);
add(1, 2)(3);
add(1)(2, 3);
如果還是按照上面的這種思路轻姿,我們是不是要寫很多種呢...
我們當然可以自己實現(xiàn)一個工具函數(shù)專門來生成 柯里化 函數(shù)。
主要思路是什么呢逻炊,要判斷當前傳入函數(shù)的參數(shù)個數(shù) (args.length) 是否大于等于原函數(shù)所需參數(shù)個數(shù) (fn.length) 互亮,如果是,則執(zhí)行當前函數(shù)余素;如果是小于豹休,則返回一個函數(shù)。
const curry = (fn, ...args) =>
// 函數(shù)的參數(shù)個數(shù)可以直接通過函數(shù)數(shù)的.length屬性來訪問
args.length >= fn.length // 這個判斷很關(guān)鍵=暗酢M!
// 傳入的參數(shù)大于等于原始函數(shù)fn的參數(shù)個數(shù)视乐,則直接執(zhí)行該函數(shù)
? fn(...args)
/**
* 傳入的參數(shù)小于原始函數(shù)fn的參數(shù)個數(shù)時
* 則繼續(xù)對當前函數(shù)進行柯里化洛搀,返回一個接受所有參數(shù)(當前參數(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ù)留美,默認都可以使用單參數(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
是一個很強大的工具函數(shù)庫谎砾,比如 節(jié)流,防抖捧颅,深拷貝等等景图,只要引入 lodash ,我們就可以直接使用碉哑。
Ramda
是一個函數(shù)式編程的理念的函數(shù)庫挚币。
柯里化有什么作用
主要有3個作用: 參數(shù)復用、提前返回和 延遲執(zhí)行
我們來簡單的解釋一下:
參數(shù)復用:拿上面 f
這個函數(shù)舉例谭梗,只要傳入一個參數(shù) z
,執(zhí)行宛蚓,計算結(jié)果就是 1 + 2 + z
的結(jié)果激捏,1 和 2 這兩個參數(shù)就直接可以復用了。
提前返回 和 延遲執(zhí)行 也很好理解凄吏,因為每次調(diào)用函數(shù)時远舅,它只接受一部分參數(shù)闰蛔,并返回一個函數(shù)(提前返回),直到(延遲執(zhí)行)傳遞所有參數(shù)為止图柏。