居然不会函数节流跟防抖,来看这篇包学包会

前言

前端开发中会遇到一些频繁的事件触发,好比:window的scroll、resize;mousedown、mousemove,keyup、keydown等等,假如你对本身的代码不作什么的处理,你会发现页面卡顿、触发接口请求频繁等问题,本文将浅析函数节流跟防抖实现,一步一步逐渐揭开函数节流跟防抖的真面目💜javascript

概念

理解防抖跟节流触发原理,根据不一样使用场景合理使用前端

函数防抖(debounce)

当调用动做过n毫秒后,才会执行该动做,若在这n毫秒内又调用此动做则将从新计算执行时间,不会执行
复制代码

理解原理:
尽管触发事件,可是必定在事件触发 n 秒后才执行,若是在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,就是要等触发完事件 n 秒内再也不触发事件,才会执行!java

函数节流(throttle)

预先设定一个执行周期,当调用动做的时刻大于等于执行周期则执行该动做,而后进入下一个新周期
复制代码

理解原理:
规定时间内,保证执行一次该函数react

实现

防抖

根据防抖原理,实现代码以下,以后的示例都将是常规使用:
git

# 箭头log函数
let count = 0
const log = () => {
    console.log(this)
    ++count
    console.log(count)
}
# 函数表达式
const log = function (evt) {
    console.log(this)
    console.log(evt)
    ++count
    console.log(count)
}
复制代码
const debounce = function (fn, delay){
    let timeout = null
    return function () {
        if (timeout) {
            clearTimeout(timeout)
        }
        timeout = setTimeout(fn, delay)
    }
}
复制代码

使用频繁click事件为例
常规使用: meContain.onclick = debounce(log, 1000)
react demo: ...onClick={debounce(log.bind(this), 1000)} github

经过例子你会发现,1s以内频繁click,都会在最后输出一次log,验证了防抖原理

小伙伴们有没有发现此时的防抖函数仍存在缺陷segmentfault

  • this指向和event 对象
  • 假如用户如今一直点击提交按钮的话,就会一直不发出请求,也得不到任何提示,对用户体验至关很差

this指向和event 对象

this指向
bash

  • log函数中 console.log(this),不使用 debounce 函数的时候,this 的值为:undefined, 这是由于使用了箭头函数,此时须要onClick调用的时候bind(this), this指向react组件示例
  • 常规使用中console.log(this),不使用 debounce 函数的时候,this 的值为: <div id="mecontain"></div>

event 对象markdown

  • 不使用 debouce 函数,会打印 ClickEvent 对象
  • debounce 函数中,却只会打印 undefined

解决以上问题,来更改咱们的代码app

const debounce = function (fn, delay){
    let timeout = null
    return function () {
        const context = this;
        const args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(() => {
            fn.apply(context, args)
        }, delay)
    }
}
复制代码

交互优化体验

若是但愿马上执行函数一次,不用等到事件中止触发后才执行,而后等到中止触发 n 秒后,再能够从新触发执行
经过添加isImmeDiate来判断是否马上执行

const debounce = function (fn, delay,isImmeDiate= false){
    let timeout = null
    return function () {
        const context = this;
        const args = arguments;
        if(timeout) clearTimeout(timeout)
        if(isImmeDiate) {
            # 判断是否已经执行过,不要重复执行
            let callNow = !timeout
            timeout = setTimeout(function(){
                timeout = null;
             }, delay)
            if(callNow)  result = fn.apply(context, args)
        } else {
            timeout = setTimeout(() => {
                fn.apply(context, args)
            }, delay)
        }
        return result
    }
}
复制代码

若是要添加一个取消debounce开关,只须要添加一个cancle函数清除定时器timeout = null

const debounce = function (fn, delay,isImmeDiate= false){
    let timeout = null
    const debounced =  function () {
        const context = this;
        const args = arguments;
        if(timeout) clearTimeout(timeout)
        if(isImmeDiate) {
            # 判断是否已经执行过,不要重复执行
            # setTimeout也是一直在更新的
            let callNow = !timeout
            timeout = setTimeout(function(){
                timeout = null;
             }, delay)
            if(callNow)  result = fn.apply(context, args)
        } else {
            timeout = setTimeout(() => {
                fn.apply(context, args)
            }, delay)
        }
        return result
    }
    debounced.prototype.cancle = function() {
        clearTimeout(timeout)
        timeout = null
    }
    return debounced
}
复制代码

到这里咱们实现了一个防抖函数,可是小伙伴们有没有别的想法呢?

节流

根据节流原理,实现代码以下,以后的示例都将是常规使用:

时间戳实现

const throttle = function (fn, delay) {
    let preTime = 0;
    return function () {
        const context = this;
        const args = arguments;
        const now = +new Date();
        if (now - preTime > delay) {
            fn.apply(context, args);
            preTime = now;
        }
    }
}
复制代码

定时器实现

const throttle = function (fn, delay) {
    let timeout = null
    return function () {
        const context = this;
        const args = arguments;
        if (!timeout) {
            timeout = setTimeout(function(){
                timeout = null
                fn.apply(context, args)
            }, delay)
        }
    }
}
# 若是须要马上执行,其实变动下执行顺序便可
timeout = setTimeout(function(){
    timeout = null
    //fn.apply(context, args)
}, delay)
fn.apply(context, args)
复制代码

一样使用频繁click事件为例 常规使用: meContain.onclick = throttle(log, 1000)

经过例子你会发现,频繁click,都会在根据你设定的周期输出一次log,验证了节流原理

小伙伴们有没有发现此时的节流函数存在的特色

  • 时间戳会马上执行,定时器会在 n 秒后第一次执行
  • 时间戳中止触发后没有办法再执行事件,定时器实现中止触发后依然会再执行一次事件

合并二者特色

const throttle = function (fn, delay) {
    let timeout = null
    let preTime = 0;
    const later = function() {
        preTime = +new Date()
        timeout = null
        fn.apply(context, args);
    }
    const throttled = function () {
        const context = this;
        const args = arguments;
        const now = +new Date();
        #下次触发fn剩余的时间
        const remaining = delay - ( now - preTime)
        #若是没有剩余的时间了或者系统时间变动
        if (remaining <= 0 || remaining > delay) {
            if(timeout) {
                clearTimeout(timeout)
                timeout = null
            }
            preTime = now
           fn.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(later, remaining)
        }
    }
    throttled.cancel = function() {
        clearTimeout(timeout);
        previous = 0;
        timeout = null;
    }
    return throttled
}
复制代码

总结

节流,在规定时间内,保证执行一次该函数;防抖,当持续触发事件时,必定时间段内没有再触发事件,事件处理函数才会执行一次,若是设定的时间到来以前,又一次触发了事件,就从新开始延时

文档

浅谈函数防抖
函数节流
函数防抖
函数节流

自我推荐

深刻源码了解Vue错误处理
Webpack DllPlugin让构建速度柔顺丝滑

相关文章
相关标签/搜索