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 相关文章推荐
脚本吧 - 幻宇工作室用到js,超强推荐share.js
Dec 23 Javascript
jQuery 标题的自动翻转实现代码
Oct 14 Javascript
javascript动态添加、修改、删除对象的属性与方法详解
Jan 27 Javascript
javascript 回到顶部效果的实现代码
Feb 17 Javascript
JavaScript获取XML数据附示例截图
Mar 05 Javascript
js仿土豆网带缩略图的焦点图片切换效果实现方法
Feb 23 Javascript
JS实现黑客帝国文字下落效果
Sep 01 Javascript
jQuery Html控件基本操作(日常收集整理)
Mar 11 Javascript
Node.js Addons翻译(C/C++扩展)
Jun 12 Javascript
文本框只能输入数字的js代码(含小数点)
Jul 10 Javascript
vue脚手架vue-cli的学习使用教程
Jun 06 Javascript
VUE2.0+ElementUI2.0表格el-table实现表头扩展el-tooltip
Nov 30 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
第七节 类的静态成员 [7]
2006/10/09 PHP
php 获得汉字拼音首字母的函数
2009/08/01 PHP
避免Smarty与CSS语法冲突的方法
2015/03/02 PHP
PHP SPL标准库之数据结构栈(SplStack)介绍
2015/05/12 PHP
php上传图片获取路径及给表单字段赋值的方法
2016/01/23 PHP
PHP微信公众号自动发送红包API
2016/06/01 PHP
jQuery中与toggleClass等价的程序段 以及未来学习的方向
2010/03/18 Javascript
jquery.uploadify插件在chrome浏览器频繁崩溃解决方法
2015/03/01 Javascript
jQuery实现类似标签风格的导航菜单效果代码
2015/08/25 Javascript
Fullpage.js固定导航栏-实现定位导航栏
2016/03/17 Javascript
使用Promise解决多层异步调用的简单学习心得
2016/05/17 Javascript
基于JavaScript实现的折半查找算法示例
2017/04/14 Javascript
Angular2入门教程之模块和组件详解
2017/05/28 Javascript
jQuery实现frame之间互通的方法
2017/06/26 jQuery
基于JavaScript实现弹幕特效
2020/08/27 Javascript
vue 组件的封装之基于axios的ajax请求方法
2018/08/11 Javascript
详解在vue-cli中使用graphql即vue-apollo的用法
2018/09/08 Javascript
VUE 动态组件的应用案例分析
2019/12/02 Javascript
js String.prototype.trim字符去前后空格的扩展
2020/08/23 Javascript
在antd4.0中Form使用initialValue操作
2020/11/02 Javascript
Element-ui upload上传文件限制的解决方法
2021/01/22 Javascript
Python捕获异常堆栈信息的几种方法(小结)
2020/05/18 Python
Python如何给函数库增加日志功能
2020/08/04 Python
HTML5中外部浏览器唤起微信分享功能的代码
2020/09/15 HTML / CSS
Bootstrap File Input文件上传组件
2020/12/01 HTML / CSS
美国在线纱线商店:Darn Good Yarn
2019/03/20 全球购物
大学生职业生涯规划范文——找准自我,定位人生
2014/01/23 职场文书
保护环境倡议书500字
2014/05/19 职场文书
住房租房协议书
2014/08/20 职场文书
办公楼租房协议书范本
2014/11/25 职场文书
2015新年寄语(一句话)
2014/12/08 职场文书
2015年图书馆个人工作总结
2015/05/26 职场文书
运动员加油词
2015/07/18 职场文书
医务人员医德医风心得体会
2016/01/25 职场文书
正确使用MySQL update语句
2021/05/26 MySQL
Windows Server 版本 20H2 于 8 月 9 日停止支持,Win10 版本 21H1 将于 12 月结束支
2022/07/23 数码科技