通过实例了解Javascript柯里化流程


Posted in Javascript onMarch 03, 2020

函数式编程是一种如今比较流行的编程范式,它主张将函数作为参数进行传递,然后返回一个没有副作用的函数,说白了,就是希望一个函数只做一件事情。

像Javascript,Haskell,Clojure等编程语言都支持函数式编程。

这种编程思想涵盖了三个重要的概念:

  • 纯函数
  • 柯里化
  • 高阶函数

而这篇文章主要是想向大家讲清楚柯里化这个概念。

什么是柯里化

首先我们先来看一个例子:

function sum(a, b, c) {
 return a + b + c;
}
// 调用
sum(1, 2, 3); // 6

上述函数实现的是将a,b,c三个参数相加,改写为柯里化函数如下:

function sum(a) {
 return function (b) {
  return function(c) {
    return a + b + c;
    } 
  }
}
// 调用
let sum1 = sum(1);
let sum2 = sum1(2);
sum2(3); // 6

所谓柯里化就是把具有较多参数的函数转换成具有较少参数的函数的过程。

我们来一步步看上面那个柯里化函数做了什么,首先第一步调用了sum(1),此时变量sum1相当于:

sum1 = function(b) {
  return function(c) {
  // 注意此时变量a存在于闭包中,可以调用,a = 1
  return a + b + c;
 }
}

然后调用sum1(2),此时赋值给变量sum2相当于:

sum2 = function(c) {
 // 变量a,b皆在闭包中, a = 1, b = 2
 return a + b + c;
}

最后调用sum2(3),返回1 + 2 + 3的结果6;

这就是一个最简单的柯里化函数,是不是很简单呢?

柯里化函数的作用

那么问题来了,上面改写后的柯里化函数和原函数比起来代码多了不少,而且也不如原函数好理解,柯里化函数到底有什么用呢?

确实,柯里化函数在这里看起来的确是很臃肿,不实用,但在很多场景下他的作用是很大的,甚至很多人在不经意间已经在使用柯里化函数了。举一个简单的例子:

假设我们有一批的长方体,我们需要计算这些长方体的体积,实现一个如下函数:

function volume(length, width, height) {
  return length * width * height;
}
volume(200, 100, 200);
volume(200, 150, 100);
volume(200, 50, 80);
volume(100, 50, 60);

如上计算长方体的体积函数会发现存在很多相同长度的长方体,我们再用柯里化函数实现一下:

function volume(length, width, height) {
  return function(width) {
    return function(height) {
     return length * width * height;
      }
  }
}
let len200 = volume(200);
len200(100)(200);
len200(150)(100);
len200(50)(80);
volume(100)(50)(60);

如上,通过实现一个len200函数我们统一处理长度为200的长方体的体积,这就实现了参数复用。

我们再举一个只执行一次函数的例子:

function execOnce(fun) {
  let flag = true;
 return function() {
  if (flag) {
   fun && fun();
   flag = false;
  }
 }
}
let onceConsole = execOnce(function() {
  console.log('只打印一次');
});
onceConsole();
onceConsole();

如上,我们实现了一个execOnce函数,该函数接受一个函数参数,然后返回一个函数,变量flag存在闭包中,用来判断返回的函数是否执行过,onceConsole相当于:

let onceConsole = function() {
 if (flag) {
    (function() {
    console.log('只打印一次');
   })()
   flag = false;
  }
}

这也是柯里化函数的一个简单应用。

通用柯里化函数的实现

既然柯里化函数这么实用,那么我们能不能实现一个通用的柯里化函数呢?所谓通用,就是说该函数可以把函数参数转换为柯里化函数,看下第一版实现的代码:

// 第一版
var curry = function (fn) {
 var args = [].slice.call(arguments, 1);
 return function() {
  var newArgs = args.concat([].slice.call(arguments));
  return fn.apply(null, newArgs);
 };
};
 function add(a, b) {
  return a + b;
 }

var addFun = curry(add, 1, 2);
addFun() // 3
//或者
var addOne = curry(add, 1);

如上代码,我们接受一个函数作为参数,然后收集其它的参数,将这些参数传给这个函数参数去执行。但上面的代码有个问题,参数不够自由,比如我们想这么调用就会报错:

var addFun = curry(function(a, b,c) {
 return a + b + c;
}, 1);
addFun(2)(3); // 报错 addFun(...) is not a function

这好像违背了我们参数复用的原则,改进如下:

