最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

详细介绍JavaScript函数柯里化的一些思考

来源:懂视网 责编:小采 时间:2020-11-27 20:24:21
文档

详细介绍JavaScript函数柯里化的一些思考

详细介绍JavaScript函数柯里化的一些思考:1. 高阶函数的坑在学习柯里化之前,我们首先来看下面一段代码:var f1 = function(x){ return f(x); }; f1(x);很多同学都能看出来,这些写是非常傻的,因为函数f1和f是等效的,我们直接令var f1 = f;就行了,完全没有必要包裹那么一层。但是,下面一段代码
推荐度:
导读详细介绍JavaScript函数柯里化的一些思考:1. 高阶函数的坑在学习柯里化之前,我们首先来看下面一段代码:var f1 = function(x){ return f(x); }; f1(x);很多同学都能看出来,这些写是非常傻的,因为函数f1和f是等效的,我们直接令var f1 = f;就行了,完全没有必要包裹那么一层。但是,下面一段代码

3. 函数柯里化进一步思考

如果说上一节的例子中,我们不是直接运行f(x),而是把函数f当做一个参数,结果会怎样呢?我们来看下面这个例子:

假设f1返回函数gg的作用域指向xs,函数f作为g的参数。最终我们可以写成如下形式:

var f1 = function(f,xs){
 return g.call(xs,f);
};

实际上,用f1来替代g.call(xxx)的做法叫反柯里化。例如:

var forEach = function(xs,f){
 return Array.prototype.forEach.call(xs,f);
};
var f = function(x){console.log(x);};
var xs = {0:'peng',1:'chen',length:2};
forEach(xs,f);

反curring就是把原来已经固定的参数或者this上下文等当作参数延迟到未来传递。
它能够在很大程度上简化函数,前提是你得习惯它。

抛开反柯里化,如果我们要柯里化f1怎么办?

使用闭包,我们可以写成如下形式:

var f1 = function(f){
 return function(xs){
 return g.call(xs,f);
 }
};
var f2 = f1(f);
f2(xs);

f传入f1中,我们就可以得到f2这个新函数。

只传给函数一部分参数通常也叫做局部调用(partial application),能够大量减少样板文件代码(boilerplate code)。

当然,函数f1传入的两个参数不一定非得包含函数+非函数,可能两个都是函数,也可能两个都是非函数。

我个人觉得柯里化并非是必须的,而且不熟悉的同学阅读起来可能会遇到麻烦,但是它能帮助我们理解JS中的函数式编程,更重要的是,我们以后在阅读类似的代码时,不会感到陌生。知乎上罗宸同学讲的挺好:

并非“柯里化”对函数式编程有意义。而是,函数式编程在把函数当作一等公民的同时,就不可避免的会产生“柯里化”这种用法。所以它并不是因为“有什么意义”才出现的。当然既然存在了,我们自然可以探讨一下怎么利用这种现象。

练习:

// 通过局部调用(partial apply)移除所有参数
var filterQs = function(xs) {
 return filter(function(x){ return match(/q/i, x); }, xs);
};
//这两个函数原题没有,是我自己加的
var filter = function(f,xs){
 return xs.filter(f);
};
var match = function(what,x){
 return x.match(what);
};

分析:函数filterQs的作用是:传入一个字符串数组,过滤出包含’q'的字符串,并组成一个新的数组返回。

我们可以通过如下步骤得到函数filterQs

a. filter传入的两个参数,第一个是回调函数,第二个是数组,filter主要功能是根据回调函数过滤数组。我们首先将filter函数柯里化:

var filter = function(f){
 return function (xs) {
 return xs.filter(f);
 }
};

b. 其次,filter函数传入的回调函数是matchmatch的主要功能是判断每个字符串是否匹配what这个正则表达式。这里我们将match也柯里化:

var match = function(what){
 return function(x){
 return x.match(what);
 }
};
var match2 = match(/q/i);

创建匹配函数match2,检查字符串中是否包含字母q。

c. 把match2传入filter中,组合在一起,就形成了一个新的函数:

var filterQs = filter(match2);
var xs = ['q','test1','test2'];
filterQs(xs);

从这个示例中我们也可以体会到函数柯里化的强大。所以,柯里化还有一个重要的功能:封装不同功能的函数,利用已有的函数组成新的函数。

4. 函数柯里化的递归调用

函数柯里化还有一种有趣的形式,就是函数可以在闭包中调用自己,类似于函数递归调用。如下所示:

function add( seed ) {
 function retVal( later ) {
 return add( seed + later );
 }
 retVal.toString = function() {
 return seed;
 };
 return retVal;
}
console.log(add(1)(2)(3).toString()); // 6

add函数返回闭包retVal,在retVal中又继续调用add,最终我们可以写成add(1)(2)(3)(...)这样柯里化的形式。
关于这段代码的解答,知乎上的李宏训同学回答地很好:

每调用一次add函数,都会返回retValue函数;调用retValue函数会调用add函数,然后还是返回retValue函数,所以调用add的结果一定是返回一个retValue函数。add函数的存在意义只是为了提供闭包,这个类似的递归调用每次调用add都会生成一个新的闭包。

5. 函数组合(compose)

函数组合是在柯里化基础上完成的:

var compose = function(f,g) {
 return function(x) {
 return f(g(x));
 };
};
var f1 = compose(f,g);
f1(x);

将传入的函数变成两个,通过组合的方式返回一个新的函数,让代码从右向左运行,而不是从内向外运行。

函数组合和柯里化有一个好处就是pointfree。

pointfree 模式指的是,永远不必说出你的数据。它的意思是说,函数无须提及将要操作的数据是什么样的。一等公民的函数、柯里化(curry)以及组合协作起来非常有助于实现这种模式。

// 非 pointfree,因为提到了数据:name
var initials = function (name) {
 return name.split(' ').map(compose(toUpperCase, head)).join('. ');
};

// pointfree
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));

initials("hunter stockton thompson");
// 'H. S. T'

文档

详细介绍JavaScript函数柯里化的一些思考

详细介绍JavaScript函数柯里化的一些思考:1. 高阶函数的坑在学习柯里化之前,我们首先来看下面一段代码:var f1 = function(x){ return f(x); }; f1(x);很多同学都能看出来,这些写是非常傻的,因为函数f1和f是等效的,我们直接令var f1 = f;就行了,完全没有必要包裹那么一层。但是,下面一段代码
推荐度:
标签: js 简介 函数
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top