「前端每日一问(33)」闭包与柯里化函数、偏函数的关系?

365 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

本题难度:⭐ ⭐

答:

柯里化函数是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数的编程技巧。

偏函数是指将一系列参数固定到一个函数上,生成另一个较小算术数的函数的编程技巧。

柯里化函数和偏函数的区别是:

柯里化函数

f(x, y, z) 只能转化为 f(x)(y)(z),有多少个参数,就转化成多少个函数调用
偏函数

f(x, y, z) 可以转化成 f(x, y)(z),也可以转化成 f(x)(y, z)

他们俩的目的都是为了避免频繁调用具有相同参数函数的同时,又能够轻松的重用。

重点:他们俩的实现都用到了闭包

柯里化函数

假设我们有一个求立方体体积的函数。

function getVolume(width, length, height) {
  return width * length * height
}

如果我们碰到的立方体的长和宽老是不变,只有高度经常变化。

const volume1 = getVolume(1, 2, 3)
const volume2 = getVolume(1, 2, 4)
const volume3 = getVolume(1, 2, 5)

console.log(volume1) // 6
console.log(volume1) // 8
console.log(volume1) // 10

我们就这么写:

function getVolume (width) {
  return length => {
    return height => {
      return width * length * height
    }
  }
}

之后碰到长和宽不变的立方体就可以这样计算体积。

const getTwoWithVolume = getVolume(1)(2)

const volume1 = getTwoWithVolume(3) 
const volume2 = getTwoWithVolume(4)
const volume3 = getTwoWithVolume(5)

console.log(volume1) // 6
console.log(volume1) // 8
console.log(volume1) // 10

不怎么变化的长和宽就被记录到了闭包里,第一个参数 width 被记录在 getVolume 函数的闭包里,第二个参数 length 被记录在匿名函数的闭包里。

image.png

上面的 getVolume 函数用箭头函数来写长这样,箭头函数的代码确实很简洁,但可能不好理解。

const getVolume = width => length => height => width * length * height

偏函数

偏函数的实现也用到了闭包,代码如下:

const partial = function (fn, ...args) {
  return function (...rest) {
    return fn(...args, ...rest)
  }
}

const getVolume = (width, length, height) => width * length * height

const twoMultiplyHeight = partial(getVolume, 1, 2) // 宽固定为1,长固定为2,高在不断变化
console.log(twoMultiplyHeight(3)) // 6
console.log(twoMultiplyHeight(4)) // 8
console.log(twoMultiplyHeight(5)) // 10

const tenMultiplyHeight = partial(getVolume, 5, 2) // 宽固定为5,长固定为2,高在不断变化
console.log(tenMultiplyHeight(2)) // 20
console.log(tenMultiplyHeight(3)) // 30
console.log(tenMultiplyHeight(4)) // 40

const tenMultiplyLengthAndHeight = partial(getVolume, 10) // 宽固定为10,长和高不断变化
console.log(tenMultiplyLengthAndHeight(2, 5)) // 100
console.log(tenMultiplyLengthAndHeight(3, 4)) // 120
console.log(tenMultiplyLengthAndHeight(3, 3)) // 90

以宽固定为1,长固定为2,高在不断变化为例,闭包中记录的就是固定的前两个参数。

image.png

拓展

我们平时经常写的 ajax 请求,可能在不知不觉中就用到了偏函数。

比如所有的请求都要在 headers 里写一样的 token,又比如根据请求方法单独抽离出用得多的 get 请求和 post 请求,你可能会写出类似下面的代码:

function request (method, url, headers) {
  // ...
}

const headers = {
  authorization: 'xxx'
}

const getRequest = url => request('get', url, headers)
const postRequest = url => request('post', url, headers)

getRequest('/api/getBookList')
postRequest('/api/login')

结尾

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

你也可以关注《前端每日一问》这个专栏,防止失联哦~

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(32)」如何用闭包实现一个单例模式?

下一篇:

「前端每日一问(34)」闭包和循环陷阱