函数柯理化
- 定义
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成
接受一个单一参数(最初函数的第一个参数)的函数,
并且返回接受余下的参数且返回结果的新函数的技术
- 个人理解:
sum(1,2,3) <=> currySum(1)(2)(3) //
并且一下方式也可以
currySum(1)
currySum(2)(3)
currySum(1)(3)
- 初级return 嵌套
function sum(a,b,c){
return (a + b + c)
}
function currySum(a,b,c){
return function(b){
return function(c){
return a+b+c
}
}
}
- 递归版本
如果我们有n个参数[...a,b,c,d,e]等等。
我们就要无限的return里面return 函数。
这个时候用到了递归
code
let add = (a,b,c) => a + b + c
function curry(fn,args){
let _this = this;
let _args = args || [];
let len = fn.length;
return function(){
//将参数伪数组 转化为一个数组,arguments 为参数数组
let args = Array.prototype.slice.apply(arguments)
args = Array.prototype.concat.call(_args,args)//合并数组
if(args.length < len){
// return curry(fn,args) // 直接调用自己 也可以
return curry.call(_this,fn,args) //call可以传递 多个参数
}
// console.log(args); // 整合了 所有传参 在args
return fn.apply(this,args) //注意这里 没有用call,为什么???
//因为需要传递一个数组吖
}
}
let currySum = curry(add)
console.log(currySum(1)(2)(3));
这样的话呢,我们就可以写成currySum(1)(2)(3) 这种形式
存在问题
以下方式是无法输出的
currySum(1)
currySum(1,2)(3)
最终版本
代码
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function () {
_args.push(...arguments);
console.log('111');
return _adder;
};
// console.log(_adder);
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
console.log('222'); //在浏览器输出 会打印两次
return _args.reduce(function (a, b) {
return a + b;
});
};
return _adder;
}
console.log(add(1)(2)(3) instanceof Function);
console.log(add(1)(2)(3));
console.log(add(1, 2, 3)(4));
console.log(add(1)(2)(3,4)(4)(5,6));
console.log(add(1)(2)(3,4)(4)(5,6).toString());//解释
这样就可以无限输出了
代码是赋值来的 链接在文末
解释:
toString是挂载在 func _adder下面的属性,可以在func的constructor下面查看到,还有func自带的length,name等属性。所以如果不加toString的话 得到的是 f num,toString之后得到方法的返回值。
关于 222 会输出两次的问题,个人用node 运行js 是没有被输出的,浏览器打开会被浏览器运行打印两次。这个地方小作者我也不是很懂,希望有幸被大神读到,指点一下,不胜感激。
柯理化应用
这里没有个人原创,来自各种大佬
函数复用
- 正则复用
柯理化前
function reg(reg,str){
return reg.test(str)
}
console.log(reg(/\d+/g,'kk'));
console.log(reg(/[a-z]+/g,'kk'));
柯理化后
//柯理化函数
function curryReg(reg){
return function(str){
return reg.test(str)
}
}
let cRegNumber = curryReg(/[0-9]+/g)
let cRegString = curryReg(/[a-z]+/g)
console.log(cRegNumber(1314));
console.log(cRegString('kk'))
总结
项目中封装可复用函数,就不用每次都传入一次正则,在公共的地方规定一次就好,对于后期的维护也非常方便
- 提前确认
由于浏览器的兼容性,一些操作可能需要不同的初始化api
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)
}
}
}
})()
const btn = document.getElementById('btn')
on(btn,'click',function(){ //调用
alert('PianistK')
})
- 延迟运行
说到 延迟 一定能想到 bind 只是预先绑定,这里有些柯理化的意思
Function.prototype.mybind = function(obj,para1,para2){
const _this = this;
function inner(para1,para2){ //这是一个闭包 obj在bind的时候 绑定到了inner
// return _this.call(obj)
//等同于 上面call的实现原理
let fnName = _this.name
obj[fnName] = _this;
const result = obj[fnName](para1,para2);
delete obj[fnName];
return result;
}
inner.prototype = Object.create(_this.prototype) //修正原型链
return inner;
}
let mybindFn = getName.bind(obj)
console.log(mybindFn('from','bind')
参考资料
链接: 函数柯理化.
全力以赴PianistK