防抖和节流

163 阅读2分钟

防抖( debounce)

将用户的操作行为触发转换为程序行为触发,防止用户操作的结果抖动。一段时间内,事件在我们规定的间隔 n 秒内多次执行,回调只会执行一次

特点:等待某种操作停止后,加以间隔进行操作

  • 持续触发不执行
  • 不触发的一段时间之后再执行
应用场景
  • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
<!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>
</head>

<body>
    <input type="text">


    <script>
        let inputa = document.querySelector('input')

        function output(val) {
            if(val!==''){
                console.log('输出了' + val);
            }
        }

    // 添加防抖,闭包
    function deboince(fn,delay){
        return function(value){
            // let _this = this //全局对象
            // let _value = value //==input.value
            //清除定时器
            clearTimeout(fn.id)
            //开启定时器
            fn.id = setTimeout(function(){
                //call,改变了this的指向
                fn.call(this,value)
                // console.log(33,_this);
            },delay)
        }
    }
   let deboinceFn = deboince(output,1000) //

        inputa.addEventListener('input',function(e){
            // console.log(e.target.value);
            deboinceFn(e.target.value)
        })
    </script>
</body>
</html>

call

fn.call:当前实例(函数fn)通过[原型链]的查找机制,找到function.prototype上的call方法,function call(){[native code]} 把找到的call方法执行
call方法执行的时候,内部处理了一些事情
1.首先把要操作的函数中的this关键字变为call方法第一个传递的实参
2.把call方法第二个及之后的实参获取到
3.把要操作的函数执行,并且把第二个以后传递进来的实参传递给函数 fn.call(this,params) 如果不传参数,或者第一个参数是nullnudefinedthis都指向window

节流(throttle)

  • 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
应用场景
  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
<!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>
</head>

<body>
    <input type="text">


    <script>
        let inputa = document.querySelector('input')

        function output(val) {
            if (val !== '') {
                console.log('输出了' , val);
            }
        }

        // 节流
        function throttle(fun, delay) {
        let last, deferTimer
        return function (args) {
            let _this = this//全局对象window
           //arguments:类数组对象
            let argument = arguments
            // console.log(argument);

            let now = +new Date() //当前时间戳

            if (last && now < last + delay) {
                clearTimeout(deferTimer)
                deferTimer = setTimeout(function () {
                    last = now
                     /*
          apply把需要传递给fn的参数放到一个数组(或者类数组)中传递进去,
          虽然写的是一个数组,但是也相当于给fn一个个的传递
                  */
                    fun.apply(_this, argument)
                }, delay)
            }else {
                last = now
                fun.apply(_this,argument)
            }
        }
    }
        let deboinceFn = throttle(output, 1000) //
        inputa.addEventListener('input', function (e) {
            // console.log(e.target.value);
            deboinceFn(e.target.value)
        })
    </script>
</body>
</html>

apply

和call基本上一致,唯一区别在于传参方式

apply把需要传递给fn的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn一个个的传递

//fn.apply(obj,数组/类数组)
    fn.apply(obj, [1, 2]);

类数组对象:arguments

  • 在js中万物皆对象,甚至数组字符串函数都是对象。所以这个叫做arguments的东西也是个对象,而且是一个特殊的对象,它的属性名是按照传入参数的序列来的,第1个参数的属性名是’0’,第2个参数的属性名是’1’,以此类推,并且它还有length属性,存储的是当前传入函数参数的个数,很多时候我们把这种对象叫做类数组对象
function fn(){
    console.log(arguments);
}
fn(2,4,6,8,1)

image.png