从字面意思上来讲就是跟柯里化的意思相反;其实真正的反柯里化的作用是扩大适用范围,就是说当我们调用某个方法的时候,不需要考虑这个对象自身在设计的过程中有没有这个方法,只要这个方法适用于它,我们就可以使用;(这里引用的是动态语言中的鸭子类型的思想)
在学习JS反柯里化之前,我们先学习一下动态语言的鸭子类型思想,以助于我们更好的理解:
动态语言鸭子类型思想(维基百科解释):
在程序设计中,鸭子类型(duck typing)是动态类型的一种风格。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。
这个概念的名字来源于由 James Whitcomb Riley 提出的鸭子测试,“鸭子测试”可以这样表述:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
理论上的解释往往干涩难懂,换成人话来说就是:你是你妈妈的儿子/女儿,不管你是否优秀,是否漂亮,只要你是你妈亲生的,那么你就是你妈的儿子/女儿;换成鸭子类型就是,只要你会呱呱叫,走起来像鸭子,只要你拥有的行为像鸭子,不管你是不是鸭子,那么你就可以被称为鸭子;
在Javascript中有很多鸭子类型的引用,比如我们在对一个变量进行赋值的时候,显然是不需要考虑变量的类型的,正是因为如此,Javascript才更加的灵活,所以Javascript是一门典型的动态类型语言;
我们来看一下反柯里化中是怎么引用鸭子类型的:
//函数原型对象上添加uncurring方法 Function.prototype.uncurring = function() { //改变this的指向 //这里的this指向是Array.prototype.push var self = this; //这里的闭包用来返回内部函数的执行 return function() { //创建一个变量,在数组的原型对象上添加shift上面删除第一个参数 //改变数组this的指向为arguments var obj = Array.prototype.shift.call(arguments); //最后返回执行并给方法改变指向为obj也就是arguments // 并传入arguments作为参数 return self.apply(obj, arguments); }; }; //数组原型对象上添加uncurrying方法 var push = Array.prototype.push.uncurring(); //测试一下 //匿名函数自执行 (function() { //这里的push就是一个函数方法了 //相当于传入参数arguments和4两个参数,但是在上面shift方法中删除第一个参数,这里的arguments参数被截取了,所以最后实际上只传入了4 push(arguments, 4); console.log(arguments); //[1, 2, 3, 4] //匿名函数自调用并带入参数1,2,3 })(1, 2, 3)
到这里大家可以想一想arguments是一个接收参数的对象,里面是没有push方法的,那么arguments为什么能调用push方法呢?
这是因为代码var push = Array.prototype.push.uncurring();在数组的原型对象的push方法上添加了uncurring方法,然后在执行匿名函数的方法push(arguments, 4);时候实质上是在调用上面的方法在Function的原型对象上添加uncurring方法并返回一个闭包内部函数执行,在执行的过程中因为Array原型对象上的shift方法会把 push(arguments, 4);中的arguments截取,所以其实方法的实际调用是push(4),所以最终的结果才是[1,2,3,4]
在《JavaScript设计模式与开发实践》一书中,JS函数的反柯里化的案例是这样写的:
//定义一个对象 var obj = { "length":1, "0":1 } //在Function原型对象定义方法uncurrying Function.prototype.uncurrying = function() { //this指向Array.prototype.push var self = this; //闭包返回一个内部函数 return function() { // 这里可以拆开理解 //首先执行apply return //Function.prototype.call(Array.prototype.push[obj,2]) //然后Array.prototype.push.call(obj,2) //call改变指向 obj.push(2) //所以最后结果就是 {0: 1, 1: 2, length: 2} return Function.prototype.call.apply(self, arguments); } } //在 var push = Array.prototype.push.uncurrying() push(obj, 2) console.log(obj); //{0: 1, 1: 2, length: 2}
上面的方式不好理解?没关系,咱们来个好理解的: