在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. mycelipse中关于编码的配置

    (1)修改工作空间的编码方式: Window->Preferences->General->Workspace->Text file Encoding在Others里选择需要的 ...

  2. CentOS中为新用户添加sudo权限

    1.切换成root权限 su root 2.查看/etc/sudoers文件权限,如果只读权限,修改为可写权限 ls -l /etc/sudoers 3.如果是只读进行如下操作 chmod /etc/ ...

  3. Mysql和sqlite数据库操作心得

    经过最近一段时间的实际工作发现,原来只是认为Mysql和sqlite是分别独立的,数据传输和共享或有障碍,其实这是一个误区.当我们想要将sqlite中的数据存放到mysql中,最好的方法就是利用中间文 ...

  4. JSON_EXTRACT查询mysql中的{}和 [{},{}中的值]

    json_extract(a.tag, '$[*].tag_name.cn') as tag, json_extract(a.address,'$.en') as address_name, json ...

  5. Django - 项目总结

    总结: 基础,进阶,项目 Django - 练习(图书管理系统) 前后端分离得项目: vue + rest framework 项目: 图书增删改查页面 BBS + BLOG系统 CRM系统   在线 ...

  6. 103-advanced-上下文

    上下文提供了一种通过组件树传递数据的方法,无需在每个级别手动传递道具. 在典型的React应用程序中,数据通过prop自上而下(父到子)传递,但对于应用程序中许多组件所需的某些类型的道具(例如场所偏好 ...

  7. HDU1452:Happy 2004(求因子和+分解质因子+逆元)上一题的简单版

    题目链接:传送门 题目要求:求S(2004^x)%29. 题目解析:因子和函数为乘性函数,所以首先质因子分解s(2004^x)=s(2^2*x)*s(3^x)*s(167^x); 因为2与29,166 ...

  8. RedHat Linux文本模式下乱码解决方法

    如果在安装RedHat Linux时选择中文未缺省语言,在文本模式下会出现乱码情况,对于在CLI(command-line interface,命令行界面)方式下调试程序时诸多不便,因为出错信息全是乱 ...

  9. Python数据结构:列表、字典、元组、集合

    列表:shoplist = ['apple', 'mango', 'carrot', 'banana']字典:di = {'a':123,'b':'something'}集合:jihe = {'app ...

  10. Django的FBV和CB

    Django的FBV和CBV FBV FBV(function base views) 就是在视图里使用函数处理请求. 在之前django的学习中,我们一直使用的是这种方式,所以不再赘述. CBV C ...