ES6如何用一句代码实现函数的柯里化


Posted in Javascript onJanuary 18, 2020

柯里化是干什么的?首先看看下面这个函数

let store = (a,b,c) => "这是你的七仔面" 
//函数就好像一个小卖部,一碗七仔面要三张软妹币:a,b,c (五个参数)

那函数柯里化是什么?就是我们买面吃的过程可能是这样:

let curryStore = curry(store) //刚刚的小卖部被柯里化了

//最佳状况
curryStore(1,5,1)// 老板,刚好!不用找了,刚好七块钱。老板: "这是你的七仔面" 

//偶尔出现的情况
let boss = curryStore(5) //老板,这五块钱你先拿着,我找找有没有一块钱。老板:……
boss = boss(1) //有了老板,这一块钱你先拿着,我找找还有没有一块钱。。老板:……
boss(1) //哈哈,终于找到了,给!老板:"这是你的七仔面" 

curryStore(5)(1)(1) //等价于上诉情况

所以这里我们可以看到,函数柯里化,是可以用来慢慢凑齐参数,延迟函数的执行。(先分期交钱,后交货!)

做个题

现在,我们的目标是实现一个curry 函数,达到以下使用效果:
在给到足够的参数时,执行函数。不够参数的时候则返回一个新的curry函数。

let curryPlus = curry((a,b,c) => a+b+c) //这里给到一个有三个参数的函数

curryPlus(1)(2)(3) //返回 6
curryPlus(1)(2,3) //返回 6
curryPlus(1,2)(3) //返回 6

let x = curryPlus(1)(2) //喂,怎么才两个参数?返回一个curry函数(已经带了两个参数)
x(1) //返回 4
x(2) //返回 5

作为一个js很厉害的人,这种问题用一行代码解决不是问题,问题是我没那么厉害。。。

于是,我们先从土方法说起吧orz

按照curry的用法,原理是返回了一个函数,这个函数的参数个数不确定(你可能同时掏出两张一块钱),于是我们可以运用不定参数的写法:

const curry = (fn) => {
  return (...args) =>{ //不定参数,想给多少给多少
    //给钱交货环节
  }
}

柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组:

const curry = (fn,arr=[]) => { //arr数组用于记录已有参数
  return (...args) =>{  
    //给钱交货环节
  }
}

接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的curry函数,将现有的参数塞给他:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    //判断参数总数是否和fn参数个数相等
    if([...arr, ...args].length === fn.length){
      return fn(...arr, ...args) //拓展参数,调用fn
    }else{
      return curry(fn,[...arr, ...args]) //迭代,传入现有的所有参数
    }

  }
}

到这里,其实我们已经实现了curry函数。

接下来就是要看看怎么写才能更简洁。
首先,中间的代码可以写成一个立即执行函数,省掉一些...arr, ...args:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => {  //a是一个数组
      if(a.length === fn.length) {
        return fn(...a)
      }else{
        return curry(fn, a)
      }
    })([...arr, ...args]) //这里把arr和args摊开成一个数组赋值给a

  }
}

if语句可以缩减成三元表达式,也可以省掉很多字啦:

const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => { 

      return a.length === fn.length ? fn(...a) : curry(fn, a)

    })([...arr, ...args]) 

  }
}

最后,既然函数里啥事都没干,就只是return的话,不妨使用箭头函数最省的写法 input => output, 把return和大括号都给省了:

const curry = ( fn, arr = []) => {
  return (...args) => { 
    return ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //先折叠一层
  }
}

再折叠:

const curry = ( fn, arr = []) => {
  return (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //再折叠
}

收工了收工了:

const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args]) 
//衣服给您叠好了

拿去试试:

const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])
let curryPlus = curry((a,b,c,d)=>a+b+c+d)

curryPlus(1,2,3)(4) //返回10
curryPlus(1,2)(4)(3) //返回10
curryPlus(1,2)(3,4) //返回10

当然了,柯里化函数的主要作用还是延迟执行,执行的触发条件不一定是参数个数相等,也可以是其他的条件,例如参数个为0的情况,那么我们需要对上面curry函数稍微做修改:

const curry = ( fn, arr = []) => (...args) => ( (a,b) => b.length === 0? fn(...a) : curry(fn, a))([...arr, ...args],[...args])
let curryPlus = curry((...x)=>x.reduce((a,b)=>a+b))