function curry(fn, args) {
 var length = fn.length;
 args = args || [];
 return function(...rest) {
  var _args = [...args, ...rest];
  return _args.length < length
   ? curry.call(this, fn, _args)
  : fn.apply(this, _args);
 }
}
var fn = curry(function(a, b, c) {
 console.log(a + b + c);
});
fn('a', 'b', 'c'); // abc
fn('a', 'b')('c'); // abc
fn('a')('b')('c'); // abc

如上实现就很完善,该工具函数的实现总结起来就一句话:

利用闭包将函数的参数储存起来,等参数达到一定数量时执行函数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
文字幻灯片
Jun 26 Javascript
Mootools 1.2教程 选项卡效果(Tabs)
Sep 15 Javascript
js有序数组的连接问题
Oct 01 Javascript
jquery 获取dom固定元素 添加样式的简单实例
Feb 04 Javascript
详谈LABJS按需动态加载js文件
May 07 Javascript
基于jquery实现复选框全选,反选,全不选等功能
Oct 16 Javascript
js操作浏览器的参数方法
Jan 21 Javascript
vue axios 在页面切换时中断请求方法 ajax
Mar 05 Javascript
jQuery Ajax async=&gt;false异步改为同步时,解决导致浏览器假死的问题
Jul 22 jQuery
微信小程序返回箭头跳转到指定页面实例解析
Oct 08 Javascript
JS实现点星星消除小游戏
Mar 24 Javascript
解决vue项目运行提示Warnings while compiling.警告的问题
Sep 18 Javascript
微信小程序getLocation 需要在app.json中声明permission字段
Mar 03 #Javascript
Javascript作用域和作用域链原理解析
Mar 03 #Javascript
JS数组方法reduce的用法实例分析
Mar 03 #Javascript
Javascript模拟实现new原理解析
Mar 03 #Javascript
JS面向对象编程——ES6 中class的继承用法详解
Mar 03 #Javascript
JS面向对象编程实现的拖拽功能案例详解
Mar 03 #Javascript
序列化模块json代码实例详解
Mar 03 #Javascript
You might like
phpstrom使用xdebug配置方法
2013/12/17 PHP
几道坑人的PHP面试题 试试看看你会不会也中招
2014/08/19 PHP
PHP常量使用的几个需要注意的地方(谨慎使用PHP中的常量)
2014/09/12 PHP
php实现根据词频生成tag云的方法
2015/04/17 PHP
谈谈PHP中substr和substring的正确用法及相关参数的介绍
2015/12/16 PHP
将PHP程序中返回的JSON格式数据用gzip压缩输出的方法
2016/03/03 PHP
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
cookie的复制与使用记住用户名实现代码
2013/11/04 Javascript
JavaScript中判断变量是数组、函数或是对象类型的方法
2015/02/25 Javascript
js简单设置与使用cookie的方法
2016/01/22 Javascript
JS实现iframe自适应高度的方法(兼容IE与FireFox)
2016/06/24 Javascript
js实现碰撞检测特效代码分享
2016/10/16 Javascript
原生Aajax 和jQuery Ajax 写法个人总结
2017/03/24 jQuery
js使用i18n实现页面国际化的方法
2017/05/09 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
浅析node应用的timing-attack安全漏洞
2018/02/28 Javascript
vue安装和使用scss及sass与scss的区别详解
2018/10/15 Javascript
Vue封装Axios请求和拦截器的步骤
2020/09/16 Javascript
Vue实现穿梭框效果
2020/09/30 Javascript
Python的Django框架中settings文件的部署建议
2015/05/30 Python
Python中shutil模块的常用文件操作函数用法示例
2016/07/05 Python
Flask框架各种常见装饰器示例
2018/07/17 Python
python常用函数与用法示例
2019/07/02 Python
python Pandas如何对数据集随机抽样
2019/07/29 Python
使用html5制作loading图的示例
2014/04/14 HTML / CSS
优秀毕业生求职推荐信范文
2013/11/21 职场文书
六个一活动实施方案
2014/03/21 职场文书
学校节能减排倡议书
2014/05/16 职场文书
优秀大学生自荐信
2014/06/09 职场文书
爱护公共设施的标语
2014/06/24 职场文书
2014年小学国庆节活动方案
2014/09/16 职场文书
介绍信的写法
2015/01/31 职场文书
优秀志愿者感言
2015/08/01 职场文书
小学2016年“我们的节日·重阳节”活动总结
2016/04/01 职场文书
500字作文之难忘的同学
2019/12/20 职场文书
Node-Red实现MySQL数据库连接的方法
2021/08/07 MySQL