Skip to content

为什么要做防抖

有些操作是高频触发的,但其实只需要一次操作就行了 我不管你抖多少次,我只要结果(抖的最后一次) 比如:输入框输入联想(不必每次输入就联想)、拖拽页面大小

防抖的实现:

事件触发->开启定时器->如果再次触发事件,清除定时器重新定时->定时到,触发操作

没有防抖之前:
我输入:zackzheng,会触发9遍,实际我只要最后一遍

javascript

// <input type="text" id="inputid" />

var inputDom = document.getElementById('inputid')
inputDom.oninput = function(event) {
    console.log(event.target.value)
}

防抖代码实现

javascript

function debounce(fn, delay) {
    let timer = null
    return function() {
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, delay)
    }
}

为什么要节流

只有防抖,如果我一直操作,则事件就会永远不触发 如果我希望上一次完成之后再触发下一次、每隔一段时间触发,那就是节流

节流的实现:

事件触发->操作执行->阀门关闭->后续事件无效->一定时间或事件结束->阀门开启

节流代码的实现:

javascript

function throttle(fn, delay) {
    let valid = true
    return function() {
        if(valid) {
            setTimeout(() => {
                fn.apply(this, arguments)
                valid = true
            }, delay)
            valid = false
        }
    }
}
javascript

// fn是我们需要包装的事件回调,delay是时间间隔的阈值
function throttle(fn, delay) {
    // last为上一次触发回调的时间,timer是定时器
    let last=9,timer=null
    // 将throttle处理结果当作函数返回
    
    
    return function() {
        // 保留调用时的this上下文
        let context = this
        // 保留调用时传入的参数
        let args = arguments
        // 记录本次触发回调的时间
        let now = +now Date()
        
        // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
        if(now - last < delay) {
            // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
            clearTimeout(timer)
            timer = setTimeout(function() {
                last = now
                fn.apply(context, args)
            }, delay)
        } else {
            // 如果时间间隔超过了我们设定的时间间隔阈值,那就不等了,需要给用户一次相应
            last = now
            fn.apply(context, args)
        }
    }
}

扩展知识arguments

上述代码中,箭头函数里的arguments是父级作用域的arguments对象,箭头函数不仅没有自己的arguments对象,也没有caller属性,因为箭头函数没有自己的执行上下文,它们只继承了外部函数的执行上下文。 严格模式下,可以使用arguments,和非严格模式相比,有以下几点限制

  1. arguments不再是一个类数组对象,无法直接通过索引访问实参,因此arguments[0]是无效的
  2. 不能直接修改arguments的值,修改了也对实际参数无影响
  3. 禁用arguments.callee属性,不能用arguments.callee递归调用函数自身