防抖(debounce)和节流(throttle) 通俗说明 + 示例

最近在面试,被问到防抖和节流的问题,虽然平时也有用电梯函数这种延时执行的代码,但是被问到概念的时候还是有点懵逼,所以赶紧来学习一下,分享一下学习成果,顺便把自己写的demo贴到下面,感兴趣的同学可以直接复制下面代码作为一个react组件引入到代码中,打开不同的注释掉的函数,看看效果,体验一下就明白了。

防抖(debounce)和节流(throttle)

首先明确一点防抖(debounce) 和 节流(throttle) 都是对函数执行的控制,因为某些事件会频繁的触发,例如 scroll mouseMove resize 等...

  • 防抖:是固定延时后执行,例如延时1s后执行一次,每次触发事件都会重置这个延时,防抖处理过的函数会推迟执行,假如你一直触发事件,防抖处理过的函数就一直不执行。
  • 节流:是固定时间执行一次,例如固定1秒执行一次,每次调用函数不会推迟执行,假如你一直触发事件,节流处理过的函数还是会1s执行一次。

this 的指向问题 (延伸)

  • 箭头函数不绑定this,没有argument参数,它使用的this是作用域继承来的this
  • es5函数的this,指向函数的调用者
  • call, apply, bind 可以改变this的指向
  • argument参数是从执行的函数的作用域中获取的

例子

  • 下面这个例子可以copy到react执行环境中查看效果,同时this的指向问题,也可以在代码中提现
  • 可以新建一个js文件把当前代码复制到xx.js 文件中,在其他地方引用该组件体验
import React, { Component } from 'react'

/**
 *
 * 防抖:防抖是指限制访问次数,在n秒内执行一次,但是如果在n秒内再次触发的话,会等待n秒再次执行
 * 节流:节流是指限制访问频率,是每n秒触发一次 
 * @param {*} fn 回调函数
 * @param {*} time 触发间隔
 */

function debounce(fn, time) {
    let timer;
    return function () {
        const [x,y] = [...arguments];
        if (timer) {
            clearTimeout(timer)
        }
        timer =  setTimeout(fn.bind(this, x,y), time);  
    }
}


/**
 * 立即执行的防抖函数
 *
 * @param {*} fn 回调函数
 * @param {*} time 触发间隔
 * @returns
 */
function rightNowDebounce(fn,time) {
    let timer;
    return function () {
        const [x,y] = [...arguments];
        if (timer) {
            clearTimeout(timer)
        } else {
            fn.apply(this, [x,y]);
        }
        timer =  setTimeout(fn.bind(this,x,y), time);  
    }
}

/**
 *
 * 时间差形式的防抖 (实现形式一)
 * @param {*} fn 回调函数
 * @param {*} durition 触发间隔
 * @returns
 */
function durationThrottle(fn, durition) {
    let startTime;
    return function () {
        const [x,y] = [...arguments]
        if (startTime) {
            let cur = new Date().getTime();
            if (cur - startTime > durition) {
                startTime = cur;
                fn.apply(this, [x,y])
            }
        } else {
            startTime = new Date().getTime();
        }
    }
}

/**
 * 定时器形式的节流 (实现方式二)
 *
 * @param {*} fn 回调函数
 * @param {*} timer 触发间隔
 */
function timerThrottle(fn, duration) {
    let timer
    return function () {
        const args = [...arguments]
        console.log('args', args)
        if (!timer) {
            timer = setTimeout(function() {
                fn.apply(this, args);
                timer = null;
            }.bind(this), duration);
        }
    }
}

/**
 *
 * 会立即执行的节流函数
 * @param {*} fn
 * @param {*} durition
 * @returns
 */

function rightNowThrottle(fn, durition) {
    let startTime;
    return function () {
        const [x,y] = [...arguments]
        if (startTime) {
            let cur = new Date().getTime();
            if (cur - startTime > durition) {
                startTime = cur;
                fn.apply(this,[x,y])
            }
        } else {
            fn.apply(this,[x,y])
            startTime = new Date().getTime();
        }
    }
}

const fn1 = function () {
    const [x,y] = [...arguments]
    this.setState(function name({number}) {
        return {number: number+1, positionX:x, positionY:y} 
    })
} 

const definedDebounce = debounce(fn1, 1000)
const definedRightNowDebounce = rightNowDebounce(fn1, 1000)
const definedThrottle = durationThrottle(fn1, 1000)
const definedRightNowThrottle = rightNowThrottle(fn1, 1000)
const definedTimerThrottle = timerThrottle(fn1, 1000)

export default class Debous extends Component {
    state = {
        number: 0,
        positionX: 0,
        positionY: 0,
    }

    hoverContaienr = (e) => {
        const { clientX, clientY } = e
         definedDebounce.apply(this, [clientX, clientY])
        // definedRightNowDebounce.apply(this, [clientX, clientY])
        // definedThrottle.apply(this, [clientX, clientY])
        // definedRightNowThrottle.apply(this, [clientX, clientY])
        // definedTimerThrottle.apply(this, [clientX, clientY])

        
    }

    render() {
        return (
            <div 
                style={{ width: '100%', height:300, background:'pink', fontSize:'36px',textAlign:'center' }}
                onMouseMove={this.hoverContaienr}
            >
                <div style={{position:'absolute', top: '50%', left:'50%', transform:'translate(-50%, -50%)'}}>
                    {this.state.number}
                    <span style={{fontSize:16, color: '#333'}}>({`${this.state.positionX},${this.state.positionY}`})</span>
                </div>
            </div>
        )
    }
}

参考文档 防抖节流

posted @ 2021-01-09 20:22  熊猫呵呵哒  阅读(681)  评论(0编辑  收藏  举报