Currying?is an advanced technique of working with functions. It’s used not only in JavaScript, but in other languages as well.
Currying is a transformation of functions that translates a function from callable as?f(a, b, c)?into callable as?f(a)(b)(c).
Currying doesn’t call a function. It just transforms it.
https://javascript.info/currying-partials
As you can see, the implementation is straightforward: it’s just two wrappers.
The result of?curry(func)?is a wrapper?function(a).
When it is called like?curriedSum(1), the argument is saved in the Lexical Environment, and a new wrapper is returned?function(b).
Then this wrapper is called with?2?as an argument, and it passes the call to the original?sum.
Currying is a pattern where a function with more than one parameter is broken into multiple functions that, when called in series, will accumulate all of the required parameters one at a time. This technique can be useful for making code written in a functional style easier to read and compose. It's important to note that for a function to be curried, it needs to start out as one function, then broken out into a sequence of functions that each accepts one parameter.
// here ...args collects arguments as array (rest)
// Here we check if current args passed equals the number of args func expects
// if yes, we spread args elements to pass into func (spread). This is our base case.
/* if not, we return a function that collects the next arguments passed in next and we recursively call curriedFunc, accumalating and spreading the values of args first and then the values of next. next will take into consideration a variable amount of next arguments e.g (1, 2) (1) (1,2,3) */
function curry(func){
? ? return function curriedFunc(...args){
? ? ? ? if(args.length>=func.length){
? ? ? ? ? ? return func(...args)
????????}else{
????????????return function(...next){
????????????????return curriedFunc(...args,...next);
????????????}
????????}
????}
}
const join=(a,b,c)=>{return`${a}_$潦闲_${c}`}
const curriedJoin=curry(join)
curriedJoin(1, 2, 3) // '1_2_3'
curriedJoin(1)(2, 3) // '1_2_3'
curriedJoin(1,2)(3)// '1_2_3'
function curry(func){
????return function curried(...args){
????????const complete=args.length>=func.length
????????????&&!args.slice(0,func.length).includes(curry.placeholder);
????????if(complete) return func.apply(this,args)
????????return function(...newArgs){
// replace placeholders in args with values from newArgs
const res=args.map(arg=>arg===curry.placeholder&&newArgs.length?newArgs.shift():arg);
????????return curried(...res,...newArgs);
????????}
????}
}
curry.placeholder=Symbol()
Function: length
The?length?data property of a?Function?instance indicates the number of parameters expected by the function.
function curry(fn){
????return function curryInner(...args){
????????if(args.length>=fn.length) return fn(...args);
????????return(...args2)=>curryInner(...args,...args2);
????};
}
reference:https://cloud.tencent.com/developer/article/1431398
柯里化,又稱部分求值,一個(gè)currying的函數(shù)首先會(huì)接受一些參數(shù)哺壶,接受這些部分參數(shù)后,函數(shù)并不會(huì)立即求值横殴,而是繼續(xù)返回另一個(gè)函數(shù)昆禽,部分參數(shù)在函數(shù)形成的閉包中被保存起來,待到函數(shù)被真正需要求值的時(shí)候择卦,之前傳入的所有參數(shù)都會(huì)被一次性用于求值敲长。
柯里化的作用就是將普通函數(shù)轉(zhuǎn)變成高階函數(shù),實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建函數(shù)秉继、延遲計(jì)算祈噪、參數(shù)復(fù)用等作用。
把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù)尚辑,并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)辑鲤。
fn(1, 2, 3, 4) -> fn(1)(2)(3)(4)()
假設(shè)這個(gè)函數(shù)是用于求和,那么就是把本來接收多個(gè)參數(shù)一次性求和的函數(shù)改成了接收單一參數(shù)逐個(gè)求和的函數(shù)杠茬,這樣是不是容易理解了月褥。
currying函數(shù): 判斷傳入的參數(shù)長(zhǎng)度是否為0,若為0執(zhí)行函數(shù)瓢喉,否則收集參數(shù)到args數(shù)組宁赤;另一種常見的應(yīng)用是bind函數(shù)。
example1:
// 通用currying函數(shù)栓票,接受一個(gè)參數(shù)fn(即要被currying的函數(shù))
var currying = function(fn) {
? ? var args = [];
? ? return function curried() {
? ? ? ? if (arguments.length === 0) {
? ? ? ? ? ? return fn.apply(this, args);
? ? ? ? } else {
? ? ? ? ? ? [].push.apply(args, arguments);
? ? ? ? ? ? return arguments.callee;
? ? ? ? }
? ? }
};
// 將被currying的函數(shù)
var cost = (function() {
? ? var money = 0;
? ? return function() {
? ? ? ? for (var i = 0, l = arguments.length; i < l; i++) {
? ? ? ? ? ? money += arguments[i];
? ? ? ? }
? ? ? ? return money;
? ? }
})();
var cost = currying( cost );? ? // 轉(zhuǎn)化成currying函數(shù)
cost( 100 );? ? // 未真正求值
cost( 200 );? ? // 未真正求值
cost( 300 );? ? // 未真正求值
console.log (cost());? ? // 求值并輸出:600
example2:
實(shí)現(xiàn)上就是返回一個(gè)高階函數(shù)决左,通過閉包把傳入的參數(shù)保存起來。當(dāng)傳入的參數(shù)數(shù)量不足時(shí),遞歸調(diào)用bind方法哆窿;數(shù)量足夠時(shí)則立即執(zhí)行函數(shù)链烈。
function curry(fn){
????const len=fn.length;
????return function curried(...args){
????????if(args.length<len){
????????????return curried.bind(null,...args);
????????}
????????return fn.apply(null,args);
????}
}
ES6極簡(jiǎn)寫法
const currying = fn =>?
? ? curried = (...args1) =>?
????????args1.length>=fn.length
????????? fn(...args1)?
????????: (...args2) => curried(...args1,...args2)
fn.length表示函數(shù)的所有參數(shù)個(gè)數(shù)嗎?不是挚躯,函數(shù)的length屬性獲取的是形參的個(gè)數(shù)
如果很難理解强衡,看下面例子:
function currying(fn,length){
????length=length || fn.length;
????return function(...args){
????????return args.length>=length
?????????????fn.apply(this,args)
????????????:currying(fn.bind(this,...args),length-args.length)
????}
}
JS的API哪些應(yīng)用到了函數(shù)柯里化的實(shí)現(xiàn)?
實(shí)現(xiàn)一個(gè)sum函數(shù)码荔,sum(1,2)(3).valueOf()這樣調(diào)用后的結(jié)果為6
function?sum(...rest){
????let args=?[...rest];
????const?f?=?function(...others){
????????args=?args.concat(others);
????????return?f;
????};
????f.valueOf?=?function()?{
????????let?result?=?0;
????????for(let?val?of?args)?{
????????????result?+=?val;
????????}
????????return?result;
????}
????return?f;
}