js高级函数 柯里化 去柯里化 偏函数 节流 防抖 分时 惰性加载

面向切面编程AOP

/**
 * 面向切面编程 AOP
 * 应用场景:把一些跟核心业务逻辑模块无关的功能抽离出来,包括日志统计、安全控制、异常处理等
 * 实现方式:把这些功能抽离出来之后,再通过“动态织入”的方式掺入业务逻辑模块中。
 * 优点:这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。
 * @param beforefn
 * @returns {function(): *}
 */
Function.prototype.before = function( beforefn ){
  let fn = this; // 保存原函数的引用
  return function(){ // 返回包含了原函数和新函数的"代理"函数
    beforefn.apply( fn , arguments ); // 执行新函数,修正 this 
    return fn.apply( fn , arguments ); // 执行原函数
  }
};
Function.prototype.after = function( afterfn ){
  let fn = this;
  return function(){
    let result = fn.apply( fn , arguments );
    afterfn.apply( fn , arguments );
    return result;
  }
};
//测试用例
let func = function(name){
  console.log( 2,name );
};
func = func.before(function(arg){
  console.log( 1,arg );
}).after(function(arg){
  console.log( 3,arg );
});
func('aop');
// 输出
// 1 'aop'
// 2 'aop'
// 3 'aop'

函数柯里化

/**
 * 函数柯里化
 * 每次调用都将参数存起来,最后需要使用的时候才求值
 * @param fn
 * @returns {Function}
 */
let currying = function (fn) {
  let args = [];
  return function () {
    if (arguments.length === 0) {
      return fn.apply(this, args);
    } else {
      args.push(...arguments);
      return arguments.callee;
    }
  }
};
//测试用例
let costFn = (function () {
  let money = 0;
  return function () {
    for (let i = 0, l = arguments.length; i < l; i++) {
      money += arguments[i];
    }
    return money;
  }
})();
let cost = currying(costFn); // 转化成 currying 函数
cost(100); // 未真正求值
cost(200); // 未真正求值
cost(300); // 未真正求值
console.log(cost());
// 输出
// 600


//方法2
function sum(...params){
  const cache=this._cache || (this._cache=[])
  if(params.length){
    this._cache=[...cache,...params]
    return this
  }else{
    return cache.reduce((total,item)=>total+item,0)
  }
}
sum(1,2)
sum(3,4)
console.log(sum())



去柯里化

/**
 * 去柯里化
 * 让一个对象去借用原本不属于他的方法
 * @returns {function(): any}
 */
Function.prototype.uncurrying = function () {
  let self = this;
  return function () {
  	//不能用箭头函数,因为箭头函数没有this和arguments
  	//结合测试用例,apply 相当于 self.call(obj,2)
    return Function.prototype.call.apply(self, arguments);
  }
};
//测试用例
let push = Array.prototype.push.uncurrying();
let obj = {
  "length": 1,
  "0": 1
};
push(obj, 2);
console.log(obj); 
// 输出
// {0: 1, 1: 2, length: 2}

偏函数

/**
 * 偏函数
 * 局部应用(偏函数)是指固定一个函数的一些参数,然后产生另一个更小元的函数。
 * 元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数。
 * @param fn
 * @param arg
 */
function partial(fn,...argFix){
  //argFix可用undefined占位
  return function(...argCurr){
    let arg=[],idx=0;
    for(let i=0;i<argFix.length;i++){
      if(argFix[i]===undefined){
        arg[i]=argCurr[idx++];
      }else{
        arg[i]=argFix[i];
      }
    }
    //将剩余的参数拼接到一起
    arg=arg.concat(argCurr.slice(idx));
    return fn(...arg);
  }
}

//测试用例
function add(a,b,c,d,e){
  return a*b+c*d+e;
}
let partial_add=partial(add,1,undefined,2,undefined);
console.log(partial_add(3,4,5));
//1*3+2*4+5 = 16

函数节流

/**
 * 函数节流
 * 应用场景:函数可能被频繁调用影响性能,但事实上不需要频繁响应
 * 实现方式:定时器,500ms内只执行一次
 * 优点:减少性能消耗
 * @param fn
 * @param interval
 * @returns {Function}
 */
let throttle = function (fn, interval) {
  let timer, // 定时器
    firstTime = true; // 是否是第一次调用
  return function () {
    let args = arguments,
      __self = this;
    if (firstTime) { // 如果是第一次调用,不需延迟执行
      fn.apply(__self, args);
      return firstTime = false;
    }
    if (timer) { // 如果定时器还在,说明前一次延迟执行还没有完成
      return false;
    }
    timer = setTimeout(function () { // 延迟一段时间执行
      clearTimeout(timer);
      timer = null;
      fn.apply(__self, args);
    }, interval || 500);
  };
};
//测试用例
window.onresize = throttle(function () {
  console.log(1);
}, 500);

函数防抖

/**
 * 防抖
 * 持续触发事件不执行,不触发事件一段时间之后再执行一次
 * @param func
 * @param delay
 * @returns {Function}
 */
function debounce(fn, delay) {
  let timer;
  return function () {
    clearTimeout(timer); // 如果持续触发,那么就清除定时器,定时器的回调就不会执行。
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay || 500)
  }
}
//测试用例
box.onmousemove = debounce(function (e) {
  box.innerHTML = `${e.clientX}, ${e.clientY}`
}, 1000);

分时函数

/**
 * 分时函数
 * 应用场景:大量执行耗费性能高的操作,容易导致页面卡顿奔溃
 * 实现方式:定时器分批执行
 * 优点:不卡顿,不假死,不奔溃
 * @param arr
 * @param fn
 * @param count
 * @param interval
 * @returns {Function}
 */
let timeChunk = function (arr, fn, count, interval) {
  let timer;
  let start = function () {
    for (let i = 0; i < Math.min(count || 20, arr.length); i++) {
      let obj = arr.shift();
      fn(obj);
    }
  };
  return function () {
    timer = setInterval(function () {
      if (arr.length === 0) { // 如果全部节点都已经被创建好
        return clearInterval(timer);
      }
      start();
    }, interval || 200); // 分批执行的时间间隔
  };
};

//测试用例
let arr = [];
for (let i = 1; i <= 1000; i++) {
  arr.push(i);
}
let renderFriendList = timeChunk(arr, function (n) {
  let div = document.createElement('div');
  div.innerHTML = n;
  document.body.appendChild(div);
}, 8);
renderFriendList();

惰性加载

/**
 * 惰性加载
 * 应用场景:调用函数前要做一些嗅探工作,只需要执行一次,且执行的时候才需要判断
 * 实现方式:第一次执行的时候嗅探,然后返回嗅探后需要执行的分支代码替换本函数
 * 优点:不再每次做if判断,且第一次执行才判断,减少计算次数,节约性能
 * @param elem
 * @param type
 * @param handler
 */
let addEvent = function( elem, type, handler ){
  if ( window.addEventListener ){
    addEvent = function( elem, type, handler ){
      elem.addEventListener( type, handler, false );
    }
  }else{//老IE
    addEvent = function( elem, type, handler ){
      elem.attachEvent( 'on' + type, handler );
    }
  }
  addEvent( elem, type, handler );
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值