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


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 相关文章推荐
innerHTML,outerHTML,innerTEXT三者之间的区别
Jan 28 Javascript
jquery遍历input取得input的name
Apr 27 Javascript
onclick与listeners的执行先后问题详细解剖
Jan 07 Javascript
javascript控制图片播放的实现代码
Jul 29 Javascript
jquery+CSS3实现淘宝移动网页菜单效果
Aug 31 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
May 10 Javascript
前端弹出对话框 js实现ajax交互
Sep 09 Javascript
[原创]javascript typeof id==='string'?document.getElementById(id):id解释
Nov 02 Javascript
Angular2学习笔记——详解NgModule模块
Dec 02 Javascript
vue-cli构建项目使用 less的方法
Oct 04 Javascript
对vue生命周期的深入理解
Dec 03 Vue.js
Vue项目中如何封装axios(统一管理http请求)
May 02 Vue.js
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中实现Javascript的escape()函数代码
2010/08/08 PHP
基于ubuntu下nginx+php+mysql安装配置的具体操作步骤
2013/04/28 PHP
php中引用符号(&)的使用详解
2013/11/13 PHP
Laravel学习基础之migrate的使用教程
2017/10/11 PHP
php curl操作API接口类完整示例
2019/05/21 PHP
在JavaScript里嵌入大量字符串常量的实现方法
2013/07/07 Javascript
文本有关的样式和jQuery求对象的高宽问题分别说明
2013/08/30 Javascript
js获取客户端外网ip的简单实例
2013/11/21 Javascript
利用js制作html table分页示例(js实现分页)
2014/04/25 Javascript
使用javascript实现监控视频播放并打印日志
2015/01/05 Javascript
js实现touch移动触屏滑动事件
2015/04/17 Javascript
jQuery实现自定义右键菜单的树状菜单效果
2015/09/02 Javascript
jQuery网页右侧广告跟随滚动代码分享
2020/04/20 Javascript
Javascript 基础---Ajax入门必看
2016/07/06 Javascript
基于vue打包后字体和图片资源失效问题的解决方法
2018/03/06 Javascript
jQuery Ajax实现Select多级关联动态绑定数据的实例代码
2018/10/26 jQuery
JavaScript之数组扁平化详解
2019/06/03 Javascript
JavaScript 如何计算文本的行数的实现
2020/09/14 Javascript
[02:34]肉山说——泡妞篇
2014/09/16 DOTA
[59:08]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第一局
2016/02/27 DOTA
Python中实现远程调用(RPC、RMI)简单例子
2014/04/28 Python
利用Python脚本实现ping百度和google的方法
2017/01/24 Python
Python openpyxl读取单元格字体颜色过程解析
2019/09/03 Python
keras实现多GPU或指定GPU的使用介绍
2020/06/17 Python
利用python下载scihub成文献为PDF操作
2020/07/09 Python
python“静态”变量、实例变量与本地变量的声明示例
2020/11/13 Python
python 制作简单的音乐播放器
2020/11/25 Python
英国领先的瓷砖专家:Walls and Floors
2018/04/27 全球购物
C&A巴西网上商店:时尚、衣服、手机和鞋子
2020/06/07 全球购物
main 主函数执行完毕后,是否可能会再执行一段代码,给出说明
2012/12/05 面试题
水务局局长岗位职责
2013/11/28 职场文书
一年级数学教学反思
2014/02/01 职场文书
党员干部观看《周恩来四个昼夜》思想汇报
2014/09/10 职场文书
大学迎新生标语
2014/10/06 职场文书
2015年法律事务部工作总结
2015/07/27 职场文书
2016大学生毕业实习心得体会
2016/01/23 职场文书