详解用场景去理解函数柯里化(入门篇)


Posted in Javascript onApril 11, 2019

前言

函数柯里化就是将多参简化为单参数的一种技术方式,其最终支持的是方法的连续调用,每次返回新的函数,在最终符合条件或者使用完所有的传参时终止函数调用。

场景实例

与其他文章不同,我在本文会重点分享一些柯里化的经典使用场景,让你在学会这点技巧后能切实的提升代码的可维护性。

编写可重用小模块代码

比如我们有个方法部分逻辑前置是相同的,后面的执行是因为参数不同导致结果不同的,下面是代码部分。
计算商品的折扣,我们需要根据不同的折扣以及商品的入参返回其实际的价格。

// before
function getPrice(price,discount){
	return price * discount;
}

let price = getPrice(500,0.1);


// after 
function getPrice(discount){
	return price =>{
 	return price * discount
 }
}
// 使用,在这种使用效果下,我们可以固定的肢解拿到百分之十折扣的函数,
//也就是针对使用0.1折扣的商品价格都可以简化这个折扣的传递,从而达到简化参数的目的
//那么从函数的运行上来讲,也比之前的效率高了,如果解析折扣的过程比较复杂
let tenDiscount = getPrice(0.1);
let price = tenDiscount(500);

let price = getPrice(0.1)(500)

看上去有点鸡肋,因为我们本来的写法很简单,使用了柯里化反而让简单的事情变得复杂了,这主要是因为没有达到我们要把一个函数变成柯里化的经典场景。假如你下面的代码变成了下面这样,也许你就能觉察出如果有使用柯里化就会非常方便了,因为针对第一个参数做了若干的处理,甚至可以称为一个算法或者完整的逻辑判断流程,那么如果有多个参数调用都涉及这个方法的调用,同一个参数的这部分逻辑是相同可以共用跳过的。codepen连接:链接

// complexed fun 
function getPriceComplex(price,discount){
 let actualDiscount = 1;
 if(discount > 0.8 ) {
 	actualDiscount = 0.8;
 } else if(discount > 0.5){
 	actualDiscount = 0.5;
 } else {
 actualDiscount = 0.1;
 }
 let actualPrice = price - price % 100 ;
	return actualPrice * actualDiscount;
}

// complexed fun better
function getPriceComplexBetter(discount){
 let actualDiscount = 1;
 if(discount > 0.8 ) {
 	actualDiscount = 0.8;
 } else if(discount > 0.5){
 	actualDiscount = 0.5;
 } else {
 actualDiscount = 0.1;
 }
 return price => {
 	 let actualPrice = price - price % 100 ;
			return actualPrice * actualDiscount;
 }
}


console.log(getPriceComplex(500,0.9))
let exp1 = getPriceComplexCp(0.9);
console.log(exp1);
/** price => {
 let actualPrice = price - price % 100;
 return actualPrice * actualDiscount;
}*/
// 相同的输入参数时 可以缓存下之前代码逻辑的执行结果 实现模块的可重用,如果你之前的逻辑是一个纯函数
console.log(exp1(500))// 400
console.log(exp1(400))// 320


// get real discount 
// 当你针对第一个参数的逻辑较为复杂时,出于可维护角度,建议如此 ;
// 当你另外一个逻辑也是基于这个返回结果时,出于重用角度,建议如此
function getActualDiscount(discount){
 let actualDiscount = 1;
 if(discount > 0.8 ) {
 	actualDiscount = 0.8;
 } else if(discount > 0.5){
 	actualDiscount = 0.5;
 } else {
 actualDiscount = 0.1;
 }
 return actualDiscount;
}
// complexed fun best
function getPriceComplexBest(discount){
 let actualDiscount =getActualDiscount(discount);
 return price => {
 	 let actualPrice = price - price % 100 ;
			return actualPrice * actualDiscount;
 }
}

总结,无论如何,我们使用某种技巧或者封装或者其他,都是为了让代码更可用,原先复杂不可测试、不可理解的代码变得更有调理,更节省性能的角度出发的,当你的思维方式中有这种的时候,你就不会觉得是为了形式而使用,而是你的编码习惯或者风格就是如此。

简单改造普通函数为柯里

假如我们需要把一个原来非柯里的函数如何快速改造,在不影响原来主要代码逻辑的情况下,想下我们代码可能如何写?

// 只考虑两个参数
function add(a,b){
 return a + b
}

// 但如果你是用柯里化的方式:两个参数的时候 ,但这样对原代码变动非常大,对于一些复杂的逻辑,这基本不可能
function curryAdd(...args){
 return (...newArgs) => {
 	return anoNumber * number;
 };
}

// 我们写一个通用的柯里化函数的方式,经过这个函数的转换,我们可以将调用方式简化
function curry = (fn,...args){
	return (..._args)=>{
 	return fn(...args, ..._arg);
 }
}

let curryAdd = curry(add,10);
let curryAdd2 = curryAdd(11)

不定参数的累加

一个比较经典的练手题,把下面的代码用柯里化的方式实现,其难点简单分析如下:如果你没有了解过柯里化,可能觉得基本无法完成。

1 动态入参个数,这个也许还可以通过arguments循环完成2 每次都能接受新的参数继续累加,这必须是返回新函数并带有之前的结果,要求是具有柯里化特点3 每次不在追加参数时,需要能得到的值,这个需要你了解toString方法来改变结果值

实现一个add方法,使计算结果能够满足如下预期: add(1)(2)(3) = 6

add(1, 2, 3)(4) = 10

add(1)(2)(3)(4)(5) = 15

