JavaScript的柯里化函数
柯里化,或者说部分应用,是一种函数式编程的技术,对于熟悉以传统方式编写 JavaScript 代码的人来说可能会很费解。但如果使用得当,它可以使你的 JavaScript 函数更具可读性。
更具可读性和灵活性
函数式 JavaScript 被吹捧的优点之一就是拥有短小紧凑的代码风格,可以用最少行数、更少重复的代码得到正确的结果。有时这会以牺牲可读性为代价;如果你还不熟悉函数式编程的方法,这种方法写的代码会很难阅读和理解。
如果之前你遇到过柯里化这个术语,但是不知道它是什么意思,把它当做奇怪的、难理解的技术而不去理会,这也是可以理解的。但是事实上柯里化是一个非常简单的概念,它在处理函数参数的时候,涉及到一些常见的问题,同时为开发人员提供了灵活的选择范围。
什么是柯里化
简单来说,柯里化是一种允许使用部分函数参数构造函数的方式。也就是意味着,你在调用一个函数时,可以传入需要的全部参数并获得返回结果,也可以传入部分参数并的得到一个返回的函数,它需要传入的就是其余的参数。它真的就是那么简单。
在像 Haskell 和 Scala 这样的函数式编程语言中,柯里化是其基本原理。 JavaScript 具有函数式编程的能力,但是并没有默认支持柯里化函数(至少目前的版本没有支持)。但是我们已经知道了一些函数式的技巧,那我们就也可以让在 JavaScript 里实现柯里化。
为了让你理解柯里化的原理,我们开始来写第一个柯里化的 JavaScript 函数,使用熟悉的语法去新建一个我们想要的柯里化函数。比如说,我们假设有个函数是问候某个人的名字。我都很容易想到,创建一个简单的函数,接受问候语和一个名字作为参数,然后在控制台打印出整个问候的句子:
JavaScript
1
2
3
4
|
var greet = function(greeting, name) {
console.log(greeting + ", " + name);
};
greet("Hello", "Heidi"); //"Hello, Heidi"
|
这个函数需要把名字和问候语两个参数全部提供,才能够正确运行。但是我们可以使用简单的嵌套的柯里化方法重写这个函数,这样基本的函数只需要一个参数问候语,并返回以需要问候的名字为参数的另一个方法。
第一个柯里化函数
JavaScript
1
2
3
4
5
|
var greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};
|
我们刚刚所写的这种小小的调整,使我们获得了一个可以接受任何问候语为参数的新函数,并且返回一个以我们想要问候的人名为参数新函数:
JavaScript
1
2
3
|
var greetHello = greetCurried("Hello");
greetHello("Heidi"); //"Hello, Heidi"
greetHello("Eddie"); //"Hello, Eddie"
|
我们也可以直接调用原始的柯里化函数,只需要把每个参数分别加上小括号,一个接着一个:
JavaScript
1
|
greetCurried("Hi there")("Howard"); //"Hi there, Howard"
|
为什么不在你的浏览器里试试?
全部都柯里化
炫酷的事情来了,现在我们已经学会了如何用这种方法处理传统函数的参数,我们可以处理尽可能多的参数:
JavaScript
1
2
3
4
5
6
7
8
9
|
var greetDeeplyCurried = function(greeting) {
return function(separator) {
return function(emphasis) {
return function(name) {
console.log(greeting + separator + name + emphasis);
};
};
};
};
|
当有四个参数时和两个参数相比,具有同样地灵活性。无论嵌套多少层,我们都能够写出一个新的定制化的问候函数,无论我们选择了多少人、以多少种想要的方式。
JavaScript
1
2
3
|
var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?");
greetAwkwardly("Heidi"); //"Hello...Heidi?"
greetAwkwardly("Eddie"); //"Hello...Eddie?"
|
更重要的是,在原始的柯里化函数的基础上,我们可以传入需要的参数来创建一个新的变异函数,这个新的函数能够继续接受其余的参数,每个参数分别在一个圆括号里:
JavaScript
1
2
3
|
var sayHello = greetDeeplyCurried("Hello")(", ");
sayHello(".")("Heidi"); //"Hello, Heidi."
sayHello(".")("Eddie"); //"Hello, Eddie."
|
并且我们可以很容易地定义再下一级的变异函数:
JavaScript
1
2
3
|
var askHello = sayHello("?");
askHello("Heidi"); //"Hello, Heidi?"
askHello("Eddie"); //"Hello, Eddie?"
|
柯里化经典函数
你可以看到这种方法是多么的强大,尤其是当你需要许多复杂的定制化的函数时。唯一的问题就是语法。当你建立这些柯里化函数的时候,需要保证嵌套的返回子函数,并且调用它们的时候需要多组圆括号,每个都包含一个自己独立的参数。这会变得一团糟。
为了解决这个问题,一种方法就是去新建一个快速的脏柯里化函数,它以一个已经存在的没有嵌套返回的函数为参数。这个柯里化函数需要得到传入函数的参数列表,并用这些函数返回原始函数的柯里化版本:
JavaScript
1
2
3
4
5
6
7
8
|
var curryIt = function(uncurried) {
var parameters = Array.prototype.slice.call(arguments, 1);
return function() {
return uncurried.apply(this, parameters.concat(
Array.prototype.slice.call(arguments, 0)
));
};
};
|
为了使用这种方式,我们传入带有任意个参数的函数名字,以及我们想预填充的若干个参数。我们得到的就是一个需要其余参数的函数:
JavaScript
1
2
3
4
5
6
|
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."
|
正如之前,我们在用柯里化函数构建子函数的时候,并不会限制参数的个数:
JavaScript
1
2
|
var greetGoodbye = curryIt(greeter, "Goodbye", ", ");
greetGoodbye(".", "Joe"); //"Goodbye, Joe."
|
认真思考柯里化
我们这个小小的柯里化函数可能无法处理所有的边缘情况,比如丢失或可选参数,但只要我们严格按照语法传递参数,它就能够有效工作。
一些函数式的 JavaScript 库,如 Ramda 拥有更灵活的柯里化功能,它可以打乱一个函数需要的参数,并允许你单独或分组地传入参数,以创建一个定制化的柯里化的变形函数。如果你想广泛地柯里化,这可能是一个方向。
无论你选择如何对程序进行柯里化,是只用嵌套的括号还是更倾向于包括一个更稳健的柯里化函数,使用统一的命名规范有助于使你的代码更具可读性。每个派生出的函数都应该有一个可以清楚表明它行为和期望参数的名字。
参数顺序
进行柯里化的时候,一定要记住参数的顺序很重要。使用我们刚刚讲的方法,你很明显想要原始函数的最后一个的参数,是从柯里化函数一层层变形得到的函数的参数。
提前考虑的参数顺序会使柯里化更容易地应用到你的项目中。并且为了适应更多地情况,在设计函数时,考虑按照容易变化的程度排列参数的顺序不是一个坏习惯。
结论
柯里化对于函数式 JavaScript 是一种极其有用的技术。它允许你生成一个简洁、易配置、表现统一的库,而且使用起来上手快、具有可读性。在你的编码实践中加入柯里化会激励你在全部代码中的部分函数上应用它,这样避免了很多潜在的重复工作,并可以帮助你养成关于函数命名和处理函数参数的好习惯。
JavaScript的柯里化函数的更多相关文章
- javascript curry 柯里化函数 仿lodash的curry
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 柯里化函数之Javascript
柯里化函数之Javascript 定义 依据定义来说,柯里化就是将一个接收"多个"參数的函数拆分成一个或者很多个接收"单一"參数的函数.定义看起来是比較抽象的. ...
- javascript中利用柯里化函数实现bind方法
柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预 ...
- 浅谈JavaScript中的柯里化函数
首先,不可避免的要引经据典啦,什么是柯里化函数呢(from baidu): 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返 ...
- JavaScript 反柯里化
浅析 JavaScript 中的 函数 uncurrying 反柯里化 柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间 ...
- curry柯里化函数实现
curry柯里化函数实现 参考文章: 一行写出javascript函数式编程中的curry 感谢作者分享 第一步: 缓存原始函数的参数个数 function curry(fn) { var limit ...
- 理解Javascript的柯里化
前言 本文1454字,阅读大约需要4分钟. 总括: 本文以初学者的角度来阐述Javascript中柯里化的概念以及如何在工作中进行使用. 原文地址:理解Javascript的柯里化 知乎专栏: 前端进 ...
- Swift # 柯里化函数
前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...
- JavaScript之柯里化
//未柯里化 function add(a,b){ return a + b; } //柯里化 function add(y){ return function(x){ console.log(y + ...
随机推荐
- iOS 的各种识别码
在iOS系统中,获取设备唯一标识的方法有很多: 1. x UDID (Unique Device Identifier,已过期) 2. x OPEN UDID (已过期) 3. 广告标示符(IDFA- ...
- django系列3.2--url的别名和反向解析 reverse
命名URL和反向解析 在网页中某些要提交的地址等,需要改变的时候,此时网页过多,如果一个一个的去改,工作量巨大,这时就可以用到命名url,在html文件的模版中,只写命名,这样当需要我们改变urls. ...
- Git 教程 -- 基于自己学习记录
Git 教程 -- 基于自己学习记录 1. 引言 由于学校布置了一项熟悉 git 和 svn 操作的实验,所以自己重新温习了下 git,记录过程在这. 2. 注册登录 GitHub. 3. 选择一个仓 ...
- 洛谷P2179 [NOI2012]骑行川藏(拉格朗日乘数法)
题面 传送门 题解 看\(mashirosky\)大佬的题解吧--这里 //minamoto #include<bits/stdc++.h> #define R register #def ...
- httpclient4.5 https请求 忽略身份验证
import org.apache.commons.collections.MapUtils; import org.apache.http.*; import org.apache.http.cli ...
- 【微信小程序】——wxss引用外部CSS文件及iconfont
小程序引入外部文件的方式是:@import "*/*.wxss"; 因为业务需要,正在开发的小程序中需要使用iconfont,很容易想到了H5的引入方式: ```` @font-f ...
- Linux 下的 netfilter 认识与常规操作
Linux 下的 netfilter 认识与常规操作 前言 博客写到今天,1年7个月.可是包含所有写作经历,这个时间线可以达到三年. 上次更新了一篇 "镇站之宝" ,也是本站阅读量 ...
- C语言实现单链表,并完成链表常用API函数
C语言实现单链表,并完成链表常用API函数: 1.链表增.删.改.查. 2.打印链表.反转打印.打印环形链表. 3.链表排序.链表冒泡排序.链表快速排序. 4.求链表节点个数(普通方法.递归方法). ...
- mysql 查询小技巧
数据字段中存放的是id集,形如 1,2,15,35 也可类推json格式 查询时不用拆分了, 用上 instr.concat搜索和连接字符串 查询fids中包含15的 select * from ...
- day2: python3.5学习——逻辑判断
1. 简单的用户名和密码输入 username = "Helen"password = "123abc" _username = input("use ...