当前位置:首页 > 日记 > 正文

浅谈JS中的反柯里化 | uncurrying

浅谈JS中的反柯里化 |  uncurrying

反柯里化

相反,反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.

即把如下给定的函数签名,

obj.func(arg1, arg2)

转化成一个函数形式,签名如下:

func(obj, arg1, arg2)

这就是 反柯里化的形式化描述。

例如,下面的一个简单实现:

Function.prototype.uncurrying = function() {  var that = this;  return function() {    return Function.prototype.call.apply(that, arguments);  }};function sayHi () {  return "Hello " + this.value +" "+[].slice.call(arguments);}var sayHiuncurrying=sayHi.uncurrying();console.log(sayHiuncurrying({value:'world'},"hahaha"));

解释:

  • uncurrying是定义在Function的prototype上的方法,因此对所有的函数都可以使用此方法。调用时候:sayHiuncurrying=sayHi.uncurrying(),所以uncurrying中的 this 指向的是 sayHi 函数; (一般原型方法中的 this 不是指向原型对象prototype,而是指向调用对象,在这里调用对象是另一个函数,在javascript中函数也是对象)
  • call.apply(that, arguments) 把 that 设置为 call 方法的上下文,然后将 arguments 传给 call方法,前文的例子,that 实际指向 sayHi,所以调用 sayHiuncurrying(arg1, arg2, ...) 相当于 sayHi.call(arg1, arg2, ...);
  • sayHi.call(arg1, arg2, ...), call 函数把 arg1 当做 sayHi的上下文,然后把 arg2,... 等剩下的参数传给sayHi,因此最后相当于 arg1.sayHi(arg2,...);
  • 因此,这相当于 sayHiuncurrying(obj,args) 等于 obj.sayHi(args)。

最后,我们反过来看,其实反柯里化相当于把原来 sayHi(args) 的形式,转换成了 sayHiuncurrying(obj,args),使得sayHi的使用范围泛化了。 更抽象地表达, uncurryinging反柯里化,使得原来 x.y(z) 调用,可以转成 y(x',z) 形式的调用 。 假设x' 为x或者其他对象,这就扩大了函数的使用范围。

通用反柯里化函数

上面例子中把uncurrying写进了prototype,这不太好,我们其实可以把 uncurrying 单独封装成一个函数;

var uncurrying= function (fn) {  return function () {    var args=[].slice.call(arguments,1);    return fn.apply(arguments[0],args);      }  };

上面这个函数很清晰直接。

使用时 调用 uncurrying 并传入一个现有函数 fn, 反柯里化函数会返回一个新函数,该新函数接受的第一个实参将绑定为 fn 中 this的上下文,其他参数将传递给 fn 作为参数。

所以,对反柯里化更通俗的解释可以是 函数的借用,是函数能够接受处理其他对象,通过借用泛化、扩大了函数的使用范围。

所以 uncurrying更常见的用法是对 Javascript 内置的其他方法的 借调 而不用自己都去实现一遍。

文字描述比较绕,还是继续看代码:

var test="a,b,c";console.log(test.split(","));var split=uncurrying(String.prototype.split);  //[ 'a', 'b', 'c' ]console.log(split(test,','));          //[ 'a', 'b', 'c' ]

split=uncurrying(String.prototype.split) 给 uncurrying 传入一个具体的fn,即String.prototype.split ,split 函数就具有了 String.prototype.split 的功能,函数调用 split(test,',') 时,传入的第一个参数为 split 执行的上下文,剩下的参数相当于传给原 String.prototype.split 函数。

再看一个例子:

var $ = {};console.log($.push);             // undefinedvar pushUncurrying = uncurrying(Array.prototype.push);$.push = function (obj) {  pushUncurrying(this,obj);};$.push('first');console.log($.length);            // 1console.log($[0]);              // firstconsole.log($.hasOwnProperty('length'));   // true

这里模仿了一个“类似jquery库” 实现时借用 Array 的 push 方法。 我们知道对象是没有 push 方法的,所以 console.log(obj.push) 返回 undefined,可以借用Array 来处理 push,由原生的数组方法(js引擎)来维护 伪数组对象的 length 属性和数组成员。

同样的道理,我们还可以继续有:

var indexof=uncurrying(Array.prototype.indexOf);$.indexOf = function (obj) {  return indexof(this,obj);};$.push("second");console.log($.indexOf('first'));       // 0console.log($.indexOf('second'));       // 1console.log($.indexOf('third'));       // -1

例如我们在实现自己的类库时,有些方法如果有些方法和原生的类似,那么可以通过 uncurrying 借用原生方法。

我们还可以把 Function.prototype.call/apply 方法 uncurring,例如:

var call= uncurrying(Function.prototype.call);var fn= function (str) {  console.log(this.value+str);};var obj={value:"Foo "};call(fn, obj,"Bar!");            // Foo Bar!

这样可以非常灵活地把函数也当做一个普通“数据”来使用,有函数式编程的赶脚,在一些类库中经常能看到这样的用法。

通用 uncurrying 函数的进击

上面的 uncurrying 函数是比较符合思维习惯容易理解的版本,接下来一路进击,看几个其他版本:

首先,如果B格高一点,uncurrying 也可能写成这样:

var uncurrying= function (fn) {  return function () {    var context=[].shift.call(arguments);    return fn.apply(context,arguments);  }};

当然如果还需要再提升B格,那么还可以是这样:

var uncurrying= function (fn) {  return function () {        return Function.prototype.call.apply(fn,arguments);  }};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关文章

JavaScript的事件机制详解

JavaScript的事件机制详解

事件机制,详解,电脑软件,JavaScript,事件是将JavaScript脚本与网页联系在一起的主要方式,是JavaScript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响至关重要。本文将详细探讨JavaScript的事件机制,并对比分析了浏览器之间…

thinkPHP5.0框架简单配置作用域的

thinkPHP5.0框架简单配置作用域的

作用域,框架,配置,方法,简单,本文实例讲述了thinkPHP5.0框架简单配置作用域的方法。分享给大家供大家参考,具体如下:配置参数支持作用域的概念,默认情况下,所有参数都在同一个系统默认作用域下面。如果你的配置参数需要用于不同的项目或者相互…

Jquery根据浏览器窗口改变调整大小

Jquery根据浏览器窗口改变调整大小

浏览器,方法,调整,大小,窗口,如下所示:当浏览器窗口发生该表示,会触发resize函数// 浏览器大小发生变化时,给id为main-container的容器设置最小高度<script type="text/javascript"> $(function(){ $(window).resize(function(){ $…

excel2013如何设置纵横页面布局

excel2013如何设置纵横页面布局

页面布局,方法,设置,如何设置,电脑软件,  通常在Excel2013中记录数据,都是采用的纵排排列,就是以列的形式呈现出来,但是有时候用种方式不适合怎么办?下面小编就教你怎么设置excel2013的纵横布局,希望看完本教程的朋友都能学会并运用起来。exc…

word2013怎么插入窗口域word2013插

word2013怎么插入窗口域word2013插

步骤,方法,窗口,电脑软件,strong,  我们在使用word的时候,相信很多人都不知道窗体域这个功能,那么今天小编就来告诉你怎么插入这个功能吧。word2013插入窗口域的步骤第一步,点击&ldquo;文件&rdquo;并选择&ldquo;选项&rdquo;word2013插入窗口…

WPS备份管理怎么备份以便恢复未保

WPS备份管理怎么备份以便恢复未保

备份,恢复,文件,文件备份,电脑软件,  WPS加入了自动备份功能,一旦断电或异常关机等意外不可控情况时便可还原没有来及保存的文件,以便恢复之前的文件或异常未保存的文件。以下是小编为您带来的关于WPS备份管理备份以便恢复未保存的文件,希望…

使用JavaScript实现链表的数据结构

使用JavaScript实现链表的数据结构

链表,数据结构,代码,电脑软件,JavaScript,链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer) — 维基百科上面是维基百科对 链表 的解读。下面我们用…

