Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

阅读 lodash 源码学节流 #7

Open
huitoutunao opened this issue Jun 27, 2021 · 0 comments
Open

阅读 lodash 源码学节流 #7

huitoutunao opened this issue Jun 27, 2021 · 0 comments
Labels

Comments

@huitoutunao
Copy link
Owner

阅读 lodash 源码学节流

前言

上一篇文章我们已经学习了防抖的实现原理,如果还没有学习的同学可以戳这里学习。今天我们一起来学习节流的实现原理。

介绍

如果你在这 1s 内连续触发事件,那么只执行一次。(1s 为自定义间隔时间)

这里有首次是否执行和结束后是否执行两种效果,而它们的实现方式也各有不同。

实现

好,看完上面对节流的概述,我们可以简单实现如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
    <title>throttle</title>
    <style>
        #wrapper {
            width: 100%;
            height: 200px;
            line-height: 200px;
            text-align: center;
            font-size: 30px;
            color: #fff;
            background-color: #666;
        }
    </style>
</head>
<body>
    <div id="wrapper"></div>
    <script src="throttle.js"></script>
</body>
</html>
// throttle.js

let count = 1
let wrapperDom = document.getElementById('wrapper')

function doEvent (e) {
    wrapperDom.innerHTML = count++
}

function throttle (func, wait) {
    let timerId
    let lastInvokeTime = 0

    function throttled (...args) {
        const lastThis = this
        const lastArgs = args

        if (!timerId) {
            timerId = setTimeout(function () {
                timerId = null
                func.apply(lastThis, lastArgs)
            }, wait)
        }
    }

    return throttled
}

wrapperDom.onmousemove = throttle(doEvent, 1500)

运行效果图如下:

throttle1

从效果图可以看出,鼠标首次触发事件是在 1.5s 后,就是说它不是立即触发事件的,而且在鼠标移出区域 1.5s 后,即结束后执行了一次事件。

我现在想让它首次执行事件该如何修改代码呢?见下方实现:

function throttle (func, wait) {
    let lastInvokeTime = 0
    
    function throttled (...args) {
        const lastThis = this
        const lastArgs = args
        const time = Date.now()

        if (time - lastInvokeTime > wait) {
            func.apply(lastThis, lastArgs)
            lastInvokeTime = time
        }
    }

    return throttled
}

运行效果图如下:

throttle2

从该效果图可以看出,它与上面介绍的有两点不同:1、首次执行事件;2、结束后没有执行事件。

优化

现在将上面两种效果结合,且外部可以通过传递参数控制首次或结束是否执行事件。我们定下规则:

  1. leading: false 表示禁用在节流开始前执行。
  2. trailing: false 表示禁用在节流结束后执行。
function throttle (func, wait, options) {
    let timerId,
        lastThis,
        lastArgs,
        result

    let lastInvokeTime = 0
    let leading = true
    let trailing = true

    if (options) {
        leading = 'leading' in options ? !!options.leading : leading
        trailing = 'trailing' in options ? !!options.trailing : trailing
    }

    function throttled (...args) {
        const time = Date.now()

        lastThis = this
        lastArgs = args

        if (!lastInvokeTime && !leading) {
            lastInvokeTime = time
        }

        const remainingWait = wait - (time - lastInvokeTime)

        // 如果没有剩余的时间了或者你改了系统时间
        if (remainingWait <= 0 || remainingWait > wait) {
            if (timerId) {
                clearTimeout(timerId)
                timerId = null
            }

            lastInvokeTime = time
            result = func.apply(lastThis, lastArgs)

            if (!timerId) {
                lastThis = lastArgs = null
            }
        } else if (!timerId && trailing) {
            // leading 和 trailing 的值不允许同时为 false
            timerId = setTimeout(invokeFunc, remainingWait)
        }

        return result
    }

    function invokeFunc () {
        lastInvokeTime = leading ? 0 : Date.now()
        timerId = null
        result = func.apply(lastThis, lastArgs)

        if (!timerId) {
            lastThis = lastArgs = null
        }
    }

    // 取消
    function cancel () {
        lastInvokeTime = 0
        timerId = null
        clearTimeout(timeout)
    }

    throttled.cancel = cancel

    return throttled
}

wrapperDom.onmousemove = throttle(doEvent, 1500, {
    trailing: false
})
// wrapperDom.onmousemove = throttle(doEvent, 1500, {
//     leading: false
// })

运行效果图如下:

throttle3

结语

本文到这里就结束了,通过文章我们了解到什么是节流以及它的实现原理,使用节流函数可以解决项目中,懒加载要监听计算滚动条的位置,按一定时间的频率获取。

在前端面试中,节流函数还是一道高频考题,希望小伙伴们看完本文后能够顺利拿下。

参考文献

@huitoutunao huitoutunao added the 专题系列 JS专题 label Jun 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant