JS进阶 | 如何实现柯里化

73 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情

第一次看到柯里化这个词,感觉有点抽象,到底什么是柯里化呢?

首先,在编程中有如下之分:

  • 函数式编程
  • 命令式编程(比如 JQuery )

柯里化(Currying)是函数式编程的特点之一,由数学家柯里(Haskell Curry)提出。

柯里化

柯里化是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。简言之,是一种函数的转换。

柯里化的好处:简化代码结构,达到高内聚、低耦合。

形式

一般情况下,我们写一个函数来实现两数之和是这样的:

function sum(x, y){  
    return x + y  
}

sum(1, 2) // 3  

经过柯里化后,函数是这样的:

function sum(y){  
    return function (x){  
        return x + y  
    }  
}

sum(1)(2) // 3  

如果是更多的参数:

function sum(x) {
    return function(y) {
        return function(z) {
            return x + y + z
        }
    }
}
sum(1)(2)(3)

也可以是箭头函数的形式:

const sum = x => y => z => {
    return x + y + z
}

柯里化的优点

通过上面的例子可以看出,柯里化将多参数的函数转化为了单参数函数,并且柯里化在形式上和闭包很相似。

  • 使每个函数的职责变得单一 (每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果)
  • 延迟求值,需要的时候再进行求值
  • 复用参数逻辑

如下面这段代码,想要计算某个固定值和另外一任意值之和,只需要传入1次固定值即可:

  • 当执行 Currying() 函数并传入参数后,相当于 x = 2
  • 再执行 sum() 函数并传入参数后,相当于 return 2 + 100
  • 之后每次调用 sum(),只需传入一次参数
function Currying(x) {
  return function(y) {
    return x + y
  }
}

const sum = Currying(2)
sum(100) // 102
sum(200) // 202

实现一个柯里化

lodash.js 以及 understore.js 各自也有实现柯里化的方法,除此之外我们也可以手动实现一个:

function createCurry(func, args) {
    var len = func.length;// 函数参数的个数
    var args = args || [];

    return function () {
        var _args = Array.prototype.slice.apply(arguments);
        args.push(..._args);
        console.log(args,'--')
        if (args.length < len) {
            return createCurry.call(this, func, args);
        }
        return func.apply(this, args);
    }
}

function myFn(a, b, c) {
    return a * b * c;
}

const multi = createCurry(myFn)

console.log(multi(2)(6)(10)); // 120
console.log(multi(2,6,10)); // 120
  • 强制转化 arguments 为数组格式:
  • [].slice.call(arguments)
  • Array.prototype.slice.apply(arguments)