Javascript柯里化实现原理及作用解析


Posted in Javascript onOctober 22, 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的柯里化。

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

Javascript 相关文章推荐
JavaScript 设计模式 富有表现力的Javascript(一)
May 26 Javascript
jQuery动态设置form表单的enctype值(实现代码)
Jul 04 Javascript
javascript对话框使用方法(警告框 javascript确认框 提示框)
Jan 07 Javascript
JS+DIV+CSS排版布局实现美观的选项卡效果
Oct 10 Javascript
javascript瀑布流式图片懒加载实例解析与优化
Feb 23 Javascript
关于原生js中bind函数的简单实现
Aug 10 Javascript
jQuery Validate插件实现表单验证
Aug 19 Javascript
js基础之DOM中元素对象的属性方法详解
Oct 28 Javascript
原生JS仿QQ阅读点击展开、收起效果
Mar 08 Javascript
jQuery实现打开网页自动弹出遮罩层或点击弹出遮罩层功能示例
Oct 19 jQuery
浅谈关于vue中scss公用的解决方案
Dec 02 Javascript
vue2和vue3的v-if与v-for优先级对比学习
Oct 10 Javascript
Node.js fs模块原理及常见用途
Oct 22 #Javascript
使用vue构建多页面应用的示例
Oct 22 #Javascript
vue 单页应用和多页应用的优劣
Oct 22 #Javascript
Javascript Symbol原理及使用方法解析
Oct 22 #Javascript
多个Vue项目部署到服务器的步骤记录
Oct 22 #Javascript
针对Vue路由history模式下Nginx后台配置操作
Oct 22 #Javascript
微信小程序基于高德地图API实现天气组件(动态效果)
Oct 22 #Javascript
You might like
Smarty模板快速入门
2007/01/04 PHP
PHP+MYSQL会员系统的登陆即权限判断实现代码
2011/09/23 PHP
PHP 如何利用phpexcel导入数据库
2013/08/24 PHP
Laravel框架FormRequest中重写错误处理的方法
2019/02/18 PHP
基于JQuery的简单实现折叠菜单代码
2010/09/15 Javascript
Extjs4.0设置Ext.data.Store传参的请求方式(默认为GET)
2013/04/02 Javascript
JS记录用户登录次数实现代码
2014/01/15 Javascript
jquery实现键盘左右翻页特效
2015/04/30 Javascript
如何解决ligerUI布局时Center中的Tab高度大小
2015/11/24 Javascript
JS判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个)
2016/08/02 Javascript
jQuery与JS加载事件用法分析
2016/09/04 Javascript
Javascript 获取鼠标当前的位置实现方法
2016/10/27 Javascript
AngularJS实现使用路由切换视图的方法
2017/01/24 Javascript
Node.js搭建小程序后台服务
2018/01/03 Javascript
基于 D3.js 绘制动态进度条的实例详解
2018/02/26 Javascript
JavaScript数据结构与算法之检索算法示例【二分查找法、计算重复次数】
2019/02/22 Javascript
解决vue单页面多个组件嵌套监听浏览器窗口变化问题
2020/07/30 Javascript
[02:52]2014DOTA2西雅图国际邀请赛 CIS战队巡礼
2014/07/07 DOTA
python实现rest请求api示例
2014/04/22 Python
Python AES加密模块用法分析
2017/05/22 Python
python 正确保留多位小数的实例
2018/07/16 Python
Python实现制度转换(货币,温度,长度)
2019/07/14 Python
Python3 使用pillow库生成随机验证码
2019/08/26 Python
通过Python实现一个简单的html页面
2020/05/16 Python
Python通用唯一标识符uuid模块使用案例
2020/09/10 Python
浅谈CSS3鼠标移入图片动态提示效果(transform)
2017/11/06 HTML / CSS
HTML5 视频播放(video),JavaScript控制视频的实例代码
2018/10/08 HTML / CSS
YSL圣罗兰美妆美国官网:Yves Saint Lauret US
2016/11/21 全球购物
英国领先的NHS批准的在线药店:Pharmacy2U
2017/01/06 全球购物
GUESS Factory加拿大:牛仔裤、服装及配饰
2019/09/20 全球购物
大三在校生电子商务求职信
2013/10/29 职场文书
干部考核评语
2014/04/29 职场文书
经营理念口号
2014/06/21 职场文书
小学新课改心得体会
2016/01/22 职场文书
如何创建一个创建MySQL数据库中的datetime类型
2022/03/21 MySQL
解决 Redis 秒杀超卖场景的高并发
2022/04/12 Redis