老生常谈计算机中的编码问题 | 必

老生常谈计算机中的编码问题 | 必

编码问题,计算,老生常谈,必看,机中,计算机中的编码问题因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制1111111…

通过V8源码看一个关于JS数组排序的

通过V8源码看一个关于JS数组排序的

数组排序,诡异,源码,电脑软件,JS,前言前几天一个朋友在微信里面问我一个关于 JS 数组排序的问题。通过该问题发现了一些之前没发现的内容,下面话不多少了,来一起看看详细的介绍吧。原始数组如下:var data = [ {value: 4}, {value: 2}, {valu…

Photoshop 7.0新增了哪些功能?

Photoshop 7.0新增了哪些功能?

功能,新增了,电脑软件,Photoshop,Photoshop 7.0 上市好久了 ,当时它也是现在的CC 版本一样 独一无二的, 想知道当年PS&ldquo;进化&rdquo;的过程的同学跟我来吧!介绍你们当时新增的功能。软件名称:Adobe PhotoShop7.0 简体中文版软件大小:154MB更…

Excel中表格工作表保护密码撤销的

Excel中表格工作表保护密码撤销的

密码,操作方法,表格,工作,操作步骤,  在做excel表格时我们有时候会给自己的工作表加密,一旦忘记密码就会很麻烦,如何绕过密码,直接撤销保护。今天,小编就教大家在Excel中表格工作表保护密码撤销的操作方法。Excel中表格工作表保护密码撤销的…

jquery实现回车键触发事件 | 实例

jquery实现回车键触发事件 | 实例

回车键,事件,实例,电脑软件,jquery,键盘事件有3:keydown,keypress,keyup,分别是按下,按着没上抬,上抬键盘 。正确代码为:$(document).keyup(function(event){ if(event.keyCode ==13){ $("#submit").trigger("click"); }});推荐:keyup,防止笔记…