在JavaScript中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点。可以通过反柯里化(uncurrying)函数实现,让一个对象去借用一个原本不属于他的方法。

通常让对象去借用一个原本不属于它的方法,可以用call和apply实现,如下

var obj1 = {
name:'sven'
}
var obj2 = {
getName:function(){
return this.name
}
}
console.log(obj2.getName.call(obj1))//sven

更常见的场景之一是让类数组对象去借用Array.prototype的方法;

(function(){
Array.prototype.push.call(arguments,4)
console.log(arguments);//[1, 2, 3, 4]
})(1,2,3)

扩展:为什么类数组对象能够借用数组的方法呢?不妨理解下V8的引擎源码,就以Array.prototype.push为例:

function ArrayPush() {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
var array = TO_OBJECT(this);
var n = TO_LENGTH_OR_UINT32(array.length);
var m = %_ArgumentsLength();
.......
for (var i = 0; i < m; i++) {
array[i+n] = %_Arguments(i);
}
var new_length = n + m;
array.length = new_length;
return new_length;
}

通过这段代码大致可以看出,Array.prototype.push实际上是一个属性复制的过程,把参数按照下标依次添加到被push的对象上面,顺便修改了这个对象的length属性,这个对象到底是数组还是类数组并不重要。从源码可以看出,只要对象本身可以存取属性,且length属性可读写,就可以借用Array原型的push方法。

这样一来,方法中用到this的地方,就不在局限原本的对象,而是加以泛化并得到更广的适用性。那么有没有办法把泛化this的过程提取出来呢?那么反柯里化(uncurrying)就是解决这个问题的。反科里化(uncurrying)的话题来自JavaScript之父Brendan Eich在2011年发表的一篇文章,以下代码是实现方式之一:

Function.prototype.uncurrying = function() {
var self = this;
return function() {
var obj = Array.prototype.shift.call(arguments);
return self.apply(obj, arguments);
};
};

然后就可以定义一个push函数,更加简洁和明了的实现了一个不在局限于数组的push方法。如下:

var push = Array.prototype.push.uncurrying();
(function(){
push(arguments,4);
console.log(arguments);//[1,2,3,4]
})(1,2,3)

除了刚刚的一种反柯里化实现,还有另一种实现方式:

Function.prototype.uncurrying = function() {
var self = this;
return function() {
return Function.prototype.call.apply(self,arguments)
};
}
看似实现很简单,但理解就有点费力,再次做下阐释:
按照上边的push反柯里化函数Array.prototype.push.uncurrying()分析:
1.self = Array.prototype.push函数;
2.apply中的arguments = [目标数组,参数1,参数2]//实例中就等同于[[1,2,3],4]
对函数原型call方法执行apply方法作用相当于
Function.prototype.call.apply(Array.prototype.push,[[1,2,3],4])
再参考下边call和apply类源码实现进行分步理解
Function.prototype.apply= function(context,array) {
  // this=Function.prototype.call; context=Array.prototype.push; array=[[1,2,3],4]
context.fn = this;
  //Array.prototype.push.fn = Function.prototype.call.bind(Array.prototype.push) 第一步
var args = [];
for(var i = 0, len =array.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
  // Array.prototype.push.fn([1,2,3],4)=== Function.prototype.call.bind(Array.prototype.push)([1,2,3],4) 第二步
  
delete context.fn;
}
Function.prototype.call = function(context) {//[1,2,3],4

  //由第二步bind可知this为Array.prototype.push context=[1,2,3] arguments=[[1,2,4],4]
context.fn = this;
  //[1,2,3].fn=Array.prototype.push.bind([1,2,3]) 第三步
var args = [];
  //0为上下文,所以此处参数遍历应从1开始
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
  //[1,2,3].fn(4) === Array.prototype.push.bind([1,2,3])(4) 第四步
delete context.fn;
}
Array.prototype.push.bind([1,2,3])(4)===Array.prototype.push.call([1,2,3],4)

 

 https://92node.com/article/js-uncurrying.html

参考书籍:javascript设计模式与开发实践

javascript之反柯里化(uncurrying)的更多相关文章

  1. javascript之反柯里化uncurrying

    使用方法: // 使用 var push=Array.prototype.push.uncurrying(); var obj={ "length": 1, "0&quo ...

  2. 前端开发者进阶之函数反柯里化unCurrying

    函数柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数. 那么反柯里化函数,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用 ...

  3. JS中的反柯里化( uncurrying)

    反柯里化 相反,反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.即把如下给定的函数签名, obj.func(arg1, arg2) 转化成一个函数形式,签名 ...

  4. JS 函数的柯里化与反柯里化

    ===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...

  5. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  6. JavaScript 反柯里化

    浅析 JavaScript 中的 函数 uncurrying 反柯里化 柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间 ...

  7. 简单粗暴详细讲解javascript实现函数柯里化与反柯里化

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第 ...

  8. JavaScript中的反柯里化

    转载自:https://www.cnblogs.com/zztt/p/4152147.html 柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函 ...

  9. JavaScript中的函数柯里化与反柯里化

    一.柯里化定义 在计算机科学中,柯里化是把 接受多个参数的函数 变换成 接受一个单一参数(最初函数的第一个参数)的函数 并且返回 接受余下参数且返回结果的新函数的技术 高阶函数 高阶函数是实现柯里化的 ...

随机推荐

  1. Jenkins持续集成【简介】

    1.1 目前主流网站部署的流程    目前主流网站部署方法,通过Hudson/Jenkins工具平台实现全自动部署+测试,是一个可扩展的持续集成引擎,是一个开源软件项目,旨在提供一个开放易用的软件平台 ...

  2. T-SQL数据库备份

    /*1.--得到数据库的文件目录 @dbname 指定要取得目录的数据库名 如果指定的数据不存在,返回安装SQL时设置的默认数据目录 如果指定NULL,则返回默认的SQL备份目录名 */ /*--调用 ...

  3. poj3345 Bribing FIPA【树形DP】【背包】

    Bribing FIPA Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5910   Accepted: 1850 Desc ...

  4. 导出Excel过程中遇到的问题java.lang.NoClassDefFoundError: org/apache/xmlbeans/XmlException

    尝试实现java导出功能时遇到如下报错: org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.No ...

  5. android data recovery and nc

    目录下会出现mmcblk0.raw文件,文件大小等于手机内部存储空间的大小,该文件正是手机内部存储空间的镜像文件. 第七步,打开一款传统的数据恢复工具,由于raw文件是linux文件系统格式,因此需要 ...

  6. DevOps 创建pipline报错:The value specified for SourceVersion is not a valid commit ID

    报错内容 The value specified for SourceVersion is not a valid commit ID 解决 官方给出的解答: Thank you for your f ...

  7. 【抓包】火狐浏览器F12

    页面请求服务器的get.post两种请求,还有其他种,但是其他中基本不用,所以只记住get和post两种请求方法即可. 1.get(当前页面向服务器传值--即请求服务器)---弊端--传值长度有限 F ...

  8. 测试命令运行时间:timeit和profile

    先说timeit from timeit import Timer def test1(): n=0 for i in range(101): n+=i return n def test2(): r ...

  9. jsp内置对象与servlet的关系

      Servlet与JSP九大内置对象的关系 JSP对象 怎样获得 out->response.getWriter request ->Service方法中的req参数 response  ...

  10. zw版【转发·台湾nvp系列Delphi例程】HALCON MoveRectangle2

    zw版[转发·台湾nvp系列Delphi例程]HALCON MoveRectangle2 procedure TForm1.Button1Click(Sender: TObject);var img ...