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