柯里化,或者说部分应用,是一种函数式编程的技术,对于熟悉以传统方式编写 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的柯里化函数的更多相关文章

  1. javascript curry 柯里化函数 仿lodash的curry

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 柯里化函数之Javascript

    柯里化函数之Javascript 定义 依据定义来说,柯里化就是将一个接收"多个"參数的函数拆分成一个或者很多个接收"单一"參数的函数.定义看起来是比較抽象的. ...

  3. javascript中利用柯里化函数实现bind方法

    柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预 ...

  4. 浅谈JavaScript中的柯里化函数

    首先,不可避免的要引经据典啦,什么是柯里化函数呢(from baidu): 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返 ...

  5. JavaScript 反柯里化

    浅析 JavaScript 中的 函数 uncurrying 反柯里化 柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间 ...

  6. curry柯里化函数实现

    curry柯里化函数实现 参考文章: 一行写出javascript函数式编程中的curry 感谢作者分享 第一步: 缓存原始函数的参数个数 function curry(fn) { var limit ...

  7. 理解Javascript的柯里化

    前言 本文1454字,阅读大约需要4分钟. 总括: 本文以初学者的角度来阐述Javascript中柯里化的概念以及如何在工作中进行使用. 原文地址:理解Javascript的柯里化 知乎专栏: 前端进 ...

  8. Swift # 柯里化函数

    前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...

  9. JavaScript之柯里化

    //未柯里化 function add(a,b){ return a + b; } //柯里化 function add(y){ return function(x){ console.log(y + ...

随机推荐

  1. MVC中获取所有按钮,并绑定事件!

    <script> var btns = $('[id=addbtn]'); //不能直接使用#ID来获取,必须用[] //循环遍历所有的按钮,一个一个添加事件绑定   for (var i ...

  2. 4-C#格式处理

    本篇博客对应视频讲解 前言 前几篇文章及对应视频是带大家快速体验了一下C#,了解编程语言最基础的内容及面向对象的概念. 接下来我会进一步演示和说明C#还能做些什么. 实际上,C#就一门语言来讲,除去面 ...

  3. php函数源代码 C编写 【持续更新】

    由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 strlen() 获取字符串长度,成功则返回字符串 string 的长度 ...

  4. Nmap 扫描

    最近在家里学习渗透,看到了nmap对服务器进行端口扫描,记录学习如下: Nmap支持非常多的扫描方式,包括TCP Syn ,TCP Connect,TCP ACK,TCP FIN/Xmas/NULL, ...

  5. 12c ocp 062新考题(之前没出现过)-1

    1.One of your databases has archive logging enabled and RMAN backups are taken at regular intervals. ...

  6. 【OCP题库】最新CUUG OCP 12c 071考试题库(65题)

    65.(22-16) choose the best answer: The CUSTOMERS table has the following structure: You need to writ ...

  7. vue 动态组件

    动态组件 多个组件通过同一个挂载点进行组件的切换,is的值是哪个组件的名称,那么页面就会显示哪个组件 内置组件 (内置组件不会被渲染到页面上) component is属性     keep-aliv ...

  8. 代码审计之Catfish CMS v4.5.7后台作者权限越权两枚+存储型XSS一枚

    首先本地搭建环境,我所使用的是Windows PHPstudy集成环境.使用起来非常方便.特别是审计的时候.可以任意切换PHP版本. 本文作者:226safe Team – Poacher 0×01 ...

  9. [CISCO] 转载:冲突域与广播域(区别、知识要点)

    [CISCO] 转载:冲突域与广播域(区别.知识要点) 1.传统以太网操作(Ethernet Connection Ethernet) 传统共享式以太网的典型代表是总线型以太网.在这种类型的以太网中, ...

  10. C++实现二叉树的相应操作

    1. 二叉树的遍历:先序(递归.非递归),中序(递归.非递归),后序(递归.非递归). #include <iostream> #include <string> #inclu ...