返回顶部
首页 > 资讯 > 前端开发 > JavaScript >前端JavaScript彻底弄懂函数柯里化curry
  • 160
分享到

前端JavaScript彻底弄懂函数柯里化curry

2024-04-02 19:04:59 160人浏览 泡泡鱼
摘要

目录一、什么是柯里化( curry)二、柯里化的用途三、如何封装柯里化工具函数 一、什么是柯里化( curry) 在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系

一、什么是柯里化( curry)

数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

举例来说,一个接收3个参数的普通函数,在进行柯里化后, 柯里化版本的函数接收一个参数并返回接收下一个参数的函数, 该函数返回一个接收第三个参数的函数。 最后一个函数在接收第三个参数后, 将之前接收到的三个参数应用于原普通函数中,并返回最终结果。

数学和计算科学中的柯里化:


// 数学和计算科学中的柯里化:

//一个接收三个参数的普通函数
function sum(a,b,c) {
    console.log(a+b+c)
}

//用于将普通函数转化为柯里化版本的工具函数
function curry(fn) {
  //...内部实现省略,返回一个新函数
}

//获取一个柯里化后的函数
let _sum = curry(sum);

//返回一个接收第二个参数的函数
let A = _sum(1);
//返回一个接收第三个参数的函数
let B = A(2);
//接收到最后一个参数,将之前所有的参数应用到原函数中,并运行
B(3)    // print : 6

而对于javascript语言来说,我们通常说的柯里化函数的概念,与数学和计算机科学中的柯里化的概念并不完全一样。

在数学和计算机科学中的柯里化函数,一次只能传递一个参数;

而我们Javascript实际应用中的柯里化函数,可以传递一个或多个参数。

来看这个例子:


//普通函数
function fn(a,b,c,d,e) {
  console.log(a,b,c,d,e)
}
//生成的柯里化函数
let _fn = curry(fn);

_fn(1,2,3,4,5);     // print: 1,2,3,4,5
_fn(1)(2)(3,4,5);   // print: 1,2,3,4,5
_fn(1,2)(3,4)(5);   // print: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // print: 1,2,3,4,5

对于已经柯里化后的 _fn 函数来说,当接收的参数数量与原函数的形参数量相同时,执行原函数; 当接收的参数数量小于原函数的形参数量时,返回一个函数用于接收剩余的参数,直至接收的参数数量与形参数量一致,执行原函数。

当我们知道柯里化是什么了的时候,我们来看看柯里化到底有什么用?

二、柯里化的用途

柯里化实际是把简答的问题复杂化了,但是复杂化的同时,我们在使用函数时拥有了更加多的自由度。 而这里对于函数参数的自由处理,正是柯里化的核心所在。 柯里化本质上是降低通用性,提高适用性。来看一个例子:

我们工作中会遇到各种需要通过正则检验的需求,比如校验电话号码、校验邮箱、校验身份证号、校验密码等, 这时我们会封装一个通用函数 checkByRegExp ,接收两个参数,校验的正则对象和待校验的字符串


function checkByRegExp(regExp,string) {
    return regExp.test(string);  
}

checkByRegExp(/^1\d{10}$/, '18642838455'); // 校验电话号码
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com'); // 校验邮箱

上面这段代码,乍一看没什么问题,可以满足我们所有通过正则检验的需求。 但是我们考虑这样一个问题,如果我们需要校验多个电话号码或者校验多个邮箱呢?

我们可能会这样做:


checkByRegExp(/^1\d{10}$/, '18642838455'); // 校验电话号码
checkByRegExp(/^1\d{10}$/, '13109840560'); // 校验电话号码
checkByRegExp(/^1\d{10}$/, '13204061212'); // 校验电话号码

checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com'); // 校验邮箱
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@qq.com'); // 校验邮箱
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@gmail.com'); // 校验邮箱

我们每次进行校验的时候都需要输入一串正则,再校验同一类型的数据时,相同的正则我们需要写多次, 这就导致我们在使用的时候效率低下,并且由于 checkByRegExp 函数本身是一个工具函数并没有任何意义, 一段时间后我们重新来看这些代码时,如果没有注释,我们必须通过检查正则的内容, 我们才能知道我们校验的是电话号码还是邮箱,还是别的什么。

此时,我们可以借助柯里化对 checkByRegExp 函数进行封装,以简化代码书写,提高代码可读性。


//进行柯里化
let _check = curry(checkByRegExp);
//生成工具函数,验证电话号码
let checkCellPhone = _check(/^1\d{10}$/);
//生成工具函数,验证邮箱
let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);

checkCellPhone('18642838455'); // 校验电话号码
checkCellPhone('13109840560'); // 校验电话号码
checkCellPhone('13204061212'); // 校验电话号码

checkEmail('test@163.com'); // 校验邮箱
checkEmail('test@qq.com'); // 校验邮箱
checkEmail('test@gmail.com'); // 校验邮箱

再来看看通过柯里化封装后,我们的代码是不是变得又简洁又直观了呢。

经过柯里化后,我们生成了两个函数 checkCellPhone 和 checkEmail, checkCellPhone 函数只能验证传入的字符串是否是电话号码, checkEmail 函数只能验证传入的字符串是否是邮箱, 它们与 原函数 checkByRegExp 相比,从功能上通用性降低了,但适用性提升了。 柯里化的这种用途可以被理解为:参数复用

我们再来看一个例子

假定我们有这样一段数据:


let list = [
    {
        name:'lucy'
    },
    {
        name:'jack'
    }
]

我们需要获取数据中的所有 name 属性的值,常规思路下,我们会这样实现:


let names = list.map(function(item) {
  return item.name;
})

那么我们如何用柯里化的思维来实现呢


let prop = curry(function(key,obj) {
    return obj[key];
})
let names = list.map(prop('name'))

看到这里,可能会有疑问,这么简单的例子,仅仅只是为了获取 name 的属性值,为何还要实现一个 prop 函数呢,这样太麻烦了吧。

我们可以换个思路,prop 函数实现一次后,以后是可以多次使用的,所以我们在考虑代码复杂程度的时候,是可以将 prop 函数的实现去掉的。

我们实际的代码可以理解为只有一行 let names = list.map(prop('name'))

这么看来,通过柯里化的方式,我们的代码是不是变得更精简了,并且可读性更高了呢。

三、如何封装柯里化工具函数

接下来,我们来思考如何实现 curry 函数。

回想之前我们对于柯里化的定义,接收一部分参数,返回一个函数接收剩余参数,接收足够参数后,执行原函数。

我们已经知道了,当柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?

我们有两种思路:

  1. 通过函数的 length 属性,获取函数的形参个数,形参的个数就是所需的参数个数
  2. 在调用柯里化工具函数时,手动指定所需的参数个数

我们将这两点结合以下,实现一个简单 curry 函数:



function curry(fn,len = fn.length) {
    return _curry.call(this,fn,len)
}


function _curry(fn,len,...args) {
    return function (...params) {
        let _args = [...args,...params];
        if(_args.length >= len){
            return fn.apply(this,_args);
        }else{
            return _curry.call(this,fn,len,..._args)
        }
    }
}

我们来验证一下:


let _fn = curry(function(a,b,c,d,e){
    console.log(a,b,c,d,e)
});

_fn(1,2,3,4,5);     // print: 1,2,3,4,5
_fn(1)(2)(3,4,5);   // print: 1,2,3,4,5
_fn(1,2)(3,4)(5);   // print: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // print: 1,2,3,4,5

我们常用的工具库 lodash 也提供了 curry 方法,并且增加了非常好玩的 placeholder 功能,通过占位符的方式来改变传入参数的顺序。

比如说,我们传入一个占位符,本次调用传递的参数略过占位符, 占位符所在的位置由下次调用的参数来填充,比如这样:

直接看一下官网的例子:

接下来我们来思考,如何实现占位符的功能。

对于 lodash curry 函数来说,curry 函数挂载在 lodash 对象上,所以将 lodash 对象当做默认占位符来使用。

