JavaScript 反科里化 this [译]


Posted in Javascript onSeptember 20, 2012

本文主要讲了JavaScript中科里化和反科里化this的方法.话题来自于Brendan Eich(JavaScript之父)的一个tweet.

1.反科里化(Uncurrying)this

反科里化this的意思是:把一个签名如下的方法:

obj.foo(arg1, arg2)转换成另外一个签名如下的函数:

foo(obj, arg1, arg2)想要知道这么做有什么用,我们首先得了解一下通用方法.

2.通用方法(Generic methods)

通常情况下,某个特定的方法只能在某种特定类型的对象实例上使用.但是,有一些方法如果还可以使用在其他类型的对象实例上的话,那会非常有用,例如:

// 实际实现的简化版本: 
Array.prototype.forEach = function (callback) { 
for(var i=0; i<this.length; i++) { 
if (i in this) { 
callback(this[i], i); 
} 
} 
};

this可以看做是forEach()方法的隐含参数.满足下面这三条规则的对象都可以调用forEach()方法,都可以作为这个隐含的this:

•具有length属性: this.length
•能够通过索引访问对象元素: this[i]
•能够检查属性的存在性: i in this
arguments对象(包含了一次函数调用的所有实参)不是一个Array实例,所以它不能直接调用forEach()方法.但是你它满足调用forEach方法的三个条件.为了让该对象能够调用到forEach()方法,我们只需要让隐含的this参数作为显式参数.幸运的是,每个函数都有call()方法让我们来做件事:

function printArgs() {
    Array.prototype.forEach.call(arguments, function (elem, index) {
        console.log(index+". "+elem);
    });
}

forEach.call()比forEach()方法多一个参数:它的第一个参数就是指定的this值:
> printArgs("a", "b")
0. a
1. b

JavaScript中有几个类似的通用方法都可以以这种方式来调用,这些方法大部分来自Array.prototype.

3.反科里化this的几个用途

用例1:通过map()调用一个方法. Array.prototype.map()方法允许你在一个数组中的每个元素上调用一个函数.但如果你想调用的不是函数还是方法呢?可以利用反科里化this这么做:

> var toUpperCase = String.prototype.toUpperCase.uncurryThis();
> [ "foo", "bar", "baz" ].map(toUpperCase)
[ 'FOO', 'BAR', 'BAZ' ]

用例2:将一个通用方法转换成函数. 利用反科里化this可以将一个方法转换成一个用法更简单的函数.比如:
Array.forEach = Array.prototype.forEach.uncurryThis();
function printArgs() {
    Array.forEach(arguments, function (elem, index) {
        console.log(index+". "+elem);
    });
}

在未来版本的ECMAScript规范建议中已经有了很多类似的数组方法.
译者注:Firefox已经实现了Array.map,Array.forEach等方法.

4.实现uncurryThis()
下面是实现uncurryThis方法的三种方式.

实现1: Brendan Eich写的

Function.prototype.uncurryThis = function () { 
var f = this; 
return function () { 
var a = arguments; 
return f.apply(a[0], [].slice.call(a, 1)); 
}; 
};

实现2: 调用反科里化过的函数相当于在原方法上通过调用它的call()方法来执行.我们可以通过bind()方法把这个call()方法借过来:
Function.prototype.uncurryThis = function () { 
return this.call.bind(this); 
};

实现3: 定义的标准方法最好不要依赖过多的外部方法.此外,bind()方法只在ECMAScript 5中可用.因此我们重写了上面的实现2,如下:
Function.prototype.uncurryThis = function () { 
var f = this; 
return function () { 
return f.call.apply(f, arguments) 
}; 
};

上面的代码仍然是隐式的借用了call()方法.

5.反向操作也很有用 ? 科里化this
uncurryThis()的反向操作称之为curryThis().它将原函数的第一个参数转换成隐含的this参数.假如有个原函数:

function(self, arg) { 
return self.foo + arg; 
}

科里化this后成为:
function(arg) { 
return this.foo + arg; 
}

用例: 让一个方法把自己的this值传递到一个内嵌函数里.原来的写法:
var obj = {
    method: function (arg) {
        var self = this; // 让嵌套的函数访问到this
        someFunction(..., function() {
            self.otherMethod(arg);
        });
    },
    otherMethod: function (arg) { ... }
}

科里化后你可以这么写:
var obj = {
    method: function (self, arg) { // 附加参数`self`
        someFunction(..., function() {
            self.otherMethod(arg);
        });
    }.curryThis(), // 传入附加参数
    otherMethod: function (arg) { ... }
}

我们把隐含的参数this转换成了显式的参数self.换句话说:我们把一个动态的this转换成了一个静态的变量self.如果this总是作为一个显式的参数,则JavaScript会变的更简单点.

实现curryThis():

Function.prototype.curryThis = function () {
    var f = this;
    return function () {
        var a = Array.prototype.slice.call(arguments);
        a.unshift(this);
        return f.apply(null, a);
    };
};

