科里化,它用于创建已经设置好了一个或多个参数的函数。函数科里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。
what?
下面介绍下实践中的用法:
function showFamilyName(firstName,secondName){
alert(firstName+secondName);
}
function curriedName(secondName){
return showFamilyName('xiong',secondName);
}
showFamilyName('xiong','zhengxiang');
curriedName('zhengxiang');
curriedName 函数,本质是是在任何情况下第一个参数为‘xiong'的showFamilyName 的版本。尽管curriedName 并非科里化函数,但它很好地展现了其概念
how?
怎么创建科里化函数?科里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要科里化的函数和必要参数。如下:
function curry(fn){
var args = Array.prototype.slice.call(arguments,1);//获取fn后面的参数列表
return function(){
var innerArgs = Array.prototype.slice.call(arguments);//传入的剩余参数
var finalArgs = args.concat(innerArgs);
return fn.apply(null,finalArgs);
}
}
fn 为要进行科里化的函数,其他参数时要传入的值。
那么上述的showFamilyName 可以这样科里化
var curriedName = curry(showFamilyName,'xiong');
curriedName('zhengxiang');
说白了,科里化就是将参数绑定到函数上面的技术,如下是利用科里化,实现了绑定函数的功能,并且绑定函数中,能传递参数
function bind(fn,context){
var args = Array.prototype.slice.call(arguments,2);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(context,finalArgs);
}
}
这个常用在事件处理函数中,如果需要传入出了event 参数外时。
反科里化:
把一个签名如下的方法:
obj.foo(arg1, arg2)转换成另外一个签名如下的函数:
foo(obj, arg1, arg2)想要知道这么做有什么用,我们首先得了解一下通用方法.
即函数的运行环境,从绑定的状态转换为通过参数出入的方式
what?
var obj={};
var push = Array.prototype.push.uncurring();
push(obj,'xiong');
alert(obj.length);//1
alert(obj[0]);//first
还有,我们在写库的时候,经常会写下面的代码:
if(Array.prototype.indexOf){
__.Array.indexOf = function(){
var ary = Array.prototype.shift.call(arguments);
return Array.prototype.indexOf.apply(ary,arguments);//利用浏览器自带的indexOf函数
}
}else{
//手动实现indexOf
}
我们想要的,其实只是借用Array原型链上的一些函数。并没有必要去显式的构造一个新的函数来改变它们的参数并且重新运算。
如果用uncurrying的方式显然更加优雅和美妙,就像这样:
if(Array.prototype.indexOf){
__.Array.inidexOf = Array.prototype.indexOf.uncurrying();
}
调用方式跟之前一样,__.Array.indexOf([1,2,3],2);
实例2 。
jquery对象( 即通过$()创建的对象 )是一个对象冒充的伪数组,它有length属性,并且能够通过下标查找对应的元素,当需要给jquery对象添加一个成员时, 伪代码大概是:
$.prototype.push = function( obj ){
this[ this.length ] = obj;
this.length = this.length + 1
}
如果用uncurring 的话,就可以:
var push = Array.prototype.push.uncurrying();
$.prototype.push = function(obj){
push(this,obj);
}
借用了array对象的push函数, 让引擎去自动管理数组成员和length属性.总的来说, 使用uncurrying技术, 可以让任何对象拥有原生对象的方法
how?
说个故事:
很久以前有个皇帝喜欢听鸭子呱呱叫,于是他召集大臣组建一个一千只鸭子的合唱团。大臣把全国的鸭子都抓来了,最后始终还差一只。有天终于来了一只自告奋勇的鸡,这只鸡说它也会呱呱叫,好吧在这个故事的设定里,它确实会呱呱叫。 后来故事的发展很明显,这只鸡混到了鸭子的合唱团中。— 皇帝只是想听呱呱叫,他才不在乎你是鸭子还是鸡呢。
这个就是鸭子类型的概念,在javascript里面,很多函数都不做对象的类型检测,而是只关心这些对象能做什么。
Array构造器和String构造器的prototype上的方法就被特意设计成了鸭子类型。这些方法不对this的数据类型做任何校验。这也就是为什么arguments能冒充array调用push方法.
看下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;
}
}
可以看到,ArrayPush方法没有对this的类型做任何显示的限制,所以理论上任何对象都可以被传入ArrayPush这个访问者。
我们需要解决的只剩下一个问题, 如何通过一种通用的方式来使得一个对象可以冒充array对象。
真正的实现代码其实很简单:
这段代码虽然很短, 初次理解的时候还是有点费力. 我们拿push的例子看看它发生了什么.
var push = Array.prototype.push.uncurrying();
push( obj, 'first' );
参考:http://blog.csdn.net/xiongzhengxiang/article/details/8437319
《javascript 高级程序设计》
这段代码虽然很短, 初次理解的时候还是有点费力. 我们拿push的例子看看它发生了什么.
var push = Array.prototype.push.uncurrying();
push( obj, 'first' );