而我们的自己实现的 curry 函数,本身并没有挂载在任何对象上,所以将 curry 函数当做默认占位符

使用占位符,目的是改变参数传递的顺序,所以在 curry 函数实现中,每次需要记录是否使用了占位符,并且记录占位符所代表的参数位置。

直接上代码:



function curry(fn,length = fn.length,holder = curry){
    return _curry.call(this,fn,length,holder,[],[])
}

function _curry(fn,length,holder,args,holders){
    return function(..._args){
        //将参数复制一份,避免多次操作同一函数导致参数混乱
        let params = args.slice();
        //将占位符位置列表复制一份,新增加的占位符增加至此
        let _holders = holders.slice();
        //循环入参,追加参数 或 替换占位符
        _args.forEach((arg,i)=>{
            //真实参数 之前存在占位符 将占位符替换为真实参数
            if (arg !== holder && holders.length) {
                let index = holders.shift();
                _holders.splice(_holders.indexOf(index),1);
                params[index] = arg;
            }
            //真实参数 之前不存在占位符 将参数追加到参数列表中
            else if(arg !== holder && !holders.length){
                params.push(arg);
            }
            //传入的是占位符,之前不存在占位符 记录占位符的位置
            else if(arg === holder && !holders.length){
                params.push(arg);
                _holders.push(params.length - 1);
            }
            //传入的是占位符,之前存在占位符 删除原占位符位置
            else if(arg === holder && holders.length){
                holders.shift();
            }
        });
        // params 中前 length 条记录中不包含占位符,执行函数
        if(params.length >= length && params.slice(0,length).every(i=>i!==holder)){
            return fn.apply(this,params);
        }else{
            return _curry.call(this,fn,length,holder,params,_holders)
        }
    }
}

验证一下:


let fn = function(a, b, c, d, e) {
    console.log([a, b, c, d, e]);
}

let _ = {}; // 定义占位符
let _fn = curry(fn,5,_);  // 将函数柯里化,指定所需的参数个数,指定所需的占位符

_fn(1, 2, 3, 4, 5);                 // print: 1,2,3,4,5
_fn(_, 2, 3, 4, 5)(1);              // print: 1,2,3,4,5
_fn(1, _, 3, 4, 5)(2);              // print: 1,2,3,4,5
_fn(1, _, 3)(_, 4,_)(2)(5);         // print: 1,2,3,4,5
_fn(1, _, _, 4)(_, 3)(2)(5);        // print: 1,2,3,4,5
_fn(_, 2)(_, _, 4)(1)(3)(5);        // print: 1,2,3,4,5

我们已经完整实现了一个 curry 函数~~

到此这篇关于前端JavaScript彻底弄懂函数柯里化的文章就介绍到这了,更多相关JavaScript函数柯里化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 前端JavaScript彻底弄懂函数柯里化curry