6.如果你不想扩展函数原型

上面实现的方法都是加在了内置构造函数Function()的原型上.你应该可以轻松的将它们重写为独立的函数.

function uncurryThis(f) {
    return function () {
        return f.call.apply(f, arguments)
    };
}
function curryThis(f) {
    return function () {
        var a = Array.prototype.slice.call(arguments);
        a.unshift(this);
        return f.apply(null, a);
    };
}

7.在uncurryThis()安全的使用在已经存在的不信任的代码中

Mark Miller把uncurryThis()作为例子讲解了“安全的元编程”:

译者注:科里化this就是把函数的第一个参数转换成方法中的this.反科里化this就是把方法中的this转换成函数的第一个参数.

Javascript 相关文章推荐
一个js封装的不错的选项卡效果代码
Feb 15 Javascript
javascript 操作cookies及正确使用cookies的属性
Oct 15 Javascript
javascript中的Function.prototye.bind
Jun 25 Javascript
jquery实现动画菜单的左右滚动、渐变及图形背景滚动等效果
Aug 25 Javascript
Jquery 1.9.1源码分析系列(十二)之筛选操作
Dec 02 Javascript
使用Jasmine和Karma对AngularJS页面程序进行测试
Mar 05 Javascript
用js实现博客打赏功能
Oct 24 Javascript
详解Angular-ui-BootStrap组件的解释以及使用
Jul 13 Javascript
微信小程序有旋转动画效果的音乐组件实例代码
Aug 22 Javascript
微信小程序图片左右摆动效果详解
Jul 13 Javascript
有趣的JavaScript隐式类型转换操作实例分析
May 02 Javascript
vue打包npm run build时候界面报错的解决
Aug 13 Javascript
Array.prototype.concat不是通用方法反驳[译]
Sep 20 #Javascript
JavaScript 用Node.js写Shell脚本[译]
Sep 20 #Javascript
一个简单的网站访问JS计数器 刷新1次加1次访问
Sep 20 #Javascript
javascript分页代码(当前页码居中)
Sep 20 #Javascript
javascript获取作用在元素上面的样式属性代码
Sep 20 #Javascript
一个基于jquery的文本框记数器
Sep 19 #Javascript
html中的input标签的checked属性jquery判断代码
Sep 19 #Javascript
You might like
在PHP3中实现SESSION的功能(二)
2006/10/09 PHP
PHP5 安装方法
2007/01/15 PHP
php关于array_multisort多维数组排序的使用说明
2011/01/04 PHP
php设计模式 Facade(外观模式)
2011/06/26 PHP
PHP curl 获取响应的状态码的方法
2014/01/13 PHP
getJSON跨域SyntaxError问题分析
2014/08/07 PHP
PHP采用超长(超大)数字运算防止数字以科学计数法显示的方法
2016/04/01 PHP
程序员的表白神器“520”大声喊出来
2016/05/20 PHP
php技巧小结【推荐】
2017/01/19 PHP
JavaScript 垃圾回收机制分析
2013/10/10 Javascript
jquery实现图片上传之前预览的方法
2015/07/11 Javascript
跟我学习javascript的基本类型和引用类型
2015/11/16 Javascript
javascript中获取class的简单实现
2016/07/12 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
2016/10/24 Javascript
jQuery实现立体式数字动态增加(animate方法)
2016/12/21 Javascript
BootStrap+Mybatis框架下实现表单提交数据重复验证
2017/03/23 Javascript
详解@Vue/Cli 3 Invalid Host header 错误解决办法
2019/01/02 Javascript
纯JS开发baguetteBox.js响应式画廊插件
2020/06/28 Javascript
[15:07]lgd_OG_m2_BP
2019/09/10 DOTA
[03:17]DOTA2-DPC中国联赛1月29日Recap集锦
2021/03/11 DOTA
详解Python中使用base64模块来处理base64编码的方法
2016/07/01 Python
Python实现简易Web爬虫详解
2018/01/03 Python
Diango + uwsgi + nginx项目部署的全过程(可外网访问)
2018/04/22 Python
对Python 窗体(tkinter)文本编辑器(Text)详解
2018/10/11 Python
Django上线部署之IIS的配置方法
2019/08/22 Python
Python OrderedDict的使用案例解析
2019/10/25 Python
关于Python解包知识点总结
2020/05/05 Python
Python用K-means聚类算法进行客户分群的实现
2020/08/23 Python
css3 中实现炫酷的loading效果
2019/04/26 HTML / CSS
豪华床上用品 :Jennifer Adams
2019/09/15 全球购物
乌克兰最大的家用电器和电子产品连锁店:Eldorado
2019/10/02 全球购物
2014年政协委员工作总结
2014/12/01 职场文书
合作与交流自我评价
2015/03/09 职场文书
2015年班长个人工作总结
2015/04/03 职场文书
起诉书范文
2015/05/20 职场文书
2015年秋季运动会前导词
2015/07/20 职场文书