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 相关文章推荐
多广告投放代码 推荐
Nov 13 Javascript
js浮动图片的动态效果
Jul 10 Javascript
javascript删除数组元素并且数组长度减小的简单实例
Feb 14 Javascript
多种js图片预加载实现方式分享
Feb 19 Javascript
jQuery 实现鼠标画框并对框内数据选中的实例代码
Aug 29 jQuery
jquery学习笔记之无new构建详解
Dec 07 jQuery
Vue一个案例引发的递归组件的使用详解
Nov 15 Javascript
微信小程序picker组件关于objectArray数据类型的绑定方法
Mar 13 Javascript
详解jQuery中的getAll()和cleanData()
Apr 15 jQuery
基于jQuery实现可编辑的表格
Dec 11 jQuery
JavaScript实现轮播图效果
Oct 30 Javascript
vue-router中hash模式与history模式的区别
Jun 23 Vue.js
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
swoole和websocket简单聊天室开发
2017/11/18 PHP
PHP实现关键字搜索后描红功能示例
2019/07/03 PHP
利用jquery的获取JS文件中的字符串内容
2012/02/14 Javascript
关于js遍历表格的实例
2013/07/10 Javascript
文本框(input)获取焦点(onfocus)时样式改变的示例代码
2014/01/10 Javascript
js 判断控件获得焦点的示例代码
2014/03/04 Javascript
javascript进行数组追加方法小结
2014/06/16 Javascript
jquery实现顶部向右伸缩的导航区域代码
2015/09/02 Javascript
浅谈jQuery中的$.extend方法来扩展JSON对象
2017/02/12 Javascript
ReactNative 之FlatList使用及踩坑封装总结
2017/11/29 Javascript
jQuery实现模糊查询的方法分析
2018/05/10 jQuery
vue实现压缩图片预览并上传功能(promise封装)
2019/01/10 Javascript
vue计算属性computed的使用方法示例
2019/03/13 Javascript
webpack 代码分离优化快速指北
2019/05/18 Javascript
JavaScript 继承 封装 多态实现及原理详解
2019/07/29 Javascript
layui 地区三级联动 form select 渲染的实例
2019/09/27 Javascript
vue实现登录拦截
2020/06/29 Javascript
Python Deque 模块使用详解
2014/07/04 Python
Python算法之求n个节点不同二叉树个数
2017/10/27 Python
python 日志增量抓取实现方法
2018/04/28 Python
Python DataFrame设置/更改列表字段/元素类型的方法
2018/06/09 Python
django进阶之cookie和session的使用示例
2018/08/17 Python
10 行 Python 代码教你自动发送短信(不想回复工作邮件妙招)
2018/10/11 Python
Python BeautifulSoup [解决方法] TypeError: list indices must be integers or slices, not str
2019/08/07 Python
jupyter notebook 增加kernel教程
2020/04/10 Python
搭建pypi私有仓库实现过程详解
2020/11/25 Python
CSS3制作翻转效果_动力节点Java学院整理
2017/07/11 HTML / CSS
泰国第一在线超市:Tops
2021/02/13 全球购物
电子商务专员岗位职责
2013/12/11 职场文书
一年级学生评语
2014/04/23 职场文书
党员廉洁自律个人总结
2015/02/13 职场文书
王亚平太空授课观后感
2015/06/12 职场文书
寻找最美乡村教师观后感
2015/06/18 职场文书
2015年环卫处个人工作总结
2015/07/27 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
html5实现点击弹出图片功能
2021/07/16 HTML / CSS