curryPlus(1) //返回一个函数
curryPlus(1)(2) //返回一个函数

//遇到参数个数为0的情况才执行
curryPlus(1)(2)(4)() //返回7
curryPlus(1,2)(4)() //返回7

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
IE8 原生JSON支持
Apr 13 Javascript
javascript 写的一个简单的timer
Jul 30 Javascript
JavaScript中常见陷阱小结
Apr 27 Javascript
js取消单选按钮选中并判断对象是否为空
Nov 14 Javascript
百度移动版的url编码解码示例
Apr 29 Javascript
javascript中in运算符用法分析
Apr 28 Javascript
Javascript类型转换的规则实例解析
Feb 23 Javascript
浅谈javascript中的constructor
Jun 08 Javascript
利用JQuery直接调用asp.net后台的简单方法
Oct 27 Javascript
浅谈如何使用webpack构建多页面应用
May 30 Javascript
Vue开发环境跨域访问问题
Jan 22 Javascript
JS实现简易留言板增删功能
Feb 08 Javascript
ES6 Object.assign()的用法及其使用
Jan 18 #Javascript
vue项目中监听手机物理返回键的实现
Jan 18 #Javascript
vue组件内部引入外部js文件的方法
Jan 18 #Javascript
Node.js实现批量下载图片简单操作示例
Jan 18 #Javascript
vue实现微信浏览器左上角返回按钮拦截功能
Jan 18 #Javascript
vue+elementUi 实现密码显示/隐藏+小图标变化功能
Jan 18 #Javascript
JS数组方法slice()用法实例分析
Jan 18 #Javascript
You might like
PHP微框架Dispatch简介
2014/06/12 PHP
JS 文字符串转换unicode编码函数
2009/05/30 Javascript
理解Javascript_07_理解instanceof实现原理
2010/10/15 Javascript
jQuery输入城市查看地图使用介绍
2013/05/08 Javascript
JS取文本框中最小值的简单实例
2013/11/29 Javascript
JS判断两个时间大小的示例代码
2014/01/28 Javascript
Node.js(安装,启动,测试)
2014/06/09 Javascript
如何动态加载外部Javascript文件
2015/12/02 Javascript
jQuery之动画效果大全
2016/11/09 Javascript
Bootstrap Table 删除和批量删除
2017/09/22 Javascript
JavaScript体验异步更好的解决办法
2018/01/08 Javascript
通过npm或yarn自动生成vue组件的方法示例
2019/02/12 Javascript
利用Promise自定义一个GET请求的函数示例代码
2019/03/20 Javascript
详解js创建对象的几种方法及继承
2019/04/12 Javascript
JS中实现浅拷贝和深拷贝的代码详解
2019/06/05 Javascript
使用vscode快速建立vue模板过程详解
2019/10/10 Javascript
node.js域名解析实现方法详解
2019/11/05 Javascript
Vue 按照创建时间和当前时间显示操作(刚刚,几小时前,几天前)
2020/09/10 Javascript
Node 使用express-http-proxy 做api网关的实现
2020/10/15 Javascript
Python 学习笔记
2008/12/27 Python
python将ip地址转换成整数的方法
2015/03/17 Python
Django中使用 Closure Table 储存无限分级数据
2019/06/06 Python
python安装cx_Oracle和wxPython的方法
2020/09/14 Python
python爬取股票最新数据并用excel绘制树状图的示例
2021/03/01 Python
美国第一香水网站:Perfume.com
2017/01/23 全球购物
Dodax奥地利:音乐、电影、书籍、玩具、电子产品等
2019/08/31 全球购物
Helly Hansen工作服美国官方网上商店:为最恶劣的环境
2019/09/04 全球购物
俄罗斯电动工具和设备购物网站:Vseinstrumenti.ru
2020/11/12 全球购物
C/C++有关内存的思考题
2015/12/04 面试题
报到证丢失证明
2014/01/11 职场文书
元旦活动感言
2014/03/08 职场文书
2014党支部对照检查材料思想汇报
2014/10/05 职场文书
小学少先队辅导员述职报告
2015/01/10 职场文书
2015年事业单位工作总结
2015/04/27 职场文书
小学生暑假安全公约
2015/07/14 职场文书
Elasticsearch 配置详解
2022/04/19 Java/Android