在 js 中,如何编写一个可无限调用的柯里化 sum 函数?(sum(1,2,3)(4)()(5))。在下文中,就让我们一起实现它。而在实现前,我们需要引入两个知识点:柯里化与 Symbol.toPrimitive。
柯里化
首先,什么是柯里化?柯里化是函数式编程中的一个重要特性,是把接受多个参数的函数变换成接受部分参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
例如: func(a, b, c, d) => func(a)(b)(c)(d) 就是一个函数的柯里化应用。柯里化函数的优点是可以很方便的基于现有的通用函数构建出一定特性的新函数。我们举一个简单的例子
1 | const multiply = x => y => x * y; |
Symbol.toPrimitive
在 js 中,基本数据类型有 string, number,boolean,null,undefined,symbol 这六种。Symbol.toPrimitive 简写为 @@toPrimitive。
在 tc39 规范中,@@toPrimitive 使用 ToPrimitive ( input [ , PreferredType ] ) 抽象操作实现。操作接收一个 input 参数与一个额外的 PreferredType 参数。当一个对象能够转换为多个原始值的时候,将根据 PrefferedType 进行如下算法转换:
- 校验
input值符合 ECMAScript 规范- 如果
type(input)不为object则直接返回- 如果
type(input)为object
- 如果未定义
PreferredType,则设置hint为默认default- 如果定义
PreferredType为 hint String,则设置hint为string- 如果定义
PreferredType为 hint number, 则设置hint为number- 如果
input有定义@@toPrimitive方法
- 调用方法
@@toPrimitive(input, hint)获取结果result- 如果
type(result)不为object, 返回最终结果- 如果
type(result)为object,抛出TypeError异常- 如果
hint为default,设置hint为number- 调用原生
OrdinaryToPrimitive方法求值- 如果
hint为number, 则依照valueOf,toString的顺序调用函数- 如果
hint为string, 则依照toString,valueOf的顺序调用函数- 若函数存在且结果类型不为
object,则返回最终结果- 否则抛出
TypeError异常
通过 @@toPrimitive 方法,我们可以自定义函数的原始值 (ps. js 里函数也为对象
由此,我们可以写出如下 sum 函数
1 | const sum = (...xs) => { |
因为定义了 Symbol.toPrimitive 方法,result 便具有了函数和作为 string 或 number 的多重身份,从而实现了我们的目标。