简单实现一个柯里化函数

1,487 阅读3分钟

手写柯里化函数

什么是柯里化函数?

度娘说,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

举个例子

function Add(a, b) {
    return a + b;
}
​
// 执行 Add 函数,一次传入两个参数即可
Add(1, 2) // 3// 假设有一个 curry 函数可以做到柯里化
var AddCurry = curry(Add);
AddCurry(1)(2) // 3

但是问题来了费这么大劲封装一层,到底有什么用处呢?没有好处想让我们程序员多干事情是不可能滴,这辈子都不可能.

柯里化带来的好处

这个curry 函数就是我们说称的柯里化函数,它能帮我们在开发过程中减少很多代码冗余度,本质上是参数复用。本质上是降低通用性,提高适用性。

可是即便如此,依然还是感觉意义不是很大,但是如果我们把函数传给其他函数比如map呢?

举个例子:

比如我们有这样一段数据

var person = [{name: 'kevin'}, {name: 'daisy'}]

拿到name

var name = person.map(function (item) {
    return item.name;
})

如果我们有柯里化函数;

var prop = curry(function (key, obj) {
    return obj[key]
});
​
var name = person.map(prop('name'))

但是感觉我们这样反而更麻烦了好像,又重写了一个prop函数 ,要知道,咱们虽然多写了一个函数,但是咱们之后可以多次使用,不需要每次都写var name = person.map(function (item) { return item.name; })

只需要写var name = person.map(prop('name'))

这样让代码不是更加简单易懂了吗

手写柯里化函数 ###----###

首先 我们理解一些这个过程

function url(a,b,c){
    return `${a}${b}${c}`
}
let d= url('http://','juejin.cn','/frontend')
console.log(d);//https://juejin.cn/frontend
​
​

如果我们使用url函数,那么我们要经常输入http://这个经常重复的参数 就像我们平时使用浏览器输入网址时其实也不用输入http或者https 因此我们需要设置一个参数用来保存'http://',并且用url函数返回两个带参数的函数 ,根据作用链的关系,这里返回出来的函数是可以接受外部的参数

    function url (a){
        return function (b,c){
            return `${a}${b}${c}`
        }
    }
​
    let p=url('http://')
    console.log(p);
    结果为// return function (b,c){
            return `${a}${b}${c}`
        }

所以我们进行一些改动,我们可以看出打印出来的结果

    function url (a){
        return function (b,c){
            return `${a}${b}${c}`
        }
    }
​
    let p=url('http://')
    let p1=p('www.baidu.com','/80')
    let p2 = p('www.baidu.com','/80')
    console.log(p1,p2);// https://juejin.cn/frontend 
                            https://juejin.cn/frontend

我们可以看出这是针对函数参数进行了优化,当然现在还是一个体验版本

正片!

我们实现一个简单的加法

有一个add()函数

我们不管什么时候传入参数,都能返回正确的执行结果

我们创建一个add()函数,设置一个inner函数用来接收第二次传入的参数,最后返回函数inner

function add(){
    // 想要args用push方法,需要让args成为数组,arguments原生是个对象,所以我们用下面这种经典的方法
  let args=Array.prototype.slice.call(arguments)
​
  let inner function(){
​
•    args.push(...arguments);
    let sum = args.reduce(function(prev,cur){
            return prev+cur
        })
    return sum 
  }
​
  return inner
​
}
console.log(add(1)(2))

结果会返回一个3,基本的柯里化我们实现了

如果再加一个括号呢, 我们看结果会报错,

函数还不能实现任意次数传参

这个问题我们可以用递归来解决,可以在内部函数自己调用

function add(){
    let args=Array.prototype.slice.call(arguments)
​
    let inner = function(){
        args.push(...arguments)
        return inner
​
    }
​
    return inner
}
​
console.log(add(1)(2)(3))

返回的结果是一个内部函数

image.png 这里每次执行括号的时候都会返回一个,不断自己调用自己

现在我们需要实现所有参数全部相加,但是内部函数被返回了,我们不能返回一个额外的结果到外部

但是这里的函数其实是以字符串的形式返回,原本的函数被转换为字符串显示了,这其实就是发生了内部toString()强制类型转换

那么我们就可以修改toString方法输出内容来改变结果

function add(){
    let args=Array.prototype.slice.call(arguments)
​
    let inner = function(){
        args.push(...arguments)
        return inner
​
    }
    inner.toString=function(){
        return args.reduce(function(prev,cur){
            return prev+cur
        })
    }
​
    return inner
}
​
alert(add(1)(2)(3))
​

结果输出为 6

这里注意,toString()隐式调用只会发生在浏览器里,所有我们可以用alert来调用函数add触发toString方法

image.png \