通过实例了解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 相关文章推荐
发布一个基于javascript的动画类 Fx.js
Nov 05 Javascript
jQuery总体架构的理解分析
Mar 07 Javascript
Javascript 面向对象(一)(共有方法,私有方法,特权方法)
May 23 Javascript
jQuery插件开发基础简单介绍
Jan 07 Javascript
批量修改标签css样式以input标签为例
Jul 31 Javascript
jQuery多级弹出菜单插件ZoneMenu
Dec 18 Javascript
php利用curl获取远程图片实现方法
Oct 26 Javascript
基于jquery实现可定制的web在线富文本编辑器附源码下载
Nov 17 Javascript
真正好用的js验证上传文件大小的简单方法
Oct 27 Javascript
Nuxt.js实战和配置详解
Aug 05 Javascript
如何基于layui的laytpl实现数据绑定的示例代码
Apr 10 Javascript
vue 使用lodash实现对象数组深拷贝操作
Sep 10 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
提升PHP速度全攻略
2006/10/09 PHP
用php来检测proxy
2006/10/09 PHP
PHP实现取得HTTP请求的原文
2014/08/18 PHP
IE6/7/8/9不支持exec的简写方式
2011/05/25 Javascript
jQuery+JSON+jPlayer实现QQ空间音乐查询功能示例
2013/06/17 Javascript
JS实现局部选择打印和局部不选择打印
2014/04/03 Javascript
js+HTML5实现视频截图的方法
2015/06/16 Javascript
浅谈javascript中的call、apply、bind
2016/03/06 Javascript
Angular2利用组件与指令实现图片轮播组件
2017/03/27 Javascript
详解创建自定义的Angular Schematics
2018/06/06 Javascript
Angular中sweetalert弹框的基本使用教程
2018/07/22 Javascript
vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native
2018/08/25 Javascript
Vue+tracking.js 实现前端人脸检测功能
2020/04/16 Javascript
JavaScript实现HSL拾色器
2020/05/21 Javascript
uniapp开发小程序实现滑动页面控制元素的显示和隐藏效果
2020/12/10 Javascript
[03:14]DOTA2斧王 英雄基础教程
2013/11/26 DOTA
Python函数学习笔记
2008/10/07 Python
Python实现感知器模型、两层神经网络
2017/12/19 Python
Python cookbook(数据结构与算法)从任意长度的可迭代对象中分解元素操作示例
2018/02/13 Python
linux下python使用sendmail发送邮件
2018/05/22 Python
HTML5之SVG 2D入门4—笔画与填充
2013/01/30 HTML / CSS
美国百年历史早餐食品供应商:Wolferman’s
2017/01/18 全球购物
纽约服装和生活方式品牌:Saturdays NYC
2017/08/13 全球购物
美国大码时尚女装购物网站:ELOQUII
2017/12/28 全球购物
BAILEY 44官网:美国制造的女性服装
2019/07/01 全球购物
市场营销战略计划书
2014/05/06 职场文书
放飞梦想演讲稿200字
2014/08/26 职场文书
大学辅导员述职报告
2015/01/10 职场文书
项目经理岗位职责
2015/01/31 职场文书
技术入股协议书
2016/03/22 职场文书
2019年入党思想汇报
2019/03/25 职场文书
2019奶茶店创业计划书范本,值得你借鉴
2019/08/14 职场文书
python numpy中multiply与*及matul 的区别说明
2021/05/26 Python
基于tensorflow权重文件的解读
2021/05/26 Python
golang用type-switch判断interface的实际存储类型
2022/04/14 Golang
Win11右下角图标点了没反应怎么办?Win11点击右下角图标无反应解决方法汇总
2022/07/07 数码科技