JavaScript 函数柯里化
一、定义:
柯里化(Currying)是把承受多个参数的函数变换成承受一个繁多参数 (最后函数的第一个参数) 的函数,并且返回承受余下的参数且返回后果的新函数的技术。
通过一个简略的例子解释一下:
function add(a, b) {return a + b}
add(1, 2); // 3
将函数 add 转化为柯里化函数_add:
function _add(a){return function(b){return a + b}
}
_add(1)(2); // 3
函数 add 和函数 _add 是等价的。
_add可能解决 add 的所有残余参数,因而 柯里化 也被称为 局部求值。
实际上就是把 add 函数的 a,b 两个参数变成了先用一个函数接管 a 而后返回一个函数去解决 b 参数。
当初思路应该就比拟清晰了:只传递给函数一部分参数来调用,让它返回一个函数去解决剩下的参数。
二、柯里化函数的作用
1、参数复用
案例:拼接地址
依照一般思路去拼接一个地址
// 拼接地址
function getUrl(protocol, hostname, pathname) {return `${protocol}${hostname}${pathname}`;
}
const url1 = getUrl('https://', 'www.baidu.com', '/hasa');
const url2 = getUrl('https://', 'www.baidu.com', '/saandsa');
const url3 = getUrl('https://', 'www.baidu.com', '/hasak');
console.log(url1, url2, url3)
每次调用 getUrl 参数的时候都要反复的传入参数 ’https://’, ‘www.baidu.com’。
柯里化封装之后:
function curry(protocol) {return function (hostname, pathname) {return `${protocol}${hostname}${pathname}`;
}
}
const url_curry = curry('https://');
const url1 = url_curry('www.baidu.com', '/hasa');
const url2 = url_curry('www.baidu.com', '/saandsa');
const url3 = url_curry('www.baidu.com', '/hasak');
console.log(url1, url2, url3)
很显著,通过柯里化封装之后,之后再进行地址拼接的时候,缩小了参数个数,升高了代码反复率。
2、提前确认
案例:兼容 IE 浏览器事件的监听办法(IE is dead)
传统的办法:
/*
* @param element Object DOM 元素对象
* @param type String 事件类型
* @param listener Function 事件处理函数
* @param useCapture Boolean 是否捕捉
*/
var addEvent = function (element, type, listener, useCapture) {if (window.addEventListener) {element.addEventListener(type, function (e) {listener.call(element, e)
}, useCapture)
} else {element.attachEvent('on' + type, function (e) {listener.call(element, e)
})
}
}
缺点就是,每次对 DOM 元素进行事件绑定时都须要从新进行判断,其实对于事件监听网页一公布浏览器曾经确定了,就能够通晓浏览器到底是须要哪一种监听形式。所以咱们能够让判断只执行一次。
柯里化封装之后:
var curEvent = (function () {if (window.addEventListener) {return function (element, type, listener, useCapture) { // return funtion
element.addEventListener(type, function () {listener.call(element)
}, useCapture)
}
} else {return function (element, type, listener) {element.attachEvent('on' + type, function () {listener.call(element)
})
}
}
})
var addEvent = curEvent();
addEvent(element, "click", listener)
立刻执行函数,在触发屡次事件也仍旧只会触发一次 if 条件判断。
3、提早执行
案例:钓鱼统计分量
传统的办法:
let fishWeight = 0;
const addWeight = function(weight) {fishWeight += weight;};
addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(2.5);
console.log(fishWeight);
每次执行 addWeight 办法时,都做了一次鱼的体重的加和。
柯里化封装后:
function curryWeight(fn) {let _fishWeight = [];
return function () {if (arguments.length === 0) {return fn.apply(null, _fishWeight);
} else {_fishWeight = _fishWeight.concat([...arguments]);
}
}
}
function addWeight() {
let fishWeight = 0;
for (let i = 0, len = arguments.length; i < len; i++) {fishWeight += arguments[i];
}
return fishWeight;
}
const _addWeight = curryWeight(addWeight);
_addWeight(6.5);
_addWeight(1.2);
_addWeight(2.3);
_addWeight(2.5);
console.log(_addWeight())
在执行 _addWeight 办法时,并没有做鱼的体重的加和,之后在最初一次执行 _addWeight() 时,才做了加和。做到了提早执行 addWeight 办法的成果。
三、柯里化函数的实现
function curry() {let args = [...arguments];
let inner = function () {args.push(...arguments);
return inner;
}
// 核心内容:隐式转换,调用了外部的 toString
inner.toString = function () {return args.reduce(function (pre, next) {return pre + next;})
}
return inner;
}
const result = curry(1)(2);
console.log(Number(result));
四、柯里化总结
函数的柯里化,须要依赖参数以及递归,通过拆分参数的形式,来调用一个多参数的函数办法,以达到缩小代码冗余,减少可读性的目标。
性能方面:
- 存取 arguments 对象通常要比存取命名参数要慢一点;
- 一些老版本的浏览器在 arguments.length 的实现上是相当慢的;
- 应用 fn.apply(…) 和 fn.call(…)通常比间接调用 fn(…) 略微慢点;
- 创立大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上。
利用场景:
- 缩小反复传递不变的局部参数;
- 将柯里化后的 callback 参数传递给其余函数。