最近在面试,被问到防抖和节流的问题,虽然平时也有用电梯函数这种延时执行的代码,但是被问到概念的时候还是有点懵逼,所以赶紧来学习一下,分享一下学习成果,顺便把自己写的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={{ '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>
)
}
}
参考文档 防抖节流