JavaScript中的函数柯里化与反柯里化
高阶函数
// 递归写法(比较绕,但是可操作性更强,可以在继续下一轮参数收集前做其他处理)
function curry(fn, ...params) {
let _args = params || [] // 提前传递的部分参数
let len = fn.length // 原函数的参数个数
return (...rest) => {
Array.prototype.push.call(_args, ...rest)
console.log('_args :', _args, ', rest :', ...rest)
if (_args.length >= len) { // 收集到的参数大于等于原始函数的参数数量,则执行原函数
// 置空之后,柯里化后的函数可以在满足调用条件之后,继续开始新一轮的参数收集,
// 否则该函数在第一次满足参数收集后,之后的调用都是返回第一次收集完参数调用的结果
/**
* 不置空
*/
// return fn.apply(this, _args) // 收集到的参数满足原函数参数个数,则执行原函数
/**
* 置空
*/
let _newArgs = Array.from(_args)
_args = []
return fn.apply(this, _newArgs)
}
return curry.call(this, fn, ..._args)
}
}
2、具名函数写法(更浅显易懂,明确的返回具名函数)
// 具名函数写法(更浅显易懂,明确的返回具名函数)
function curry(fn, ...params) {
let _args = params || []
let len = fn.length
return function _fn(...rest) {
Array.prototype.push.call(_args, ...rest)
console.log('_args :', _args, ', rest :', ...rest)
if (_args.length >= len) {
/**
* 不置空
*/
// return fn.apply(this, _args)
/**
* 置空
*/
let _newArgs = Array.from(_args)
_args = []
return fn.apply(this, _newArgs)
}
return _fn
}
}
例子
// 输出日志函数
// 柯里化后,收集完所有参数后,才执行,只被执行一次
function log(sec, min, hour) {
console.log('sec, min, hour: ', sec, min, hour)
}
let curryLog = curry(log) // _args : [] curryLog('3s') // _args : [ '3s' ] , rest : 3s --- 未收集满原函数参数个数,即不满足 _args.length >= len 条件,递归执行 curry 函数/ 返回具名函数
curryLog('8m') // _args : [ '3s', '8m' ] , rest : 8m --- 未收集满原函数参数个数,即不满足 _args.length >= len 条件,递归执行 curry 函数/ 返回具名函数
curryLog('0h')
// _args : [ '3s', '8m', '0h' ] , rest : 0h
// sec, min, hour: 3s 8m 0h -- 收集满参数(这里参数有三个),执行原函数
看看置空与不置空的情况
不置空,接上面代码执行下去
_args = [] // 不置空
curryLog('5s')
// sec, min, hour: 3s 8m 0h
// _args : [ '3s', '8m', '0h', '5s' ] , rest : 5s curryLog('6h')
// _args : [ '3s', '8m', '0h', '5s', '6h' ] , rest : 6h
// sec, min, hour: 3s 8m 0h curryLog('1h')
// _args : [ '3s', '8m', '0h', '5s', '6h', '1h' ] , rest : 1h
// sec, min, hour: 3s 8m 0h
置空,接上面代码执行下去
// _args = [] // 置空(支持新开一轮收集)
curryLog('5s') // _args : [ '5s' ] , rest : 5s
curryLog('6h') // _args : [ '5s', '6h' ] , rest : 6h
curryLog('1h')
// _args : [ '5s', '6h', '1h' ] , rest : 1h
// sec, min, hour: 5s 6h 1h
第二种
// 递归写法(比较绕,但是可操作性更强,可以在继续下了一轮参数收集前做其他处理)
function curry(fn, ...params) {
let _args = params || []
return (...rest) => {
console.log('_args :', _args, ', rest :', ...rest)
if (rest.length === 0) { // 与上面的差别在于条件判断,只要传的参数为空,即执行原函数
// 是否需要置空,与上面分析情况一样
/**
* 不置空
*/
// return fn.apply(this, _args)
/**
* 置空
*/
let _newArgs = Array.from(_args)
_args = []
return fn.apply(this, _newArgs)
}
Array.prototype.push.call(_args, ...rest) // 自己控制最后执行时机,当前语句放于 if 判断之后,减少执行
return curry.call(this, fn, ..._args)
}
}
// 具名函数写法(更浅显易懂,明确的返回具名函数)
function curry(fn, ...params) {
let _args = params || []
return function _fn(...rest) { // 此处使用具名函数,用于 return,这么做逻辑更清晰;就不用像上面注释的那样,递归调用 curry 函数
console.log('_args :', _args, ', rest :', ...rest)
if (rest.length === 0) {
/**
* 不置空
*/
// return fn.apply(this, _args)
/**
* 置空
*/
let _newArgs = Array.from(_args)
_args = []
return fn.apply(this, _newArgs)
}
Array.prototype.push.call(_args, ...rest) // 自己控制最后执行时机,当前语句放于 if 判断之后,减少执行
return _fn
}
}
例子
// 输出日志函数
// 柯里化后,收集完所有参数后,才执行,只被执行一次
function log(sec, min, hour) {
console.log('sec, min, hour: ', sec, min, hour)
}
let curryLog = curry(log) curryLog('3s') // _args : [] , rest : 3s
curryLog('8m') // _args : [ '3s' ] , rest : 8m
curryLog('0h') // _args : [ '3s', '8m' ] , rest : 0h curryLog('5s') // _args : [ '3s', '8m', '0h' ] , rest : 5s
curryLog('6h') // _args : [ '3s', '8m', '0h', '5s' ] , rest : 6h
curryLog('1h') // _args : [ '3s', '8m', '0h', '5s', '6h' ] , rest : 1h
curryLog()
// _args : [ '3s', '8m', '0h', '5s', '6h', '1h' ] , rest :
// sec, min, hour: 3s 8m 0h --- 传入参数为空,满足执行条件,只取前三个参数
看看置空与不置空的情况
不置空,接上面代码执行下去
// _args = [] // 不置空
curryLog('5s') // _args : [ '3s', '8m', '0h', '5s', '6h', '1h' ] , rest : 5s
curryLog('6h') // _args : [ '3s', '8m', '0h', '5s', '6h', '1h', '5s' ] , rest : 6h
curryLog('1h') // _args : [ '3s', '8m', '0h', '5s', '6h', '1h', '5s', '6h' ] , rest : 1h
curryLog()
// _args : [ '3s', '8m', '0h', '5s', '6h', '1h', '5s', '6h', '1h' ] , rest :
// sec, min, hour: 3s 8m 0h --- 传入参数为空,满足执行条件,只取前三个参数
// _args = [] // 置空(支持新开一轮收集)
curryLog('5s') // _args : [] , rest : 5s
curryLog('6h') // _args : [ '5s' ] , rest : 6h
curryLog('1h') // _args : [ '5s', '6h' ] , rest : 1h
curryLog()
// _args : [ '5s', '6h', '1h' ] , rest :
// sec, min, hour: 5s 6h 1h --- 传入参数为空,满足执行条件,只取前三个参数
三、应用场景
// 计算月度电费/水费
let calMonthCost = curry(function(...rest) {
let costList = Array.from(rest)
return costList.reduce((prev, cur) => {
return prev + cur
})
})
calMonthCost(1)
calMonthCost(2)
calMonthCost(3)
calMonthCost(4)
calMonthCost(5)
// ...
calMonthCost() // 结果 15
function curry(mode) {
return function(valstr) {
return new RegExp(mode).test(valstr)
}
}
let isMoblie = curry(/\d{11}/)
let isEmail = curry(/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/)
console.log(isMoblie('13911111111')) // true
console.log(isEmail('test@qq.com')) // true
function validate(mode, valstr) {
return new RegExp(mode).test(valstr)
}
let isMoblie = curry(validate, /\d{11}/)
let isEmail = curry(validate, /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/)
console.log(isMoblie('13911111111')) // true
console.log(isEmail('test@qq.com')) // true
Function.prototype.bind = function(context) {
let _this = this
let args = [].slice.call(arguments, 1)
return function() {
return _this.apply(context, args.concat([].slice.call(arguments)))
}
}
四、反柯里化
function uncurrying(fn) {
return function() {
let args = [].slice.call(arguments)
let that = args.shift()
fn.apply(that, args)
}
} let person = {
name: 'jolin',
age: 18
}
let util = {
sayPerson: function(...rest) {
console.log('...rest :', ...rest)
console.log('name: ', this.name, ', age: ', this.age)
}
} let uncurrySayPerson = uncurrying(util.sayPerson)
uncurrySayPerson(person, 'test') // person 代表 util.sayPerson 的上下文,后面的都是参数 util.sayPerson 的参数
// ...rest : test
// name: jolin , age: 18 // 实际上平常我们是这么写的
util.sayPerson.call(person, 'test') // person 代表 util.sayPerson 的上下文,后面的都是参数 util.sayPerson 的参数
// ...rest : test
// name: jolin , age: 18
例子2
Function.prototype.uncurrying = function() {
let _this = this // 这里指 Array.prototype.push
return function() {
return Function.prototype.call.apply(_this, arguments)
// 1、这里暂时将 Function.prototype.call 中的 call 方法叫做 changeFn
// 2、那么 Function.prototype.changeFn.apply(Array.prototype.push, arguments)
// 3、Array.prototype.push.changeFn(arguments)
// 4、changeFn 等于 Function.prototype.call 中的 call 方法
// 5、最终等价于 Array.prototype.push.call(arguments)
// 6、call 方法接受的第一个参数代表上下文,进一步拆分 Array.prototype.push.call(arguments[0], ...arguments[n-1])
}
}
let push = Array.prototype.push.uncurrying()
let obj = {}
push(obj, 'hh') // obj 代表 Array.prototype.push 的上下文,后面的都是参数 Array.prototype.push 的参数 // 实际上平常我们是这么写的
Array.prototype.push.call(obj, 'hh') // obj 代表 Array.prototype.push 的上下文,后面的都是参数 Array.prototype.push 的参数
JavaScript中的函数柯里化与反柯里化的更多相关文章
- 浅析 JavaScript 中的 函数 currying 柯里化
原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...
- 浅析 JavaScript 中的 函数 uncurrying 反柯里化
柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...
- JS 函数的柯里化与反柯里化
===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...
- js高阶函数应用—函数柯里化和反柯里化(二)
第上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题-函数的反柯里化. 在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里 ...
- JS的防抖,节流,柯里化和反柯里化
今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- 前端学习 第六弹: javascript中的函数与闭包
前端学习 第六弹: javascript中的函数与闭包 当function里嵌套function时,内部的function可以访问外部function里的变量 function foo(x) { ...
- JavaScript中Eval()函数的作用
这一周感觉没什么写的,不过在研究dwz源码的时候有一个eval()的方法不是很了解,分享出来一起学习 -->首先来个最简单的理解 eval可以将字符串生成语句执行,和SQL的exec()类似. ...
- 【JavaScript】Javascript中的函数声明和函数表达式
Javascript有很多有趣的用法,在Google Code Search里能找到不少,举一个例子: <script> ~function() { alert("hello, ...
- 谈谈javascript 中的函数问题
聊聊javascript中的函数 本文可作为李刚<疯狂htmlcssjavas讲义>的学习笔记 先说一个题外话 前几天在知乎上流传着一个对联 上联是雷锋推到雷峰塔 nnd 这是什么对联? ...
随机推荐
- elasticsearch启动问题
ES安装完一直启动不了,问题解决. 报错: ERROR: bootstrap checks failed system call filters failed to install; check th ...
- 路由网关--spring cloud zuul
路由网关--spring boot Zuul 1.为什么需要Zuul? Zuul Ribbon 以及 Eureka 相结合,可以实现智能路由和负载均衡的功能, Zuul 能够将请求流量按某种策略分发到 ...
- Jmeter关联之正则表达式提取器(完整版)
Jmeter关联之正则表达式提取器(完整版) 在性能测试中,若想提取上一个请求的结果,作为下一次请求的参数,则需要使用关联~ 这篇博客主要讲jmeter正则表达式提取器的各种用法. 首先正则表达式 ...
- 谈谈你对本次2018级ACM新手赛的体会
第一次参加这类比赛,挺有趣的,在现场磨了四个小时也没有全写出来,收获还是挺大的,至少意识到自己是能做到这些的(笑 今后也会多多努力
- PHP ftp_nb_fget() 函数
定义和用法 ftp_nb_fget() 函数从 FTP 服务器上下载一个文件并保存到本地一个已经打开的文件中.(无阻塞) 该函数返回下列值之一: FTP_FAILED(发送/获取失败) FTP_FIN ...
- vue基础五
条件渲染 1.v-if 1.1<template>中v-if条件组 因为 v-if 是一个指令,需要将它添加到一个元素上.但是如果我们想切换多个元素呢?此时我们可以把一个<templ ...
- Android中的ListView的绘制过程中执行的方法
首先,系统在绘制ListView之前, 将会先调用getCount方法来获取Item的个数.(如果getCount方法返回0的话,列表时不显示任何内容的) 之后每绘制一个 Item就会调用一次getV ...
- 前缀和+排序——cf1043E
先不考虑第二个条件 要求i和所有其他人的分数和最小,选择x还是y,可以推出一个公式,即差xi-yi小的j都选y,反之都选x 那么按照xi-yi排序即可 然后再考虑第二个条件,做减法就行 /* xi+y ...
- Openstack组件部署 — Networking service_安装并配置Controller Node
目录 目录 前文列表 前提条件 网络环境 完成下面的步骤以创建数据库 创建service credentials服务凭证 创建Neutron的API Endpoints 配置自服务网络 安装网络组件 ...
- Codeforces 1169A Circle Metro
题目链接:codeforces.com/contest/1169/problem/A 题意:有俩个地铁,一个从 1 → 2 → …→ n → 1→ 2 →…, 一个 从 n → n-1 →…→ 1 → ...