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 相关文章推荐
jquery 元素相对定位代码
Oct 15 Javascript
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
Aug 14 Javascript
JS循环遍历JSON数据的方法
Jul 08 Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
Mar 01 Javascript
jQuery中next方法用法实例
Apr 24 Javascript
jQuery EasyUI之DataGrid使用实例详解
Jan 04 Javascript
在JavaScript中使用JSON数据
Feb 15 Javascript
jQuery插件FusionCharts实现的2D柱状图效果示例【附demo源码下载】
Mar 06 Javascript
jQuery实现文字超过1行、2行或规定的行数时自动加省略号的方法
Mar 28 jQuery
vue中如何实现后台管理系统的权限控制的方法示例
Sep 19 Javascript
Vue-Ant Design Vue-普通及自定义校验实例
Oct 24 Javascript
详解JavaScript中分解数字的三种方法
Jan 05 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新手上路(十三)
2006/10/09 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
解析PHP处理换行符的问题 \r\n
2013/06/13 PHP
php中get_meta_tags()、CURL与user-agent用法分析
2014/12/16 PHP
php实现数组按指定KEY排序的方法
2015/03/30 PHP
PHP会话操作之cookie用法分析
2016/09/28 PHP
Laravel如何同时连接多个数据库详解
2019/08/13 PHP
PHP+Mysql分布式事务与解决方案深入理解
2021/02/27 PHP
使用Firebug对js进行断点调试的图文方法
2011/04/02 Javascript
Javascript变量的作用域和作用域链详解
2015/04/02 Javascript
自己动手制作基于jQuery的Web页面加载进度条插件
2016/06/03 Javascript
jquery实用技巧之输入框提示语句
2016/07/28 Javascript
nodejs入门教程四:URL相关模块用法分析
2017/04/24 NodeJs
详解javascript中的变量提升和函数提升
2018/05/24 Javascript
jQuery实现的响应鼠标移动方向插件用法示例【附源码下载】
2018/08/28 jQuery
详解小程序之简单登录注册表单验证
2019/05/13 Javascript
js的新生代垃圾回收知识点总结
2019/08/22 Javascript
layui实现把数据表格时间戳转换为时间格式的例子
2019/09/12 Javascript
Node中对非阻塞I/O、事件循环的知识点总结
2020/01/05 Javascript
原生JS利用transform实现banner的无限滚动示例代码
2020/06/15 Javascript
[01:32]DOTA2 2015国际邀请赛中国区预选赛第四日战报
2015/05/29 DOTA
高性能web服务器框架Tornado简单实现restful接口及开发实例
2014/07/16 Python
对Python _取log的几种方式小结
2019/07/25 Python
python3 常见解密加密算法实例分析【base64、MD5等】
2019/12/19 Python
python 瀑布线指标编写实例
2020/06/03 Python
CSS3 实现图形下落动画效果
2020/11/13 HTML / CSS
Levi’s西班牙官方网站:李维斯,著名的牛仔裤品牌
2020/08/20 全球购物
高中地理教学反思
2014/01/29 职场文书
禁毒宣传标语
2014/06/19 职场文书
优秀班组事迹材料
2014/12/24 职场文书
小班下学期个人总结
2015/02/12 职场文书
基督教追悼会答谢词
2015/09/29 职场文书
先进工作者主要事迹材料
2015/11/03 职场文书
创业计划书之水果店
2019/07/18 职场文书
MySQL5.7并行复制原理及实现
2021/06/03 MySQL
ajax请求前端跨域问题原因及解决方案
2021/10/16 Javascript