防抖:
你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行!
//防抖函数:抖动停止后的时间超过设定的时间时执行一次函数--某固定时间内最多执行一次
function debounce(func, delay) {
var timeout;
//返回的函数在一个抖动结束后的delay毫秒内执行func函数
return function() {
var context = this, args = arguments; //保存函数调用时的上下文和参数
clearTimeout(timeout); //触发func前清除定时器
timeout = setTimeout(function() {
func.apply(context, args); //如果不这么做,this指向为window,参数为undefined
}, delay); //用户停止某个连续动作delay毫秒后执行func
};
}
防抖优化:
我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。因此加个 immediate 参数判断是否是立刻执行。
// 防抖优化
function debounce(func, delay, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, delay)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, delay);
}
}
}
window.addEventListener("resize", debounce(realFunc, 500));
window.addEventListener("resize", debounce(realFunc, 500,true));
节流:
每隔一段时间执行一次
使用时间戳:
使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 ),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。
// 使用时间戳
function throttle(func, wait) {
var context, args;
var previous = 0;
return function() {
var now = +new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
使用定时器
// 使用定时器
function throttle(func, wait) {
var timeout;
var previous = 0;
return function() {
context = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(function(){
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
比较两个方法:
- 使用时间戳会立刻执行,使用定时器会在 n 秒后第一次执行
- 使用时间戳停止触发后没有办法再执行事件,使用定时器停止触发后依然会再执行一次事件
双剑合璧:
我想要一个有头有尾的!就是鼠标移入能立刻执行,停止触发的时候还能再执行一次!
// 双剑合璧版
function throttle(func, wait) {
var timeout, context, args, result;
var previous = 0;
var later = function() {
previous = +new Date();
timeout = null;
func.apply(context, args)
};
var throttled = function() {
var now = +new Date();
//下次触发 func 剩余的时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果没有剩余的时间了或者你改了系统时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
//简写版
function throttle(func, delay, mustRun) {
var timeout;
var starttime = new Date(); //起始的时间
return function() {
var context = this, args = arguments;
var curtime = new Date(); //当前时间
clearTimeout(timeout);
//如果达到规定的触发时间间隔,触发func
if(curtime - starttime >= mustRun) {
func.apply(context, args);
starttime = curtime;
}
//没有达到触发时间,重新设定计时器
else {
timeout = setTimeout(func, delay);
}
}
}
在creat-react-app中封装按钮:
源码
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import {Button} from 'antd'
export default class ButtonPage extends Component {
static defaultProps = {
children: "Button",
type: "primary",
size: "default",
disabled: false,
circle: true,
icon: "",
};
static propTypes = {
children: PropTypes.string,
type: PropTypes.oneOf(["primary","danger", "normal"]),
size: PropTypes.oneOf(["default", "small", "large"]),
disabled: PropTypes.bool,
circle: PropTypes.bool,
icon: PropTypes.string,
};
// debounce已达到预期效果!
debounce (f,time) {
if(f==="") return
let timer = null
return () => {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(f.bind(this),time)
}
}
// throttle已达到预期效果!
throttle (f,time ) {
let timer = null
return () => {
if(timer) return
timer = setTimeout(() => {
f.apply(this)
timer = null
},time)
}
}
render() {
const { type, size} = this.props;
return (
<div>
<Button
type={type}
size={size}
shape="round"
onClick={this.debounce(this.props.onClick?this.props.onClick:"",2000)}
></Button>
</div>
)
}
}
使用
import Button from './components/Button/Button'
<Button type="danger" size="small" onClick={() => { console.log("1111") }}/>