这是我参与更文挑战的第3天,活动详情查看: 更文挑战
紧跟上一篇 ,这一篇主要介绍函数柯里化,及函数柯里化的实际应用
什么是函数柯里化?
- 百度百科:在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。通俗点说就是将一个函数拆分成多个函数,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数。
- 为什么会有函数柯里化?
- Currying 的重要意义在于可以把函数完全变成「接受一个参数;返回一个值」的固定形式,这样对于讨论和优化会更加方便。
- 将关注的重点聚焦到函数本身,而不因冗余的数据参数分散注意力。
- 当函数可以作为函数的参数和返回值,成为函数式编程语言后,就会不可避免地产生函数柯里化。
函数柯里化的好处有哪些?
- 参数复用
// 以封装正则校验为例
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
- 提前确认
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);
}
}
我们在做项目的过程中,封装一些dom操作可以说再常见不过,上面第一种写法也是比较常见,
但是我们看看第二种写法,它相对一第一种写法就是自执行然后返回一个新的函数,
这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。
- 延迟运行
- 最经典的是bind函数
Function.prototype.bind = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}
- 提高适用性
//通用函数解决了兼容性的问题,但是同时在不同场景下,我们可能同一种规则需要反复使用
//这就可能会造成代码的重复性 比如
function square(i){ return i*i }//平方
function dubble(i){ return i*2 }//双倍
function map(handler,list){
//handle 是操作的规则 list是操作的arrguments
return list.map(handler)
}
map(square,[1,2,3]);//数组每一项平方
map(dubble,[1,2,3]);//数组每一项加倍
//这就是通用性 我可以用同一个函数做很多不同的操作
//但是如果我们需要大量做平方操作 每次我们都需要传入方法再传入数组就造成的代码浪费
//这时我们通过柯里化提高实用性
let mapSQ = currying(map,square);//直接定义出来的新的平凡操作函数
mapSQ([1,2,3]);//以后就不用传入操作方法了
函数柯里化的通用封装方法
// 支持多参数传递
function fnCurrying(fn, args) {
var _this = this
var len = fn.length;
var args = args || [];
return function() {
var _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return fnCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
函数柯里化的性能问题
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上