闭包实战题目学习

66 阅读2分钟

遇到了一道很有意思的题目:

完成如下操作: 首先打印“咚”,隔200ms打印“哒”,再隔150ms打印“哒”,再隔100ms打印“咚” 循环三次,每次循环间隔300ms

初看这道题 想法是使用定时器,但要注意不能直接使用循环 里面放定时器,因为这样由于setTimeout是宏任务,循环三次将三个定时器放入了宏任务队列,其实近似于三个回调函数同时执行,为了确保执行的顺序,每个打印任务都应该在任务队列中排列好,首先使用setTimeout来实现

function dong(){
  console.log("咚")
}

function da() {
  console.log("哒")
}

function handle() {
  let count = 0;
  const totalLoops = 3;
  
  function loop() {
      if (count >= totalLoops) return;
      
      dong();
      
      setTimeout(() => {
          da();
          
          setTimeout(() => {
              da();
              
              setTimeout(() => {
                  dong();
                  count++;
                  setTimeout(() => {
                      loop();
                  },300);
              },100)
          },150)
      }, 200)
  }
  
  loop();
}

handle();

这里使用到了闭包来记录循环的次数,并且逐级嵌套,通过将任务都放入宏任务队列中来完成功能的实现。那自然想到能不能用微任务来实现,这里不考虑await/async的使用,使用promise实现


function handle() {
  let count = 0;
  const totalLoops = 3;

  function loop() {
    if (count >= totalLoops) return;

    dong();

    delay(200)
      .then(() => {
        da();
        return delay(150);
      })
      .then(() => {
        da();
        return delay(100);
      })
      .then(() => {
        dong();
        count++;
        if (count < totalLoops) {
          return delay(300).then(loop); // 循环间隔 300ms
        }
      });
  }

  loop();
}

handle();

这段代码主要是利用了Promise.then()的回调函数放在微任务队列中的原理,这些方法放置到了微任务队列中。

上面的都用到了闭包的原理,下面再复习一下防抖与节流

  • 防抖

在事件触发后执行,如果delay秒内函数再次被执行,那么重新计时,适合高频点击下只执行最后一次、搜索框联想输入

    function debounce(fn,delay){
        let timer = null;
        
        return function (...args){
            if(timer){
                clearTimeout(timer);
                timer = null;
            }else{
                fn.apply(this,args);
            }
            
            timer = setTimeout(() => {
                timer = null;
            },delay)
        }
    }
  • 节流

执行函数n秒之后才重新执行函数,适合固定时间内只执行一次,防止超高频次触发位置变动,比如监控浏览器resize

    function throttle(fn,delay){
        let flag = true;
        
        return function (...args){
            if(!flag) return;
            
            flag = false;
            fn.apply(fn,delay)
            setTimeout(() => {
                flag = true;
            },delay)
        }
    }