如何实现一个节流函数?

<!DOCTYPE html>
<html lang="zh-cmn-Hans">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
    <title>debounce</title>
    <style>
        #container {
            width: 100%;
            height: 200px;
            line-height: 200px;
            text-align: center;
            color: #fff;
            background-color: #444;
            font-size: 30px;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <script src="./节流函数.js"></script>
</body>

</html>

// 节流函数.js
var count = 1;
var container = document.getElementById('container');

function getUserAction(e) {
    console.log(this) // this 应该指向div 
    console.log(e)    // this 不应该打印undefined 
    container.innerHTML = count++;
};

// container.onmousemove = throttle1(getUserAction, 500);
// container.onmousemove = throttle2(getUserAction, 500);
container.onmousemove = throttle3(getUserAction, 500);

防抖和节流的作用都是防止函数多次调用。

区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

  1. 防抖 触发高频事件后 n 秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
  2. 节流 高频事件触发,但在 n
    秒内只会执行一次,本来10秒钟执行100次,变为10秒钟执行10次,所以节流会稀释函数的执行频率,每次触发事件时都判断当前是否有等待执行的延时函数

防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

第一版 使用时间戳

第一种方法:使用时间戳

  • 当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 )
  • 如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳
  • 如果小于,就不执行。
function throttle1(func, wait) {
    var context, args;
    var previous = 0;

    return function () {
        // 设置当前时间戳
        var now = +new Date();
        // 解决this 指向问题
        context = this;
        // 解决event 对象打印undefined 问题
        args = arguments;
        if (now - previous > wait) {
            // apply 函数改变this 指向div
            func.apply(context, args);
            previous = now;
        }
    }
}

第二版 使用定时器

第二种实现方式,使用定时器。

  • 当触发事件的时候,我们设置一个定时器,再触发事件的时候
  • 如果定时器存在,就不执行
  • 直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。
function throttle2(func, wait) {
    var timeout;

    return function () {
        // 解决this 指向问题
        context = this;
        // 解决event 对象打印undefined 问题
        args = arguments;
        if (!timeout) {
            // timeout 定时器标识。等待wait 时间后清空定时器,执行函数
            timeout = setTimeout(function () {
                // 清空定时器
                timeout = null;
                // apply 函数改变this 指向div
                func.apply(context, args)
            }, wait)
        }
    }
}

比较两个方法:

  • 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
  • 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件 第

三版 双剑合璧

那我们想要一个什么样的呢?

有人就说了:我想要一个有头有尾的!就是鼠标移入能立刻执行,停止触发的时候还能再执行一次!

function throttle3(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;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值