欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > javascript >内容正文

javascript

深入理解javascript系列(十七):函数柯里化

发布时间:2024/4/13 javascript 62 豆豆
生活随笔 收集整理的这篇文章主要介绍了 深入理解javascript系列(十七):函数柯里化 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

之前的系列,我们介绍了什么是高阶函数。所有以函数作为参数的函数,都可以叫作高阶函数。并且我们常常利用高阶函数来封装一些公共逻辑。

本次,我们要继续学习,继续记录,柯里化。柯里化,其实就是高阶函数的一种特殊用法。

柯里化是指这样一个函数(假设叫做createCurry),它接收函数A作为参数,运行后能够返回一个新的函数,并且这个新的函数能够处理函数A的剩余参数。

文字总是不那么好去理解,下面我们就通过例子来理解吧。

假设有一个接收三个参数的函数A。

function A(a, b, c) {// to do something }复制代码

又假设我们有一个已经封装好了的柯里化通用函数createCurry。他接收bar作为参数,能够将A转化为柯里化函数,返回结果就是这个被转化之后的函数。

var _A = createCurry(A);复制代码

那么_A作为createCurry运行的返回函数,能够处理A的剩余参数。因此下面的运行结果都是等价的。

_A(1, 2, 3); _A(1,2)(3); _A(1)(2,3); _A(1)(2)(3); A(1,2,3);复制代码

函数A被createCurry转化之后得到柯里化函数_A,_A能够处理A的所有剩余参数。因此柯里化也被称为部分求值。

在简单的场景下,我们可以不借助柯里化通用式来转化得到柯里化函数,仅凭借眼力自己封装。

例如,有一个简单的加法函数,它能够将自身的三个参数加起来并返回计算结果。

function add(a, b, c) {return a + b + c; }复制代码

那么add函数的柯里化函数_add则可以写成:

function _add(a) {return function(b) {return function(c) {return a + b + c;}} }复制代码

因此下面的运算方式是等价的。

add(1, 2, 3); _add(1)(2)(3);复制代码

当然,柯里化通用式具备更加强大的能力,仅靠眼力劲可不行。因此我们更需要知道如何封装这样一个柯里化的通用式。

首先通过_add可以看出,柯里化函数的运行过程其实是一个参数收集过程,我们将每一次传入的参数收集起来,并在最里层进行处理。因此在实现createCurry时,可以借助这个思路来进行封装。

代码如下:

// arity 用来标记剩余参数的个数 // args 用来收集参数function createCurry(func, arity, args) {//第一次执行时,并不会传入arity,而是直接获取func参数的个数 func.lengthvar arity = arity || func.length;//第一次执行也不会传入args,而是默认为空数组var args = args || [];var wrapper = function() {//将wrapper中的参数收集到args中var _args = [].slice.call(arguments);[].push.apply(args, _args);//如果参数个数小于最初的func.length,则递归调用,继续收集参数if(_args.length < arity) {arity -= _args.length;return createCurry(func, arity, args);}//参数收集完毕,执行funcreturn func.apply(func, args);}return wrapper; }复制代码

是不是有些不太容易理解,所以要多阅读几次。这个createCurry的封装其实是借助了闭包和递归,实现一个参数收集,并在收集完毕之后执行所有参数。

不知道您是否有发现,函数经过createCurry转化为一个柯里化函数后,最后执行的结果,不是正相当于执行函数自己吗?柯里化是不是把简单的问题复杂化了?

没错,柯里化确实是把简单的问题复杂化了,但在复杂化的同时,我们在使用函数时拥有了更多的自由度。对于函数参数的自由处理,正是柯里化的核心所在。

下面举一个常见的例子。

如果想要验证一串数字是否是正确的手机号,那么按照正常思路来做,可能就会写出代码如下唉:

fuction checkPhone(phoneNumber) {return /^1[34578]\d{9}$/.test(phoneNumber); }复制代码

而如果想要验证是否是邮箱呢?你然后在写一个,可是我们还会遇到更多需要验证的消息,如“身份证、登录名、密码...”。为了偷懒,我们应该封装一个更为通用的函数,把待验证的正则表达式与将要被验证的字符串作为参数传入:

function check(reg, targetString) {return reg.test(targetSting); }复制代码

但是这样封装之后,在使用时又会遇到问题,因为总是需要输入一串正则,一串字符,这样就导致使用时效率低下。

这个时候,我们就可以借助柯里化,在check的基础上再做一层封装,以简化使用。

var _check = createCurry(check);var checkPhone = _check(/xxxxxx/); var checkEmail = _check(/xxxxxx/);复制代码

最后在使用时就会变得更加简洁与直观了。

checkPhone('13979227922'); checkEmail('xsxsx@163.com');复制代码

在这个过程中可以发现,柯里化能够应对更加复杂的逻辑封装。当情况变得多变时,柯里化依然能够应付自如。

虽然柯里化在一定程度上将问题复杂化,也让代码变得更加不容易理解,但是柯里化在面对复杂情况时的灵活性却让我们不得不爱。



总结

以上是生活随笔为你收集整理的深入理解javascript系列(十七):函数柯里化的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。