事件委托、闭包、节流、防抖

转载自 几个问题

事件委托

  • Walk the dog
  • Pay bills
  • Make dinner
  • Code for one hour

做一下操作来将事件绑定到元素

document.addEventListener('DOMContentLoaded', function() {
  let app = document.getElementById('todo-app');
  let items = document.getElementByClassName('item');

  for(let item of items) {
    item.addEventListener('click', function () {
      alert('you clicked on item: ' + item.innerHtml)
    })
  }
})

如果item有1000个,那就要用事件委托
下面是事件委托的代码

document.addEventListener('DOMContetentLoaded', function() {
  let app = document,getElementById('too-app');
  app.addEventListener('click', function (e) {
    if (e.target && e.target.nodeName === "LI") {
      let item = e.target;
      alert("you clicked on item:" + item.innerHTML)
    }
  })
})

在循环中使用闭包

闭包基本上是内部函数可以访问其范围之外的变量。

编写一个函数,该函数将遍历整数列表,并在延迟3秒后打印每个元素的索引。

const arr = [10,12,15,21];
arr.map((v,i)=> {
  setTimeout(function() {
    console.log(i)
  }, 3000)
})

使用for循环 - var

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
    setTimeout(function (i) {
      return function () {
        console.log(i)
      }
     }(i), 3000)
 }

使用for循环 - let

const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
    setTimeout(function () {
        console.log(i)
     }, 3000)
 }

事件的节流和防抖

节流

如果咱们不让点击事件一个时间段内重复触发,就可以用节流, 就是控制频率

定时器函数实现:

如果第一次执行,直接放行, 如果存在定时器就退出函数, 否则就执行定时器

  const throttle = (fn,intervel) {
    let isFirst = true,
    timer = null;
    return function () {
      let arg = arguments, _me = this;
      if (isFirst) {
        fn.apply(_me, args);
        isFirst = false;
      }
      if (timer) {
        return false
      }
     timer =  setTimout(() => {
         window.clearTimeout(timer);
         timer = null;
         fn.apply(_me, args);
      },intervel)
    }
  }

利用时间差, 若这一次执行的时间 减去 上一次的执行时间 大于某个时间

function throttle(fn, interval) {
  let last = 0;
  return function () {
    let _me = this;
    let args = arguments;
    let now = +new Date();
    if (now - last >= interval) {
      last = now;
      fn.apply(_me, args);
    }
  }
}

防抖

防止手抖点击多次只有最后一次生效

function debounce(fn, delay) {
  let timer = null;
  return function () {
    let _me = this;
    let args = arguments;
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(function () {
      fn.apply(_me, args)
    }, delay)
  }
}

用throttle来优化Debounce

如果用户操作的频率十分频繁,不等debounce设置的delay时间结束就进行下一次操作,于是每一次debounce都会为该用户生成新的计时器,回调函数被延迟了不计无数次。频繁的延迟会导致用户迟迟得不到响应。

所以我们借力throttle的思想优化debounce, ---- delay时间内,我可以为你重新生成定时器,但只要delay时间到了,我必须要给用户一个响应。

// fn是我们需要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {
  // last为上一次触发回调的时间, timer是定时器
  let last = 0, timer = null
  // 将throttle处理结果当作函数返回
  
  return function () { 
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()
    
    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last < delay) {
    // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
        last = now
        fn.apply(context, args)
    }
  }
}

// 用新的throttle包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)

document.addEventListener('scroll', better_scroll)

你可能感兴趣的:(事件委托、闭包、节流、防抖)