javascript中有趣的反柯里化深入分析


Posted in Javascript onDecember 05, 2012

写在前面的话:国内对前端的研究在某些方面也不逊色于国外,这篇文章虽然看不太懂,但我很欣赏这种深入研究的精神!

反科里化的话题来自javascript之父Brendan Eich去年的一段twitter. 近几天研究了一下,觉得这个东东非常有意思,分享一下。先忘记它的名字,看下它能做什么.

javascript中有趣的反柯里化深入分析

不要小看这个功能,试想下,我们在写一个库的时候,时常会写这样的代码,拿webQQ的Jx库举例。

javascript中有趣的反柯里化深入分析
我们想要的,其实只是借用Array原型链上的一些函数。并没有必要去显式的构造一个新的函数来改变它们的参数并且重新运算。

如果用uncurrying的方式显然更加优雅和美妙,就像这样:

javascript中有趣的反柯里化深入分析

还能做很多有趣和方便的事情.

javascript中有趣的反柯里化深入分析

javascript中有趣的反柯里化深入分析

甚至还能把call和apply方法都uncurrying,把函数也当作普通数据来使用. 使得javascript中的函数调用方式更像它的前生scheme, 当函数名本身是个变量的时候, 这种调用方法特别方便.

scheme里面调用函数是这样:
javascript中有趣的反柯里化深入分析

javascript里可以写的很接近.
javascript中有趣的反柯里化深入分析

再看看jquery库,由于jquery对象( 即通过$()创建的对象 )是一个对象冒充的伪数组,它有length属性,并且能够通过下标查找对应的元素,当需要给jquery对象添加一个成员时, 伪代码大概是:

javascript中有趣的反柯里化深入分析

如果用uncurrying的话, 就可以

javascript中有趣的反柯里化深入分析

借用了array对象的push函数, 让引擎去自动管理数组成员和length属性.

而且可以一次把需要的函数全部借过来, 一劳永逸. 一段测试代码:

javascript中有趣的反柯里化深入分析

总的来说, 使用uncurrying技术, 可以让任何对象拥有原生对象的方法. 好了,如果到这里依然没有引起你的兴趣,那么你可以去干点别的了。

接下来一步一步来看看原理以及实现。
在了解反currying化这个奇怪的名字之前,我们得先搞清楚currying。

维基百科上的定义:科里化( currying ); 又称部分求值,是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数并且返回结果的新函数的技术。

通俗点讲,currying有点类似买房子时分期付款的方式,先给一部分首付( 一部分参数 ), 返回一个存折( 返回一个函数 ),合适的时候再给余下的参数并且求值计算。

来看看我们都用过的currying, 我们经常在绑定context 的时候实现一个Function.prototype.bind函数.

javascript中有趣的反柯里化深入分析

高阶函数是实现currying的基础, 所谓高阶函数至少满足这2个特性:
1,函数可以当作参数传递,
2,函数可以作为返回值。

Javascript在设计之初,参考了很多scheme语言的特性。而scheme是函数式语言鼻祖lisp的2大方言之一,所以javascript也拥有一些函数式语言的特性,包括高阶函数,闭包,lambda表达式等。

当javascript中的函数返回另一个函数,此时会形成一个闭包,而在闭包中就可以保存第一次运算的参数,我们用这个思想,来写一个通用的currying函数。

javascript中有趣的反柯里化深入分析

我们约定, 当传入参数时候, 继续currying化, 参数为空时才开始求值.

假设在实现一个计算每月花费的函数, 每天结束前我们都要记录今天花了多少钱, 但我们只关心月底的花费总值, 无需每天都计算一次.

javascript中有趣的反柯里化深入分析

使用currying函数, 便可以延迟到最后一刻才一起计算, 好处不言而喻, 在很多场合可以避免无谓的计算, 节省性能, 也是实现惰性求值的一种方案.

好了,现在才走进正题,

curring是预先填入一些参数.

反curring就是把原来已经固定的参数或者this上下文等当作参数延迟到未来传递.

其实就是搞这样一个事情,将:

obj.foo( arg1 ) //foo本来是只在obj上的函数. 就像push原本只在Array.prototype上

转化成这样的形式

foo( obj, arg1 ) // 跟我们举的第一个例子一样.将[].push转换成push( [] )

就像原本是接在电视插头上的插座,把它拆下来之后,其实也能用来接冰箱。

Ecma上Array和String的每个原型方法后面都有这么一段话,比如push:

NOTE The push function is intentionally generic; it does not require that its this value be an Array object.
Therefore it can be transferred to other kinds of objects for use as a method. Whether the concat function can be applied.

Javascript为什么要这样设计, 我们先来复习下动态语言中重要的鸭子类型思想.

说个故事:

很久以前有个皇帝喜欢听鸭子呱呱叫,于是他召集大臣组建一个一千只鸭子的合唱团。大臣把全国的鸭子都抓来了,最后始终还差一只。有天终于来了一只自告奋勇的鸡,这只鸡说它也会呱呱叫,好吧在这个故事的设定里,它确实会呱呱叫。 后来故事的发展很明显,这只鸡混到了鸭子的合唱团中。— 皇帝只是想听呱呱叫,他才不在乎你是鸭子还是鸡呢。

这个就是鸭子类型的概念,在javascript里面,很多函数都不做对象的类型检测,而是只关心这些对象能做什么。

