Skip to content
本页目录

函数柯里化

为了更方便和浅显的解释何为函数柯里化,需先了解一个概念:元(variable)。那么什么是呢?

在数学、物理上,就是未知数的个数;在计算机科学上,就是函数的参数个数。

和函数柯里化有什么关系呢?

所谓的函数柯里化就是将一个多参数函数转换成多个单参数函数,换成函数描述就是将一个n元函数转换成n个一元函数。

函数参数

关于函数参数,这里需要特别说明,在JavaScript标准内置对象Function上的length属性 仅表示函数期望的参数数量。又由于其它函数都是由Function派生而来(或者说是其实例,不理解的话,可以看看原型链),故所有函数都有length这一属性。
此外,还需将其与函数内的arguments.length区分开,两者区别如下:

  • Function.length:表示函数的形参,这里的形参不包括剩余参数,只包括在第一个具有默认值的参数之前的参数;
  • arguments.length:表示函数的实参

若还是不明白两者区别,可前往MDN查看Function.lengtharguments.length

柯里化

接下来实现函数柯里化。

JavaScript
function curry(func, args) {
  const len = func.length
  args = args || []
  return function () {
    const _args = [...args, ...arguments]
    if (_args.length < len) {
      return curry.call(this, func, _args)
    }
    return func.apply(this, _args)
  }
}

// 验证一下
const yui = curry((school, name, age) => {
  return `${school}-${name}-${age}`
})
yui('樱丘高中', 'yui', 14)  // '樱丘高中-yui-14'
yui('樱丘高中', 'yui')(14)  // '樱丘高中-yui-14'
yui('樱丘高中')('yui')(14)  // '樱丘高中-yui-14'
yui('樱丘高中')('yui', 14)  // '樱丘高中-yui-14'

更推荐的ES6写法

JavaScript
const curry = func =>
  judge = (...args) =>
    args.length === func.length
      ? func(...args)
      : (arg) => judge(...args, arg)

// 验证同上

偏函数

偏函数是一种较特殊的柯里化函数,特殊在偏函数是固定一个或者多个参数,换成函数描述就是将一个n元函数转换成一个n-x元函数。
=> 其实Function#bind()就是一个典型的偏函数。

JavaScript
const O = {}
function partial(func) {
  const args = [].slice.call(arguments, 1)
  let len = args.length
  return function () {
    let idx = 0
    for (let i = 0; i < len; i++) {
      args[i] = args[i] === O ? arguments[idx++] : args[i]
    }
    len = arguments.length
    while (idx < len) args.push(arguments[idx++])
    return func.apply(this, args)
  }
}

// 验证一下
const getInfo = (school, name, age) => {
  return `${school}-${name}-${age}`
}
const yui = partial(getInfo, '樱丘高中', 'yui')
yui('14')  // '樱丘高中-yui-14'
const mio = partial(getInfo, '樱丘高中')
mio('mio', '15')  // '樱丘高中-mio-15'

更推荐的ES6写法

JavaScript
const partial = (func, ...args) =>
  (..._args) =>
    func.call(null, ...args, ..._args)

// 验证同上