手写防抖节流函数

防抖

  1. 每次都检查timeout是否有内容,如果有就clearTimeout,然后生成一个新的timeout,这个新的timeout的回调函数是这个传入的func,等待时间为wait
  2. 改变debouned函数的作用域到正确(this和arguments指向)
  3. let context = this; let args = arguments;
  4. func.apply(context, args);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #container {
            width: 100%;
            height: 200px;
            line-height: 200px;
            text-align: center;
            color: #fff;
            background-color: #444;
            background-size: 30px;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <button id="btn">取消防抖</button>
    <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
    <script>
    </script>
    <!-- <script src="./debounce.js"></script> -->
    <script src="./throttle.js"></script>
</body>

</html>
//防抖:
//当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
//应用场景:
//1.scroll事件滚动触发
//2.搜索框输入查询
//3.表单验证
//4.按钮提交事件
//5.浏览器窗口缩放,resize事件

function debounce(func, wait, immediate) {
    //优化,如果doSomeThing有return值,此时可以用result来接收防抖优化后的doSomeThing的返回值
    let timeout, result;
    //函数也是对象 debounced对象
    let debounced = function() {
        //此处的this,为debounced的this,也就是doSOme()的this,即是container
        //但是func(也就是doSomeThing())的this却为window
        //func.apply(context, args);使得doSomeTing的作用域也为container
        let context = this;
        //改变event的指向问题,从doSomeTing接收的event为underfind到doSome接收的event为onmousemove事件
        //此处的arguments,为debounced的arguments,也就是doSOme()的arguments,即是onmousemove事件
        //但是func(也就是doSomeThing())的arguments却为undefined
        let args = arguments;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            //1.当首次激发事件,timout为null,所以callNow为true可以立即执行
            //2.当首次激发事件,但是没有到达wait时间,再次激发事件,此时timeout有值,
            //所以callNow为false,不能立即执行,完成防抖操作
            //3.当长时间没有再次激发事件,就将timeout=null,方便下次可以再次立即执行
            let callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait);
            //立即执行
            if (callNow) result = func.apply(context, args);
        } else {
            timeout = setTimeout(function() {
                func.apply(context, args);
            }, wait);
        }
        return result;
    }
    debounced.cancel = function() {
        clearTimeout(timeout);

        timeout = null;
    }
    return debounced;
}


let count = 0;
let container = document.querySelector('#container');
let btn = document.querySelector('#btn');

function doSomeTing(e) {
    //可能会做回调或者ajax请求
    container.innerHTML = count++;
    //e:underfind
    console.log(e);
    return '想要的东西'
}

let doSome = debounce(doSomeTing, 300, true);
btn.onclick = function() {
    //取消防抖操作
    doSome.cancel();
}

container.onmousemove = doSome;

节流

//节流
//原理:控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次
//应用场景:
//1.DOM元素的拖拽功能实现
//2.射击游戏类
//3.计算鼠标移动的距离
//4.监听scroll滚动事件

function throttle(func, wait, options) {
    let context, args, timeout;
    let old = 0;
    //如果用户不传options
    if (!options) options = {};
    let later = function() {
        old = new Date().valueOf();
        timeout = null; 
        func.apply(context, args);
    }
    return function() {
        context = this;
        agrs = arguments;
        //每一次被运行时,都会获得当下的时刻
        let now = new Date().valueOf();
        if (options.leading === false && !old) {
        	//更新old
            old = now;
        }
        if (now - old > wait) {
            //第一次执行 ,最后一次不执行
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            func.apply(context, args);
            old = now;
        }// 当触发事件时候,会设置定时器,此时定时器timeout一直存在,无法进入该else if,
        //如果再次触发,且wait延迟达到,此时将timeout=null,将再次进入else if
        else if (!timeout && options.trailing !== false) {
            //第一次不执行,最后一次还会执行
            timeout = setTimeout(later, wait);
        }
    }
}


let count = 0;
let container = document.querySelector('#container');
let btn = document.querySelector('#btn');

function doSomeTing(e) {
    //可能会做回调或者ajax请求
    container.innerHTML = count++;
    //e:underfind
    console.log(e);
    return '想要的东西'
}

let doSome = throttle(doSomeTing, 5000, {
    leading: true,
    trailing: true
});


container.onmousemove = doSome;

另一种节流方案

throttle是一个闭包,其返回的函数是这个闭包的入口,而pre是这个闭包隐藏的局部变量,是一个私有变量,而返回函数的执行过程中,会改变pre的值,而当第一次onmousemove 完成后,第二次再次执行onmousemove 事件,想要pre回到最开始状态(pre = null),此时下一次又可以按原始状态进行了。

else里面实现:相当于执行的是最后一次,功能就是将定时器清空,然后过一个interval后将pre回到最开始状态(pre = null),其中还可以实现最后一次计数func.apply(context, args),或者取消最后一次计数,不执行func.apply(context, args),这里按实际需求来编写代码。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值