前言

很长时间没有更新,一方面是工作比较忙,另一方面自己也处于一个学习的过程。接下来应该会逐渐恢复到稳定更新的状态,分享一些有趣的知识点以及我个人的思考。感兴趣的朋友可以关注下呀!

如果有不对的地方麻烦大家斧正,我会及时更新,感谢。

博客地址 ?? fe-code[1]

bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被 bind 的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。—— MDN

bind 方法想必大家都很熟悉,通常用来做 this 的绑定,它会返回一个新函数。但是还有一点,我们往往会忽略掉:其余的参数将作为新函数的参数供调用时使用

栗子:

function add(a, b) {    return a + b;}const addBind = add.bind(null, 1);addBind(2); // 3

显而易见,bind 调用时传入的参数会在 addBind 调用时和新参数一起传入。

Polyfill

那这是怎么做到的呢?我们简单看下 bind 的 Polyfill 版本,网络上类似的实现也有很多。

Function.prototype.mybind = function(context, ...args) {    const f = this;    const fpro = function() {};    const fBound = function(..._arg) {        // fpro.prototype.isPrototypeOf(this)  判断是否是 new 调用的 fBound        return f.apply(fpro.prototype.isPrototypeOf(this)                ? this                : context,                [...args, ..._arg]);    };    if (this.prototype) {        fpro.prototype = this.prototype;    }    fBound.prototype = new fpro();    return fBound;};

可以看到,bind 实际返回的是类似于 fBound 的函数,我们简化一下看看。

// 删减部分代码// f // argsconst fBound = function(..._arg) {    return f.apply(null, [...args, ..._arg]);};

其实就是利用闭包保存了上文的参数,最后再一起传给目标函数使用。这样如果不考虑 this,不使用 bind,我们可以直接把 add 函数改成这样:

function add(a) {    return function (b) {        return a + b;    }}add(1)(2); // 2

是不是很熟悉,一道很常见的面试题,这就是我们要说的柯里化。

curry

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。—— 维基百科

curry 其实是函数式编程中非常重要的一个思想,通过简单的局部调用,让函数具有预加载能力以及减少样板代码。

正因为我们有类似于add(1)(2)这样的需求,但是每次手动实现又很繁琐,所以可以使用一个特殊的 curry 函数,来帮助我们达到需求(在 lodash 等一些函数库中均有类似实现,感兴趣的同学可以去看看源码)。

简单实现

function curry(fn) {    const len = fn.length;    return function bindfn() {        if(arguments.length < len) {            return bindfn.bind(null, ...arguments);             // 关键:保存参数,并在调用时和后面的参数一起传入 bindfn        } else {            return fn.call(null, ...arguments);        }    }}

这个实现方案正是利用我们上面提到的 bind 的特性,所以我们再要实现 add 需求的时候就可以这样做:

// 普通写法function add(a, b) {    return a + b}const curryAdd = curry(add);curryAdd(1)(2); // 3

其他版本

不使用 bind 我们要怎么实现一个 curry 呢?

function _curry(fn) {    const len = fn.length;    function warp (..._arguments) {        let _arg = _arguments;        // 允许一次传完所有参数,虽然这和不用 curry 一样 add(1, 2);        if (_arg.length >= len) {            return fn(..._arg);        }        function fnc(...args) {            _arg = [..._arg, ...args];            if (_arg.length < len) { // 参数不够,继续返回函数                return fnc;            } else { // 参数足够,执行原函数                return fn(..._arg);            }        }        return fnc;    }    return warp;}

但是需求往往是多变的,以上两种方案,我们都是用形参和实参的个数来判断的函数是否需要执行,但是如果这种需求呢?

add(1)(2)() // 3add(1)(2)(3)() // 6,参数不确定且只有手动调用(),才会执行函数。很容易会想到需要改判断函数返回的判断条件。

function _curry1(fn) {    function warp (..._arguments) {        let _arg = _arguments;        function fnc(...args) {            _arg = [..._arg, ...args];            if (args.length > 0) { // 通过判断当前是否传入了参数,来决定函数返回                return fnc;            } else {                return fn(..._arg);            }        }        return fnc;    }    return warp;}

当然,这种 add 函数本身也得做调整,用来满足任意参数累加的需求。

function addInfinity(...args) {    return args.reduce((pre, cur) => pre + cur);}

应用

前面啰嗦这么多当然不是为了水这一篇文章,而且add(1)(2) 这种东西除了能应付面试还能做什么?我们真正需要的,还是实际应用到业务中。

•request

相信大家都写过或者看过类似的代码。

function request(url, params) {    return new Promise((resolve, reject) => {        setTimeout(() => resolve(url), 1000);    })}//function getData(params) {    return request('/api/getData', params);}function setData(params) {    return request('/api/setData', params);}//...

这就非常适合用 curry 来做处理,减少样板代码。

const getData = _curry(request, '/api/getData'); // 默认入参const setData = _curry(request, '/api/setData');

可能有同学会发现,之前写的 curry 函数并不支持这种写法。没错,不过稍微处理一下就好,我这里就不做处理了,大家可以自己试试。

•map

数组的 map 函数大家都用过,简单看一个场景。

[1,2,3].map(x => 2 * x);

如果希望这个是一个通用型的功能应该怎么处理呢?很容易会想到这么写。

function map(fn, arr) {    return arr.map(fn);}function multiply2(x) {    return 2 * x;}// map(multiply2, arr);

好像看起来很不错,简洁易用。那换成 curry 我们来看下。

const mapMultiply2 = curry(map, multiply2);// mapMultiply2(arr);

可以看见,curry 更利于我们去抽取函数,组合函数,让我们把更多的精力放在 multiply2 上。

总结

到这里,这篇文章就结束了。相信大家对 curry 也有了一些了解,比如,通过局部调用,让函数具有预加载能力以及减少样板代码。当然,关于柯里化的东西其实还很多,我这里讲的比较浅显,以后有机会再分享这方面的东西。

参考文章

•JS 函数式编程指南

后记

如果你看到了这里,且本文对你有一点帮助的话,希望你可以动动小手支持一下作者,感谢?。文中如有不对之处,也欢迎大家指出,共勉。好了,又耽误大家的时间了,感谢阅读,下次再见!

文章仓库 ??fe-code[2]社交聊天系统(vue + node + mongodb)- ???Vchat[3]

感兴趣的同学可以关注下我的公众号 前端发动机,好玩又有料。

References

[1] ?? fe-code: https://github.com/wuyawei/fe-code

curry化 js_前端发动机从 bind 聊到 curry (柯里化)相关推荐

  1. 【转载】JS中bind方法与函数柯里化

    原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情 ...

  2. JS - 函数柯里化

    一.概念 柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数. 简单来说,柯里化是一种函数的转换,它是指将一个函数从可调用的 ...

  3. bind函数返回值-1_javascript函数柯里化

    函数柯里化 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由 Chris ...

  4. JS函数curry(柯里化)

    原文地址:http://blog.jobbole.com/77956/ 什么是柯里化? 柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(译注:最初函数的第一个参数)的函数,如果 ...

  5. 从一道面试题认识函数柯里化

    最近在整理面试资源的时候,发现一道有意思的题目,所以就记录下来. 题目 如何实现 multi(2)(3)(4)=24? 首先来分析下这道题,实现一个 multi 函数并依次传入参数执行,得到最终的结果 ...

  6. 打造属于自己的underscore系列(五)- 偏函数和函数柯里化

    这一节的内容,主要针对javascript函数式编程的两个重要概念,偏函数(partial application) 和函数柯里化(curry)进行介绍.着重讲解underscore中对于偏函数应用的 ...

  7. 函数式编程 -- 纯函数、柯里化函数

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 学习函数式编程,要知道什么是纯函数,使用纯函数的好处,了解有关副作用的相关信息,以及纯函数相关的功能库Lodash,还有什么是函数的柯里化. 一.纯 ...

  8. 柯里化思想--JS中的预处理机制

    2019独角兽企业重金招聘Python工程师标准>>> 柯里化思想:就是预处理机制 利用函数执行,可以形成一个"不销毁的私有作用域的原理", 把需要预先处理的内容 ...

  9. 【译】理解JavaScript中的柯里化

    译文开始 函数式编程是一种编程风格,这种编程风格就是试图将传递函数作为参数(即将作为回调函数)和返回一个函数,但没有函数副作用(函数副作用即会改变程序的状态). 有很多语言采用这种编程风格,其中包括J ...

最新文章

  1. idea run和debug都是灰色的,没法点怎么解决
  2. OpenGL-坐标系概念
  3. [USACO07NOV]牛继电器Cow Relays
  4. 操作系统学习---虚拟内存
  5. python小说全站爬虫_起点小说网全站爬虫(Python)
  6. ORACLE 中ROWNUM用法总结(转载)
  7. jQuery标题文字淡入淡出显示效果
  8. 游戏筑基开发之利用文件函数取出配置文件(ini文件)的数据实体(C语言)
  9. 20191107每日一句
  10. [转]企业安全建设二——如何推动安全策略
  11. 批量重命名图片,去除括号
  12. Android APP极光推送取消关联启动配置
  13. ATT CK 阶段之 Initial Access --Drive-by Compromise
  14. 基于共振解调的轴承故障诊断方法总结(一)
  15. 使用update!导致的更新时候的错误信息不显示 ruby on rails
  16. 数值分析(1)-什么是数值分析
  17. 如何自己烧制全文RSS(打造自己RSS源)
  18. Chromium-Dev中一些英文缩写
  19. php mysql留言板系统_PHP结合Mysql数据库实现留言板功能
  20. 身份证号码的合法性校验

热门文章

  1. 【Kafka】Kafka 增量 Rebalancing: Support and Policies
  2. 02-伪异步I/O通讯模型
  3. 【ElasticSearch】es 使用function_score及soft_score定制搜索结果的分数
  4. 80-450-020-原理-索引-索引失效与优化
  5. 【es】ES RestHighLevelClient 请求报错:Connection reset by peer
  6. 95-130-340-源码-source-kafka相关-FlinkKafkaConsumer分析
  7. 95-190-452-源码-window-Trigger-ContinuousEventTimeTrigger
  8. 【Docker】Docker容器和主机如何互相拷贝传输文件
  9. Idea在plugin的marketplace中一直查不到东西安装插件
  10. 构建私有的 CA 机构