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

前言

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

场景实例

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

编写可重用小模块代码

好比咱们有个方法部分逻辑前置是相同的,后面的执行是由于参数不一样致使结果不一样的,下面是代码部分。算法

计算商品的折扣,咱们须要根据不一样的折扣以及商品的入参返回其实际的价格。数组

// 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)
复制代码

不定参数的累加

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

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);
复制代码

参考文章

相关文章
相关标签/搜索