一文帶你搞懂JavaScript Currying(柯里化)函數(shù)
大家好,我是進(jìn)階學(xué)習(xí)者。
一、什么柯里化(Currying)?
柯里化(Currying)是一種關(guān)于函數(shù)的高階技術(shù)。它不僅被用于 JavaScript,還被用于其他編程語言。
柯里化是一種函數(shù)的轉(zhuǎn)換,它是指將一個函數(shù)從可調(diào)用的 f(a, b, c) 轉(zhuǎn)換為可調(diào)用的 f(a)(b)(c)??吕锘粫{(diào)用函數(shù)。它只是對函數(shù)進(jìn)行轉(zhuǎn)換。
二、案例
例:
將創(chuàng)建一個輔助函數(shù) curry(f),該函數(shù)將對兩個參數(shù)的函數(shù) f 執(zhí)行柯里化。換句話說,對于兩個參數(shù)的函數(shù) f(a, b) 執(zhí)行 curry(f) 會將其轉(zhuǎn)換為以 f(a)(b) 形式運(yùn)行的函數(shù):
- function curry(f) { // curry(f) 執(zhí)行柯里化轉(zhuǎn)換
- return function(a) {
- return function(b) {
- return f(a, b);
- };
- };
- }
- // 用法
- function sum(a, b) {
- return a + b;
- }
- let curriedSum = curry(sum);
- alert( curriedSum(1)(2) ); // 3
運(yùn)行結(jié)果:
注:
正如所看到的,實(shí)現(xiàn)非常簡單:只有兩個包裝器(wrapper)。
- curry(func) 的結(jié)果就是一個包裝器 function(a)。
- 當(dāng)它被像 curriedSum(1) 這樣調(diào)用時,它的參數(shù)會被保存在詞法環(huán)境中,然后返回一個新的包裝器 function(b)。
- 然后這個包裝器被以 2 為參數(shù)調(diào)用,并且,它將該調(diào)用傳遞給原始的 sum 函數(shù)。
三、目的是什么?它有什么好處?
例:
要了解,需要一個實(shí)際中的例子。
有一個用于格式化和輸出信息的日志(logging)函數(shù) log(date, importance, message)。在實(shí)際項(xiàng)目中,此類函數(shù)具有很多有用的功能,例如通過網(wǎng)絡(luò)發(fā)送日志(log),在這兒僅使用 alert:
- function log(date, importance, message) { alert([${date.getHours()}:${date.getMinutes()}] [${importance}] ${message});
- }
讓將它柯里化!
- log = _.curry(log);
柯里化之后,log 仍正常運(yùn)行:
- log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
運(yùn)行結(jié)果:
……但是也可以以柯里化形式運(yùn)行:
- log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
運(yùn)行結(jié)果:
現(xiàn)在,可以輕松地為當(dāng)前日志創(chuàng)建便捷函數(shù):
- // logNow 會是帶有固定第一個參數(shù)的日志的偏函數(shù)
- let logNow = log(new Date());// 使用它
- logNow("INFO", "message"); // [HH:mm] INFO message
現(xiàn)在,logNow 是具有固定第一個參數(shù)的 log,換句話說,就是更簡短的“偏應(yīng)用函數(shù)(partially applied function)”或“偏函數(shù)(partial)”。
可以更進(jìn)一步,為當(dāng)前的調(diào)試日志(debug log)提供便捷函數(shù):
- let debugNow = logNow("DEBUG");
- debugNow("message"); // [HH:mm] DEBUG message
注:
柯里化之后,沒有丟失任何東西:log 依然可以被正常調(diào)用。
可以輕松地生成偏函數(shù),例如用于生成今天的日志的偏函數(shù)。
四、高級柯里化實(shí)現(xiàn)
下面是用于多參數(shù)函數(shù)的“高級”柯里化實(shí)現(xiàn),也可以把它用于上面的示例。
- function curry(func) {
- return function curried(...args) {
- if (args.length >= func.length) {
- return func.apply(this, args); }
- else { return function(...args2) {
- return curried.apply(this, args.concat(args2));
- }
- }
- };
- }
例:
- function sum(a, b, c) {
- return a + b + c;}
- let curriedSum = curry(sum);
- alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常調(diào)用
- alert( curriedSum(1)(2,3) ); // 6,對第一個參數(shù)的柯里化
- alert( curriedSum(1)(2)(3) ); // 6,全柯里化
運(yùn)行結(jié)果:
五、總結(jié)
本文基于JavaScript基礎(chǔ),介紹了Currying 函數(shù)。Currying 是一種轉(zhuǎn)換,將 f(a,b,c) 轉(zhuǎn)換為可以被以 f(a)(b)(c) 的形式進(jìn)行調(diào)用。
JavaScript 實(shí)現(xiàn)通常都保持該函數(shù)可以被正常調(diào)用,并且如果參數(shù)數(shù)量不足,則返回偏函數(shù)。Currying 函數(shù)讓能夠更容易地獲取偏函數(shù)。通過按案例的分析進(jìn)行詳細(xì)的講解。
代碼很簡單,希望對你學(xué)習(xí)有幫助。
本文轉(zhuǎn)載自微信公眾號「前端進(jìn)階學(xué)習(xí)交流」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系前端進(jìn)階學(xué)習(xí)交流公眾號。