function add() {
 // 第一次执行时,定义一个数组专门用来存储所有的参数
 var _args = [].slice.call(arguments);
 // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值,执行时已经收集所有参数为数组
 var adder = function () {
  var _adder = function() {
   // 执行收集动作,每次传入的参数都累加到原参数
   [].push.apply(_args, [].slice.call(arguments));
   return _adder;
  };
  // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
  _adder.toString = function () {
   return _args.reduce(function (a, b) {
    return a + b;
   });
  }
  return _adder;
 }
 return adder(_args);
}

备注:codepen中的console.log方法被重写,会有报错的问题,你可以直接通过浏览器的console控制台调试这个方法。

部分参数应用

部分参数应用是指有些场景是希望固定传递多个参数,来得到其固定的函数,然后基于这个函数去执行代码。类似于第一个例子中的一个折扣参数得出折扣算法的使用。我们将第一个例子再复杂化一些。就会变成这样的。

function getActualDiscount(custoemrLevel,discount){
	
}
function getPriceComplex (custoemrLevel,discount){
	let actualDiscount = getActualDiscount(custoemrLevel,discount);
 return price=>{
 	return price * actualDiscount;
 }
}
// 等级一的折扣策略 
let strategyLev1WithOnepoint = getPriceComplex('lev1',0.1) ;
let actualPrice = strategyLev1WithOnepoint(500);

以上所述是小编给大家介绍的用场景去理解函数柯里化(入门篇)详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JS JavaScript获取Url参数,src属性参数
Mar 09 Javascript
JQUERY复选框CHECKBOX全选,取消全选
Aug 30 Javascript
js几个验证函数代码
Mar 25 Javascript
用NODE.JS中的流编写工具是要注意的事项
Mar 01 Javascript
使用Jasmine和Karma对AngularJS页面程序进行测试
Mar 05 Javascript
jquery遍历table的tr获取td的值实现方法
May 19 Javascript
url中的特殊符号有什么含义(推荐)
Jun 17 Javascript
第五篇Bootstrap 排版
Jun 21 Javascript
给Easyui-Datebox设置隐藏或者不可用的解决方法
May 26 Javascript
让你彻底掌握es6 Promise的八段代码
Jul 26 Javascript
vue中配置mint-ui报css错误问题的解决方法
Oct 11 Javascript
Vue中$refs的用法详解
Jun 24 Javascript
Vue开发Html5微信公众号的步骤
Apr 11 #Javascript
跟混乱的页面弹窗说再见
Apr 11 #Javascript
vue实现todolist功能、todolist组件拆分及todolist的删除功能
Apr 11 #Javascript
vue实现todolist基本功能以及数据存储功能实例详解
Apr 11 #Javascript
JavaScript高阶教程之“==”隐藏下的类型转换
Apr 11 #Javascript
使用Vue父子组件通信实现todolist的功能示例代码
Apr 11 #Javascript
详解jQuery设置内容和属性
Apr 11 #jQuery
You might like
PHP统计目录下的文件总数及代码行数(去除注释及空行)
2011/01/17 PHP
PHP扩展模块Pecl、Pear以及Perl的区别
2014/04/09 PHP
PHP中shuffle数组值随便排序函数用法
2014/11/21 PHP
功能强大的php文件上传类
2016/08/29 PHP
基于PHPexecl类生成复杂的报表表头示例
2016/10/14 PHP
Yii2下点击验证码的切换实例代码
2017/03/14 PHP
TP5(thinkPHP5)框架基于ajax与后台数据交互操作简单示例
2018/09/03 PHP
javascript利用初始化数据装配模版的实现代码
2010/11/17 Javascript
AJAX异步从优酷专辑中采集所有视频及信息(JavaScript代码)
2010/11/20 Javascript
js获取键盘按键响应事件(兼容各浏览器)
2013/05/16 Javascript
jQuery实现等比例缩放大图片让大图片自适应页面布局
2013/10/16 Javascript
利用cookie记住背景颜色示例代码
2013/11/04 Javascript
使用jquery清空、复位整个输入域
2015/04/02 Javascript
JavaScript通过Date-Mask将日期转换成字符串的方法
2015/06/04 Javascript
AngularJS国际化详解及示例代码
2016/08/18 Javascript
vue router下的html5 history在iis服务器上的设置方法
2017/10/18 Javascript
JavaScript学习笔记之数组基本操作示例
2019/01/09 Javascript
JavaScript多种页面刷新方法小结
2019/04/04 Javascript
vue使用自定义指令实现拖拽
2021/01/29 Javascript
python实现网页链接提取的方法分享
2014/02/25 Python
Python聚类算法之DBSACN实例分析
2015/11/20 Python
Python基于matplotlib绘制栈式直方图的方法示例
2017/08/09 Python
python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解
2017/11/24 Python
Python实现感知机(PLA)算法
2017/12/20 Python
对python 生成拼接xml报文的示例详解
2018/12/28 Python
Django基础三之视图函数的使用方法
2019/07/18 Python
python圣诞树编写实例详解
2020/02/13 Python
Sunglasses Shop英国:欧洲领先的太阳镜在线供应商之一
2018/09/19 全球购物
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
2012/05/30 面试题
《彩色世界》教学反思
2014/04/12 职场文书
2014小学语文教师个人工作总结
2014/12/03 职场文书
努力学习保证书
2015/02/26 职场文书
校长师德表现自我评价
2015/03/05 职场文书
小学生组织委员竞选稿
2015/11/21 职场文书
pytorch 使用半精度模型部署的操作
2021/05/24 Python
Nginx+Windows搭建域名访问环境的操作方法
2022/03/17 Servers