本文链接: https://lsjlt.com/news/135800.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 前端JavaScript彻底弄懂函数柯里化curry
    目录一、什么是柯里化( curry)二、柯里化的用途三、如何封装柯里化工具函数 一、什么是柯里化( curry) 在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系...
    99+
    2024-04-02
  • JavaScript函数柯里化
    目录1 什么是函数柯里化2 柯里化的作用和特点2.1 参数复用2.2 提前返回2.3 延迟执行3 封装通用柯里化工具函数#4 总结和补充1 什么是函数柯里化 在计算机科学中,柯里化(...
    99+
    2024-04-02
  • 彻底弄懂Python中的回调函数(callback)
    目录摘要什么是回调函数回调机制的优势异步处理有关的回调函数使用一个绑定方法来代替这个简单函数。 使用闭包代替上面的类来实现使用协程来完成异步操作总结摘要 主要是介绍pyth...
    99+
    2024-04-02
  • JavaScript函数柯里化详解
    目录一、简单了解apply和call二、什么是函数柯里化?三、写一个公共的柯里化函数四、创建一个灵活的柯里化函数五、写一个可控制的执行时间的柯里化函数总结一、简单了解apply和ca...
    99+
    2024-04-02
  • 怎样用Javascript实现函数柯里化与反柯里化
    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感;下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一...
    99+
    2024-04-02
  • JavaScript函数柯里化是什么
    这篇文章将为大家详细讲解有关JavaScript函数柯里化是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、简单了解apply和callcall 和 apply 都...
    99+
    2024-04-02
  • 如何使用Javascript实现函数柯里化与反柯里化
    这篇文章将为大家详细讲解有关如何使用Javascript实现函数柯里化与反柯里化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。javascript是一种什么语言javascript是一种动态类型、弱类型的...
    99+
    2023-06-14
  • JavaScript如何实现函数柯里化
    这篇文章给大家分享的是有关JavaScript如何实现函数柯里化的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。函数柯里化的实现// 函数柯里化指的是一种将使用多个参数的...
    99+
    2024-04-02
  • 一篇文章彻底弄懂C++虚函数的实现机制
    目录1、虚函数简介2、虚函数表简介3、有继承关系的虚函数表剖析3.1、单继承无虚函数覆盖的情况3.2、单继承有虚函数覆盖的情况3.3、多重继承的情况3.4、多层继承的情况4、总结1、...
    99+
    2024-04-02
  • JavaScript 中怎么实现柯里化函数
    本篇文章为大家展示了JavaScript 中怎么实现柯里化函数,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。阶段1现在有一个加法函数:function a...
    99+
    2024-04-02
  • JavaScript函数柯里化该怎么理解
    本篇文章给大家分享的是有关JavaScript函数柯里化该怎么理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、简单了解apply和callcall 和 apply 都是为...
    99+
    2023-06-26
  • JavaScript函数柯里化的示例分析
    这篇文章将为大家详细讲解有关JavaScript函数柯里化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。函数柯里化使用方法:柯里化是函数式编程的一个重要技巧,将使用多个参数的一个函数转换成一系列...
    99+
    2023-06-27
  • 深入了解JavaScript中的函数柯里化
    目录一、参数复用二、延迟执行三、部分应用四、函数组合JavaScript函数柯里化是一种将接受多个参数的函数转换为一系列接受单个参数的函数的技术。这种技术可以让我们更方便地创建可复用...
    99+
    2023-05-16
    JavaScript函数柯里化 JavaScript 柯里化
  • 一文带你了解JavaScript函数柯里化
    目录一、定义二、柯里化函数的作用1、参数复用2、提前确认3、延迟执行三、柯里化函数的实现四、柯里化总结性能方面:应用场景:一、定义 柯里化(Currying)是把接受多个参数的函数变...
    99+
    2023-02-01
    JavaScript函数柯里化 JavaScript 柯里化
  • JavaScript中的函数柯里化怎么使用
    这篇“JavaScript中的函数柯里化怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“JavaScript中的函数柯...
    99+
    2023-07-06
  • JavaScript中函数柯里化的原理是什么
    今天就跟大家聊聊有关JavaScript中函数柯里化的原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。柯里化是这样的一个转换过程,把接受多个...
    99+
    2024-04-02
  • JavaScript函数柯里化如何使用占位符
    小编给大家分享一下JavaScript函数柯里化如何使用占位符,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!函数柯里化(支持占位符)使用方法:通过占位符能让柯里化...
    99+
    2023-06-27
  • 从柯里化分析JavaScript重要的高阶函数实例
    目录前情回顾百变柯里化缓存传参缓存判断缓存计算缓存函数防抖与节流lodash 高阶函数结语前情回顾 我们在前篇 《✨从历史讲起,JavaScript 基因里写着函数式编程》 讲到了 ...
    99+
    2022-11-13
    JavaScript 柯里化高阶函数 JavaScript 高阶函数
  • JavaScript中的高阶函数和柯里化以及组合函数是怎样的
    这期内容当中小编将会给大家带来有关JavaScript中的高阶函数和柯里化以及组合函数是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。面向对象编程和函数式编程是两种...
    99+
    2024-04-02
  • JavaScript中的函数式编程函数和组合以及柯里化是怎样的
    JavaScript中的函数式编程函数和组合以及柯里化是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。面向对象编程和函数式编程是两种非常...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作