在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设计模式与开发实践

最新文章

  1. Python札记 -- MongoDB模糊查询
  2. 2016HUAS_ACM暑假集训2K - Hero(英雄)
  3. C++ code: 将程序的输出,保存到txt文档中,且每35个数,自动换行
  4. 《Linux命令行与shell脚本编程大全》 第一、二章 学习笔记
  5. iOS webview加载html自定义选项框选词
  6. CSS+DIV之强化background属性
  7. google zxing 二维码扫描(android client分析)
  8. ArcGIS10.3.1于2015年6月发布
  9. servlet第3讲(上集)----同一用户的不同页面共享数据
  10. 高可用之KeepAlived(一):基本概念和配置文件分析
  11. Leetcode_67_Add Binary
  12. 豆瓣API
  13. 使用Asp.Net Core MVC 开发项目实践[第一篇:项目结构说明]
  14. 微信小程序富文本渲染组件html2wxml及html2wxml代码块格式化在ios下字体过大问题
  15. C++中stl的map
  16. 如何解决eclipse、MyEclipse中变量名自动补全问题
  17. mysql中varbinary、binary、char、varchar异同
  18. 使用web api开发微信公众号,调用图灵机器人接口(一)
  19. hdu - 6277,2018CCPC湖南全国邀请赛B题,找规律,贪心找最优.
  20. Magisk+Xposed+Root switch+Pokémon GO

热门文章

  1. oracle数据库实例状态
  2. Ubuntu 16.04 安装和配置 Redis
  3. Mysql和sqlite数据库操作心得
  4. https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic net::ERR_CONNECTION_TIMED_OUT问题解决
  5. mysql中sql注入的随笔
  6. DNS中A记录和CNAME记录的区别(转)
  7. redis知识总汇
  8. mysql 数据操作 单表查询 练习
  9. MySQL 的mysqldump备份
  10. MySQL的redo log结构和SQL Server的log结构对比