Array构造器和String构造器的prototype上的方法就被特意设计成了鸭子类型。这些方法不对this的数据类型做任何校验。这也就是为什么arguments能冒充array调用push方法.

看下v8引擎里面Array.prototype.push的代码:

function ArrayPush() { 
var n = TO_UINT32( this.length ); 
var m = %_ArgumentsLength(); 
for (var i = 0; i < m; i++) { 
this[i+n] = %_Arguments(i); //属性拷贝 
this.length = n + m; //修正length 
return this.length; 
} 
}

可以看到,ArrayPush方法没有对this的类型做任何显示的限制,所以理论上任何对象都可以被传入ArrayPush这个访问者。

我们需要解决的只剩下一个问题, 如何通过一种通用的方式来使得一个对象可以冒充array对象。

真正的实现代码其实很简单:

javascript中有趣的反柯里化深入分析

这段代码虽然很短, 初次理解的时候还是有点费力. 我们拿push的例子看看它发生了什么.

var push = Array.prototype.push.uncurrying(); push( obj, ‘first' );

javascript中有趣的反柯里化深入分析

Javascript 相关文章推荐
JSON 学习之完全手册 图文
May 29 Javascript
extjs 学习笔记 四 带分页的grid
Oct 20 Javascript
javascript AOP 实现ajax回调函数使用比较方便
Nov 20 Javascript
js给dropdownlist添加选项的小例子
Mar 04 Javascript
JS对img进行操作(换图片/切图/轮换/停止)
Apr 17 Javascript
node爬取微博的数据的简单封装库nodeweibo使用指南
Jan 02 Javascript
自定义百度分享的分享按钮
Mar 18 Javascript
浅谈bootstrap源码分析之tab(选项卡)
Jun 06 Javascript
JavaScript的setter与getter方法
Nov 29 Javascript
详解bootstrap-fileinput文件上传控件的亲身实践
Mar 21 Javascript
微信小程序学习笔记之文件上传、下载操作图文详解
Mar 29 Javascript
用Golang运行JavaScript的实现示例
Nov 25 Javascript
js multiple全选与取消全选实现代码
Dec 04 #Javascript
在js(jquery)中获得文本框焦点和失去焦点的方法
Dec 04 #Javascript
关于javascript中的typeof和instanceof介绍
Dec 04 #Javascript
无缝滚动改进版支持上下左右滚动(封装成函数)
Dec 04 #Javascript
js动画(animate)简单引擎代码示例
Dec 04 #Javascript
JavaScript中“+”的陷阱深刻理解
Dec 04 #Javascript
将光标定位于输入框最右侧实现代码
Dec 04 #Javascript
You might like
php读取mysql乱码,用set names XXX解决的原理分享
2011/12/29 PHP
PHP超牛逼无限极分类生成树方法
2015/05/11 PHP
php的mail函数发送UTF-8编码中文邮件时标题乱码的解决办法
2015/10/20 PHP
Yii框架页面渲染操作实例详解
2019/07/19 PHP
php高性能日志系统 seaslog 的安装与使用方法分析
2020/02/29 PHP
php7 图形用户界面GUI 开发示例
2020/02/22 PHP
用js实现输入提示(自动完成)的实例代码
2013/06/14 Javascript
网站接入QQ登录的两种方法
2014/07/22 Javascript
JavaScript闭包函数访问外部变量的方法
2014/08/27 Javascript
浅析JavaScript回调函数应用
2016/05/22 Javascript
在Layui 的表格模板中,实现layer父页面和子页面传值交互的方法
2019/09/10 Javascript
Json实现传值到后台代码实例
2020/06/30 Javascript
python 实现归并排序算法
2012/06/05 Python
python中list列表的高级函数
2016/05/17 Python
Python爬虫的两套解析方法和四种爬虫实现过程
2018/07/20 Python
python2和python3的输入和输出区别介绍
2018/11/20 Python
python 在指定范围内随机生成不重复的n个数实例
2019/01/28 Python
Django项目中添加ldap登陆认证功能的实现
2019/04/04 Python
python实现对服务器脚本敏感信息的加密解密功能
2019/08/13 Python
python OpenCV GrabCut使用实例解析
2019/11/11 Python
Python爬虫程序架构和运行流程原理解析
2020/03/09 Python
Python如何爬取b站热门视频并导入Excel
2020/08/10 Python
基于CentOS搭建Python Django环境过程解析
2020/08/24 Python
使用PyCharm官方中文语言包汉化PyCharm
2020/11/18 Python
荷兰优雅女装网上商店:Heine
2016/11/14 全球购物
中国电视购物:快乐购
2017/02/04 全球购物
Brookstone美国官网:独特新奇产品
2017/03/04 全球购物
聚美优品励志广告词
2014/03/14 职场文书
地理教师岗位职责
2014/03/16 职场文书
小学亲子活动总结
2014/07/01 职场文书
元旦趣味活动方案
2014/08/22 职场文书
2017年寒假少先队活动总结
2016/04/06 职场文书
JS数组的常用方法整理
2021/03/31 Javascript
Python爬取英雄联盟MSI直播间弹幕并生成词云图
2021/06/01 Python
Java 深入探究讲解简单工厂模式
2022/04/07 Java/Android
Mysql 一主多从的部署
2022/05/20 MySQL