做为函数式编程语言,JS带来了不少语言上的有趣特性,好比柯里化和反柯里化。html
能够对照另一篇介绍 JS 柯里化 的文章一块儿看~前端
柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,建立一个针对性更强的函数。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。编程
而反柯里化,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用范围,建立一个应用范围更广的函数。使原本只有特定对象才适用的方法,扩展到更多的对象。segmentfault
先来看看反柯里化的通用实现吧~数组
Function.prototype.unCurrying = function() { const self = this return function(...rest) { return Function.prototype.call.apply(self, rest) } }
解释下:微信
uncurrying
方法,并在执行的时候保存执行unCurrying
的方法到self还有一个实现:app
Function.prototype.unCurrying = function() { return this.call.bind(this) }
若是你以为把函数放在Function.prototype上不太好,也能够这样:编程语言
function unCurrying(fn) { return function(tar, ...argu) { return fn.apply(tar, argu) } }
用unCurrying
通用实现简单的实用一下试试:函数式编程
Function.prototype.unCurrying = function() { const self = this // 这里的self就是Array.prototype.push方法 return function(...rest) { // rest为传入的两层参数[[1,2,3],4] return Function.prototype.call.apply(self, rest) } } const push = Array.prototype.push.unCurrying() ~function(...rest) { // rest:[1,2,3] push(rest, 4) console.log(rest) // [1, 2, 3, 4] }(1, 2, 3)
反柯里化其实反映的是一种思想,即扩大方法的适用范围,仍然调用刚刚的通用unCurrying
方法借用push方法:函数
const push = Array.prototype.push.unCurrying() const obj = { a: '嘻嘻' } push(obj, '呵呵', '哈哈', '嘿嘿') console.log(obj) // { '0': '呵呵', '1': '哈哈', '2': '嘿嘿', a: '嘻嘻', length: 3 }
至关于obj.push(...)
,obj不只多了相似于数组同样以数字做为索引的属性,还多了个相似于数组的length属性,让引擎自动管理数组成员和length属性;(文后有V8引擎实现push方法的源码)
这样一个数组的push方法就被借用出来,能够应用于任何其余对象了。
只要是方法,unCurrying
就能够借用,call
方法也能够:
var call = Function.prototype.call.unCurrying(); function $(id) { return this.getElementById(id); } call($, document, 'demo') // #demo 元素
至关于document.$('demo')
,成功的借用了call方法,固然能够把document改为你但愿做为this绑定到$的任何对象,好比{ getElementById:T=>console.log(T+'呃') } // demo呃
unCurrying
自己也是方法,也能够借用本身...-。-
const unCurrying = Function.prototype.unCurrying.unCurrying() const map = unCurrying(Array.prototype.map) map({ 0: 4, 1: 'a', 2: null, length: 3 }, n => n + n) // [8, "aa", 0]
神奇吧~
简单说,函数柯里化就是对高阶函数的降阶处理,缩小适用范围,建立一个针对性更强的函数。举栗子:
function(arg1,arg2) // => function(arg1)(arg2) function(arg1,arg2,arg3) // => function(arg1)(arg2)(arg3) function(arg1,arg2,arg3,arg4) // => function(arg1)(arg2)(arg3)(arg4) function(arg1,arg2,…,argn) // => function(arg1)(arg2)…(argn)
而反柯里化就是反过来,增长适用范围,让方法使用场景更大。使用unCurrying
, 能够把原生方法借出来,让任何对象拥有原生对象的方法。举个栗子:
obj.func(arg1, arg2) // => func(obj, arg1, arg2)
也能够这样理解:
柯里化是在运算前提早传参,能够传递多个参数;
反柯里化是延迟传参,在运算时把原来已经固定的参数或者this上下文等看成参数延迟到将来传递。
附:
V8引擎中Array.prototype.push方法源码实现:
function ArrayPush() { var n = TO_UINT32(this.length); var m = %_ArgumentsLength(); for (var i = 0; i < m; i++) { this[i + n] = %_Arguments(i); // 属性拷贝 this.length = n + m; // 修正length return this.length; } }
网上的帖子大多深浅不一,甚至有些先后矛盾,在下的文章都是学习过程当中的总结,若是发现错误,欢迎留言指出~
参考:
JS 柯里化
前端开发者进阶之函数反柯里化unCurrying
JavaScript中有趣的反柯里化
js柯里化适用场景,优缺点分别是什么,还有个反柯里化?
JS进阶篇--JS中的反柯里化( uncurrying)
PS:欢迎你们关注个人公众号【前端下午茶】,一块儿加油吧~
另外能够加入「前端下午茶交流群」微信群,长按识别下面二维码便可加我好友,备注加群,我拉你入群~