【你应该了解的】函数柯里化

团队:skFeTeam  本文做者:蔡旭光git

一道面试题

已知一个add方法。github

function add(a, b, c) {  
  return a + b + c;
}
add(1,2,3)   // 6
复制代码

实现一个sum方法,知足以下条件:面试

sum(1)(2,3)   // 6
sum(1)(2)(3)   // 6
复制代码

该题其实就运用了函数柯里化的思想,咱们能够将add方法经过函数柯里化转化为sum方法。编程

先来看看什么是函数柯里化。闭包

什么是函数柯里化

维基百科中这样定义柯里化:app

计算机科学中,柯里化(英语:Currying),又译为卡瑞化加里化,是把接受多个参数函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。函数式编程

用大白话来讲就是:只传递给函数一部分参数来调用它,让它返回一个新函数去处理剩下的参数。函数

举个例子理解柯里化:

假设你有一间商店,促销季,你想给普通顾客全部商品九折优惠。ui

function setDiscount(price, discount) {  
    return price * discount;
}
复制代码

当一个普通顾客买了一件价格为100元的商品,你计算价格能够:this

const price = setDiscount(100, 0.9);
复制代码

你会发现,每次计算一个商品价格,都须要输入价格和折扣,很麻烦。

const price1 = setDiscount(500, 0.9);
const price2 = setDiscount(1000, 0.9);
const price3 = setDiscount(2000, 0.9);
复制代码

咱们能够将函数setDiscount柯里化,避免每次都输入折扣。

function currySetDiscount(discount) {  
    return function(price) {    
        return discount * price;  
    };
}

const setNinetyPercent = currySetDiscount(0.9);
复制代码

如今咱们能够这样计算普通顾客购买商品时的价格:

const price = setNinetyPercent(500);
复制代码

一样的,针对VIP顾客,全部商品打八折,能够这样计算:

const setEightyPercent = currySetDiscount(0.8);

const price = setEightyPercent(500);
复制代码

函数柯里化的这种做用,能够理解为参数复用,延迟执行,减小了代码冗余,增长了代码可读性

函数柯里化的简单实现

回到开始的那道题,咱们来写一个curry函数。

function curry(fn, args) {  
    let len = fn.length;  // 待柯里化的函数的参数长度
    let tempArgs = args || [];  
    return function() {    
        tempArgs = tempArgs.concat([...arguments])    
        if (tempArgs.length < len) {      
            return curry.call(this, fn, tempArgs);    
        } else {      
            return fn.apply(this, tempArgs);    
        }  
    };
}
复制代码

原理:用闭包保存参数,当参数数量和原函数参数数量一致时,执行函数

能够这样来柯里化add,生成sum方法:

var sum = curry(add);

sum(1)(2,3)   // 6
sum(1)(2)(3)   // 6
复制代码

函数柯里化的应用

延迟计算

咱们经常使用的bind函数。

let obj = {
  name: 'jack'
}
let showName = function() {
    console.log(this.name)
}
let showJackName = showName.bind(obj);

showJackName(); // jack
复制代码

这里bind用来改变函数执行时的上下文,可是函数自己并不执行,因此本质上是延迟计算。

咱们看下bind的模拟实现,本质上就是一种柯里化。

function myBind(){
  var self = this;
  // 第一个对象参数
  var context = Array.prototype.shift.call(arguments);
  // 其他参数
  var bindArgs = Array.prototype.slice.call(arguments);
  // 临时函数
  var fTemp = function(){};
  function fn(){
    // 合并绑定参数以及调用时参数
    var args = bindArgs.concat(Array.prototype.slice.call(arguments));
    // 原函数执行(this指向给定对象)
    self.apply(context, args);
  }
  // 临时函数prototype指向原函数prototype
  fTemp.prototype = self.prototype;
  // 新函数prototype设为临时函数的实例对象(当原函数使用New建立实例)
  fn.prototype = new fTemp();
  return fn;
}
复制代码

总结

函数柯里化是函数式编程(一种编程范式)中的一个最基本的运算,它生于函数式编程,也主要服务于函数式编程(函数的组合前提是须要单参数的函数)。咱们平常开发中其实无需特地区分是否使用函数柯里化,闭包,高阶函数等都必定程度上与函数柯里化有殊途同归之妙。

想了解skFeTeam更多的分享文章,能够点这里,谢谢~

相关文章
相关标签/搜索