【源码共读】跟着underscore学防抖

109 阅读2分钟

- 1防抖

定义:事件触发时候,对应函数并不会立即执行,而是会等待规定时间后再执行,如果在规定单位时间内又被触发,这之前触发的所有事件被取消,并重新计时

应用场景:窗口浏览器resize、scroll,按钮提交、表单输入框等

  1. 基本实现 防抖节流,我们是传入一个函数,后执行,所以我们要内部创建一个函数并返回,在返回函数进行我们相关操作
<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>
        var count = 1;
        var container = document.getElementById('container');

        function getUserAction() {
            console.log(this, event)
            container.innerHTML = count++;
        };

        // container.onmousemove = getUserAction;
        container.onmousemove = debounce(getUserAction, 200);
        function debounce(func, wait) {
            let timer = null;
            return  _debunce = () => {
                if (timer) clearTimeout(timer);
                timer = setTimeout(() => {
                    func();
                }, wait);
                
            }
        }
    </script>
</body>
</html>

2.this、arguments完善 上一版只是实现功能,但是打印this,指向window(因为func()独立函数调用,默认绑定就是window,我们可以通过call,aplly显式指向调用对象,而且事件参数有一个默认event,同时可能还有其他参数,我们可以使用es5-arguments或者使用es6剩余参数都可以

image.png

function debounce(func, wait) {
            let timer = null;
            return  _debunce = function (...arg)  {
                if (timer) clearTimeout(timer);
                timer = setTimeout(() => {
                    func.apply(this, arg);
                }, wait);

            }
        }

image.png 现在this和event都正确了 3.增加立即执行和取消功能 立即执行功能了网上版本

function debounce(func, wait, immediate = false) {
            let timer = null;
            let isInvoke = false;

            const _debunce = function (...arg) {

                if (timer) clearTimeout(timer);
                if (immediate && !isInvoke) {
                    func.apply(this, arg);
                    isInvoke = true;
                };
                timer = setTimeout(() => {
                    func.apply(this, arg);
                    isInvoke = false;
                }, wait);

            }


            _debunce.cancel = function () {
                if (timer) clearTimeout(timer);
            }

            return _debunce

        }

4.返回结果功能

const debounceFn = debounce(getUserAction, 1000, true, res => {
            console.log(res)
        });
        container.onmousemove = debounceFn;
        function debounce(func, wait, immediate = false, cb) {
            let timer = null;
            let isInvoke = false;

            const _debunce = function (...arg) {

                if (timer) clearTimeout(timer);
                if (immediate && !isInvoke) {
                    const result = func.apply(this, arg);
                    isInvoke = true;
                    if (cb) cb(result)
                };
                timer = setTimeout(() => {
                    const result = func.apply(this, arg);
                    isInvoke = false;
                    if (cb) cb(result)
                }, wait);

            }


            _debunce.cancel = function () {
                if (timer) clearTimeout(timer);
            }

            return _debunce

        }

2.节流

定义:就像水龙头没全部关住,一点点在滴水或者射击游戏技能,不管你单位时间按下多少次,炮弹只会发一次

<!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>
    <button id="btn">取消</button>
    <script>
        var count = 1;
        var container = document.getElementById('container');

        function getUserAction(event) {
            container.innerHTML = count++;
            return container.innerHTML
        };

        // container.onmousemove = getUserAction;
        container.onmousemove = throttle(getUserAction, 1000);
        function throttle(fn, interval) {
            let lastTime = 0;
            const _throttle = function(...args) {
                const nowTime = Date.now();
                const remainTime = interval - (nowTime - lastTime);
                if (remainTime <= 0) {
                    fn.apply(this, args);
                    lastTime = nowTime;
                }
            }
            return _throttle;
        }
    </script>
</body>

</html>