JS高阶函数 | 青训营笔记

178 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第9天

1.高阶函数Once

  • 高阶函数:以函数作为参数或者以函数作为返回值的函数称为高阶函数

    • 同时满足两个条件的函数常用于作为函数装饰器
  • function once(fn) {
      return function(...args) {
        if(fn) {
          const ret = fn.apply(this, args);
          fn = null;
          return ret;
        }
      }
    }
    
    • once函数接收到的参数是一个fn函数,返回的是一个新的函数
    • 在返回的函数中,只让fn执行一次,第二次执行的时候fn已经被赋值为null,就无法再次被执行了
  • const foo = once(() => {
      console.log('bar');
    });
    ​
    foo();
    foo();
    foo();
    ​
    ​
    button.addEventListener('click', once((evt) => { 
        const target = evt.target; 
        target.parentNode.className = 'completed'; 
        setTimeout(() => { 
            list.removeChild(target.parentNode); 
        }, 2000); 
    }));
    
  • 这样,我们就将一次执行这个过程抽象出来了,任何需要一次执行的函数,只要在外面包一层once就可以实现,我们可以把once成为函数装饰器。

扩展:

  • JS数组的哪些API是高阶函数:every、map、filter、forEach、reduce、sort

2.高阶函数HOF

  • HOF

    • 以函数作为参数
    • 以函数作为返回值
    • 常用于作为函数装饰器
     function HOF0(fn) {
        return function(...args) {
          return fn.apply(this, args);
        }
      }
    
  • fnHOF0(fn) 是完全等价的,无论参数、调用上下文怎么变化,他们都是等价的! 也就是说,执行fn与执行HOF0(fn)没有任何区别

  • 可以看出我们的Once函数就是在HOF0上进行拓展出来的

3.高阶函数Throttle

  • 可以定义节流函数

    function throttle(fn, time = 500){
      let timer;
      return function(...args){
        if(timer == null){
          fn.apply(this,  args);
          timer = setTimeout(() => {
            timer = null;
          }, time)
        }
      }
    }
    ​
    //效果,连续多次点击只会每500ms记录一次
    btn.onclick = throttle(function(e){
      circle.innerHTML = parseInt(circle.innerHTML) + 1;
      circle.className = 'fade';
      setTimeout(() => circle.className = '', 250);
    });
    

4.高阶函数debounce

  • 定义防抖函数

    function debounce(fn, dur){
      dur = dur || 100;
      var timer;
      return function(){
        clearTimeout(timer);
        timer = setTimeout(() => {
          fn.apply(this, arguments);
        }, dur);
      }
    }
    

5.consumer函数

  • function consumer(fn, time){
      let tasks = [],
          timer;
      
      return function(...args){
        tasks.push(fn.bind(this, ...args));
        if(timer == null){
          timer = setInterval(() => {
            tasks.shift().call(this)
            if(tasks.length <= 0){
              clearInterval(timer);
              timer = null;
            }
          }, time)
        }
      }
    }
    
    • 相当于将同步操作变成一个异步的操作
  • 用法1 逐步累加

    function add(ref, x){
      const v = ref.value + x;
      console.log(`${ref.value} + ${x} = ${v}`);
      ref.value = v;
      return ref;
    }
    ​
    //相当于是consumer化的add,这样就可以实现类似于异步的效果,相隔一秒执行一次add
    let consumerAdd = consumer(add, 1000);
    ​
    const ref = {value: 0};
    for(let i = 0; i < 10; i++){
      consumerAdd(ref, i);
    }
    
  • 用法2 连击异步增加(快速点击慢慢执行)

    btn.onclick = consumer((evt)=>{
      let t = parseInt(count.innerHTML.slice(1)) + 1;
      count.innerHTML = `+${t}`;
      count.className = 'hit';
      let r = t * 7 % 256,
          g = t * 17 % 128,
          b = t * 31 % 128;
      
      count.style.color = `rgb(${r},${g},${b})`.trim();
      setTimeout(()=>{
        count.className = 'hide';
      }, 500);
    }, 800)
    

6.iterative函数

  • function iterative(fn) {
      return function(subject, ...rest) {
        if(isIterable(subject)) {
          const ret = [];
          for(let obj of subject) {
            ret.push(fn.apply(this, [obj, ...rest]));
          }
          return ret;
        }
        return fn.apply(this, [subject, ...rest]);
      }
    }