这是我参与 8 月更文挑战的第 5 天,活动详情查看: 8月更文挑战
柯里化:把接收多个参数的函数变换成接收一个单一参数的函数(单一参数为多个参数中的第一个)并且返回接受余下的参数而且返回结果的新函数,即f(a, b, c) 转换为可调用的 f(a)(b)(c)
函数柯里化思想:一个JS预处理的思想,降低通用性,提高适用性。
特点:
参数复用:需要输入多个参数,最终只需输入一个,其余通过arguments来获取提前返回:避免重复去判断某一条件是否符合,不符合则return 不再继续执行下面的操作延迟执行:避免重复的去执行程序,等真正需要结果的时候再执行
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
高级柯里化实现
function curry (fn, currArgs) {
return function() {
let args = [].slice.call(arguments);
// 首次调用时,若未提供最后一个参数currArgs,则不用进行args的拼接
if (currArgs !== undefined) {
args = args.concat(currArgs);
}
// 递归调用
if (args.length < fn.length) {
return curry(fn, args);
}
// 递归出口
return fn.apply(null, args);
}
}
或者
function currying(fn, ...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...args2) => currying(fn, ...args, ...args2);
}
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = curry(sum);
alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用
alert( curriedSum(1)(2,3) ); // 6,对第一个参数的柯里化
alert( curriedSum(1)(2)(3) ); // 6,全柯里化
柯里化使用场景
1. 参数复用
减少重复传递不变的部分参数
function uri (protocol,hostname,pathname){
return `${protocol}${hostname}${pathname}`;
}
const uri1 = uri("https://","www.site.com","/page1");
const uri2 = uri("https://","www.site.com","/page2");
console.log(uri1);
console.log(uri2);
// 转换为柯里化函数
function uri_curring (protocol){
return function(hostname,pathname){
return `${protocol}${hostname}${pathname}`;
}
}
const uri_https = uri_curring("https://");
console.log(uri_https);
const uri1 = uri_https("www.csdn.net","/page1");
const uri2 = uri_https("www.baidu.com","/page2");
console.log(uri1); // "https://www.csdn.net/page1"
console.log(uri2); // "https://www.baidu.com/page2"
2. 提前确认
//事件监听:性能上会差一点,因为如果是在低版本的IE浏览器上每一次都会运行if()语句,产生了不必要的性能开销.
var on = function(element, event, handler) {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
} else {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
}
}
//整个函数运行一次就可以了
var on = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
var on = function(isSupport, element, event, handler) {
isSupport = isSupport || document.addEventListener;
if (isSupport) {
return element.addEventListener(event, handler, false);
} else {
return element.attachEvent('on' + event, handler);
}
}
3. 延迟运行
延迟执行也是 Currying 的一个重要使用场景,同样 bind 和箭头函数也能实现同样的功能。
function hello(name) {
console.log('Hello, ' + name);
}
setTimeout(hello('dreamapple'), 3600); //立即执行,不会在3.6s后执行
setTimeout(function() {
hello('dreamapple');
}, 3600); // 3.6s 后执行
setTimeout(()=> {
hello('dreamapple');
}, 3600); // 3.6s 后执行
//bind延迟执行: bind 方法需要强制绑定 context,也就是 bind 的第一个参数会作为原函数运行时的 this 指向
setTimeout(hello.bind(this, 'dreamapple'), 3600); // 3.6s 之后执行函数
//柯里化 延迟执行
function curryingHelper(fn) {
var _args = Array.prototype.slice.call(arguments, 1);
return function() {
var _newArgs = Array.prototype.slice.call(arguments);
var _totalArgs = _args.concat(_newArgs);
return fn.apply(this, _totalArgs);
}
}
setTimeout(curryingHelper(hello, 'dreamapple'), 3600); // 3.6s 之后执行函数
经典题
确定参数的函数柯里化实现
function sum(a,b,c,d){
return a+b+c+d
}
function curry(fn){
return function sum(...args){
if(args.length<fn.length){ // 判断接受的参数是否小于函数的参数长度
return function(){ // 参数不够长度,再次接受传递参数
return sum(...args,...arguments)
}
}
return fn(...args)// 不要求改变this,
}
}
let curried = curry(sum)
console.log(curried(1)(2)(3)(4))//10
console.log(curried(1,2)(2,4)) //9
不定参数 sum(1)(2)(3)(4)(5)...(n)
function curry(fn){
let parmas = []
return function sum(...args){
if(args.length){//判断是否有参数
parmas = [...parmas,...args]
return sum
}
return fn(parmas)
}
}
function add(arr){
return arr.reduce((acc,item)=>{
return acc+item
})
}
let curried = curry(add)
console.log(curried(1)(2)(3)(4)(10,20)())//40
// 注意最后的调用用方式,()调用不传递参数,会跳出判断,调用累加函数
//在方法内部定义一个tostring方法,返回计算的值
function curry(...args){
let parmas = args
function sum(){
parmas = [...parmas,...arguments]
return sum
}
sum.toString=function(){
return parmas.reduce((acc,item)=>{
return acc+item
})
}
return sum
}
console.log(curry(1)(2)(3)(10)(10,20).toString()) // 40