arlyxiao / best-practice

1 stars 0 forks source link

Javascript 参数无限柯里化 #44

Open arlyxiao opened 3 years ago

arlyxiao commented 3 years ago

原文出处 https://medium.com/@seenarowhani95/infinite-currying-in-javascript-38400827e581

Javascript 是一门有趣的语言,有很多的技巧性用法。柯里化概念主要来自于函数式编程,在 javascript 里面也可以轻易实现。

在一切开始之前,先简单介绍下什么是柯里化。柯里化主要目的是分解计算结果,可以将一个最终计算值分解成多步进行。主要取决于函数怎么调用。

举个例子先,一个简单的 sum 方法,主要接收三个值,返回三个值相加结果。通过柯里化该方法,我们可以保存每一步计算状态,直到所有的参数都拿到才返回最终结果。

const sum = (a, b, c) => a + b + c

const curriedSum = a => b => c => sum(a, b, c)

// sum(1, 2, 3) => 6
// curriedSum(1)(2)(3) => 6

通过上面的例子可以看到,curriedSum 返回一个 function, 该 function 可以保持前一个参数在其上下文环境中。这样就可以只计算 sum 这个 function 其中的一部分,同时这个保存部分状态的方法可以做为参数传递到需要调用它的地方。

现在开始介绍无限柯里化,Javascript 允许每个对象可以实现 valueOf 这个方法,当对象被调用时就会返回 valueOf 结果。在 Javascript 里面, 除了原始数据类型外,都是对象,包括 function。

function sum(...args) {
  return Object.assign(
      sum.bind(null, ...args),
     { valueOf: () => args.reduce((a, c) => a + c, 0) }
  )
}

// console.log(add(1)(2)(3)(1, 2, 3)) = 12

现在我们重写了 valueOf 这个方法,并且支持无限柯里化,也就是可以接收任意参数。现在我们尝试理解这个实现。因为 function 也是 object,所以我们可以给这个 function 加一个新的 property。这里我们通过调用 Object.assign 这个 API 来实现,Object.assign 将第二个参数做为属性合并到第一个参数上,并且返回一个新的 function 的实例。这里我们不需要绑定特定的 this 对象,所以参数暂时用 null 替代。

现在让我们来看下重写后的 sum 使用方式,可以接收任意参数。

const partiallyEvaluated = sum(1, 2, 3)(4)(5, 6)
console.log(+partiallyEvaluated) // 21