谈一谈js中的柯里化函数

第一次看到柯里化这个词的时候,是在群里听大佬们谈论一道面试题中了解到。那时一看这个词就感觉很懵逼,赶紧去了解了一下后才发现其实就是高阶函数的一个特殊用法。 题附上

实现一个函数,运算结果可以满足如下预期结果:
add(1)(2)(3) // 6
add(1, 2, 3) // 6
add(1)(2,3) // 6
add(1,2)(3) // 6

如果对柯里化有了解的同学,肯定一眼就看出答案了,主要是运用函数柯里化来实现。那么什么是柯里化呢?首先介绍一下什么是函数的柯里化(Currying)。《JavaScript忍者秘籍》一书中,对于柯里化的定义如下:

在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying。

现在让我们一步步来实现它
首先是 add(1)(2)(3) // 6
我们很快就能想到函数add

	function add(x){
		return function(y){
			return function(z){
				return x + y + z
			}
		}
	}

接下来是 add(1, 2, 3) // 6

	function add(x,y,z){
		return x + y + z
	}

那么问题来了 怎么把两个函数综合为一个函数呢? 通过题目我们可以发现,add参数的值的数量是相同的,只是传值的方式有所不同,所以本题的关键就是编写一个高阶函数 curry 让 add(1,2)(3) 同等转换为 add(1,2,3)

  1. 首先我们知道add函数的参数个数 为3,因此先编写函数addOrigin

    function addOrigin(x,y,z){
    	return x + y + z
    }
    
  2. 编写函数curry,当参数数量达到addOrigin的个数时,返回addOrigin,否则继续递归下去

    function curry(fn) {
        let arr = [];
        let depth = fn.length;
        const re = (...a) => {
            depth = depth - a.length;
            arr = [...arr, ...a]
            if (depth > 0) {   // 若参数个数未达到fn的要求,进行递归操作
                return re;
            }
            let result = [...arr];
            arr = [];
            depth = fn.length;
            return fn(...result);
        };
        return re;
    }
    
  3. 最后我们在通过curry这个高阶函数,生成我们需要的add函数
    const add = curry(addOrigin)

  4. 最后 我们在控制太验证一下结果
    控制台

大功告成!我们成功的利用curry 实现了函数的链式调用。同时也明白了 curry的核心思想——比较多次接受的参数总数与函数定义时的入参数量,当接受参数的数量大于或等于被 Currying函数的传入参数数量时,就返回计算结果,否则返回一个继续接受参数的函数。
上面代码中curry函数也可以简写为

	function curry(fn, ...args) {
		if (args.length >= fn.length) {
    		return fn(...args)
		}
		return function (...args2) {
   			return curry(fn, ...args, ...args2)
		}
	}

下面再看另一道很经典的面试题

实现一个函数,运算结果可以满足如下预期结果:
add(1)(2) // 3
add(1, 2, 3)(10) // 16
add(1)(2)(3)(4)(5) // 15

这道题跟上面一道题不一样的在于 参数的个数不固定,在网上找了一下答案 如下:

 function add(...values) {
        let sum = 0;
        for (key of values) {
            sum += key;
        }
        let tmp = (x) => {
            sum += x;
            return tmp;
        };
        tmp.toString = () => sum;
        return tmp;
    }
add(1)(2) // 3
add(1, 2, 3)(10) // 16
add(1)(2)(3)(4)(5) // 15

主要是利用重写对象的toString方法,但是问题在于 toString 不会主动执行, 后续有解决办法会再更新

下面是记录一下柯里化最常见的两个用途

  1. 参数复用: 固定不变的参数,实现参数复用是柯里化的主要用途之一
  2. 延迟执行: 延迟执行也是柯里化的一个重要使用场景,同样 bind 和箭头函数也能实现同样的功能。

总结

  1. 柯里化在 JavaScript 中是“低性能”的,但是这些性能在绝大多数场景,是可以忽略的。
  2. 柯里化的思想极大地助于提升函数的复用性。
  3. 柯里化生于函数式编程,也陷于函数式编程。假如没有准备好写纯正的函数式代码,那么 柯里化有更好的替代品。
  4. 函数式编程及其思想,是值得关注、学习和应用的事物。所以在文末再次安利JavaScript 程序员阅读此书 —— 《mostly-adequate-guide》

参考链接:
大佬,JavaScript 柯里化,了解一下?
从一道面试题谈谈函数柯里化(Currying)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值