Skip to content

函数柯里化 #26

Open
Open
@keep-run

Description

@keep-run

'函数柯里化可以理解为分步接受函数的参数,等到接受完毕的时候,最终执行'。下边以实现累加为例,具体剖析:

简易版

function curry(fn){
  let args=[]
  return function next(...params){
     if(params.length>0){          // 还有参数,则接着返回函数
       args=[...args,...params];
       return next
     }else{        //没有参数,表明接受完所有的参数了
       return fn(...args)
     }
  }
}

let add=curry(function(...items){
  return items.reduce((prev,cur)=>{return prev+cur},0)
})

console.log(add(1)(2,3)())   //6

升级版

在简易版中,判断最终执行函数的条件是传入了空参数。有没有办法省略这一步呢。其实是有以下两个办法:

  • js获取某个值的时候,会根据语境隐式调用toString或者valueOf函数。
  • 判断函数的参数长度;

toString或者valueOf方法

function curry(fn) {
  let args = []
  function next(...params) {
    args = [...args, ...params]
    return next
  }
  next.toString = function () {
    return fn(...args)
  }
  next.valueOf=function(){
    return fn(...args)
  }
  return next
}

let add = curry(function (...items) {
  return items.reduce((prev, cur) => { return prev + cur }, 0)
})

//case1
let res = add(1)(2, 3)
console.log(res)  //{ [Function: next] toString: [Function], valueOf: [Function] } //没有求值的操作,所以打印了函数。
// case2
let res = add(1)(2, 3)+0     //求值的操作,会调用`valueOf`。
console.log(res)  //6     

判断函数的参数长度

这种方法适用于预先知道需要柯里化的函数的参数个数。

function curry(fn) {
  let allArgs = []
  let len = fn.length
  return function next(...args) {
    allArgs = [...allArgs, ...args]
    if (allArgs.length === len){
      return fn(...allArgs)
    }else{
      return next
    }
  }
}
let add = curry(function (p1, p2, p3) {
  return p1 + p2 + p3
})
console.log('res',add(1)(2,3))   //6

或者curry函数接受第二个参数,显示指定参数的个数。

function curry(fn,len) {
  let allArgs = []
  return function next(...args) {
    allArgs = [...allArgs, ...args]
    if (allArgs.length === len){
      return fn(...allArgs)
    }else{
      return next
    }
  }
}
let add = curry(function (...items) {
  return items.reduce((prev, cur) => { return prev + cur }, 0)
},3)

console.log('res',add(1)(2,3)) //6

补充,函数的length

上面的例子用到了函数的length属性,length表示函数预期输入的参数个数,那么给了参数默认值的,或者rest参数,都不在length中。且给了默认值后面的没有默认值的参数,也不再length中。比如:

(function(a,b,c){}).length                //3   正常使用
(function(a,b,...args){}).length       //2    rest参数不计入
(function(a,b,c=1){}).length           //2    默认值的参数不计入
(function(a,b=2,c){}).length          //1     默认值之后的参数也不计入

参考文献:
1、https://juejin.im/post/5b561426518825195f499772
2、https://es6.ruanyifeng.com/#docs/function#rest-%E5%8F%82%E6%95%B0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions