JS 基础篇(七) 函数柯理化

函数柯理化

  • 定义
在计算机科学中,柯里化(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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值