彻底理解前端工程优化难点之防抖和节流

防抖和节流

在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove, 滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。 我们看个例子:

<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1div>
<script>
    var xxx = document.getElementById('xxx');
    xxx.onmousemove = test
    function test() {
      	this.innerHTML = parseInt(this.innerHTML) + 1;
    }
script>

我们可以看到,上面的代码,当鼠标在div之上移动的时候,会大量的触发事件,上面的代码比较简单,还比较好说。如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。

针对此类快速连续触发和不可控的高频触发问题,debouncethrottle 给出了两种解决策略

防抖(debounce)

触发高频事件后n毫秒内函数只会执行一次,如果n毫秒内高频事件再次被触发,则重新等待n毫秒。也就是说,两个事件之间的等待时间可能是n毫秒,也可能是多个n毫秒。

基本思想是:首次运行时把定时器赋值给一个变量,第二次执行时,如果间隔没超过定时器设定的时间则会清除掉定时器,重新设定定时器,依次反复,当我们停止下来时,没有执行清除定时器,超过一定时间后触发回调函数。

<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1div>
<script>
    var xxx = document.getElementById('xxx');
    xxx.onmousemove = debounce(test, 1000);

    function test() {
      	this.innerHTML = parseInt(this.innerHTML) + 1;
    }
  	/**
  	 * 一个防止抖动的函数(非立即执行版),会在第一次触发后等待一段事件再开始执行
  	 * func 事件绑定的打算防抖的函数
  	 * wait 防抖等待的时间
  	 */
    function debounce(func, wait) {
      // 定时器保存变量
        var clock = null;
        return function () {
            if (clock) {
              // 如果不是第一次触发,且上一次触发事件还没有执行完
              // 就清掉定时器,在后面重新计时
              clearTimeout(clock);
            }
            // 防止this指向发生改变
            var obj = this;
            // 设置定时器并开始计时
            clock = setTimeout(function () {
              // 事件触发后,且符合防抖策略,会在触发后等待一定时间执行
              func.call(obj);
              // 执行完事件,重置定时器保存变量
              clock = null;
            }, wait);
      	}
    }
script>
<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1div>
<script>
    var xxx = document.getElementById('xxx');
    xxx.onmousemove = debounce(test, 1000);

    function test() {
      	this.innerHTML = parseInt(this.innerHTML) + 1;
    }
  	// 立即执行版防抖函数
    function debounce(func, wait) {
        // 定时器保存变量
        var clock = null;
        return function () {
            // 防止this指向发生改变
            var obj = this;
            if (clock) {
              	// 事件触发的时候,如果设置的有定时器,则清掉定时器,后面再重新设置
              	clearTimeout(clock);
            }else{
                // 如果是第一次或者在等待时间外,则直接执行事件处理函数
                func.call(obj);
            }
            // 重新设置定时器
            clock = setTimeout(function () {
              	clock = null;
            }, wait);
        }
    }
script>

节流(throttle)

指连续触发事件,但是在 n毫秒内只执行一次函数。也就是说,两个事件之间的等待时间是n毫秒。

<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1div>
<script>
    var xxx = document.getElementById('xxx');
    xxx.onmousemove = throttle(test, 1000);

    function test() {
      	this.innerHTML = parseInt(this.innerHTML) + 1;
    }
		// 定时器版节流函数
    function throttle(func, wait){
        var clock = null;
        return function () {
            var obj = this;
            // 如果首次触发或者已经延时完毕
            if(clock == null){
                clock = setTimeout(function () {
                    clock = null;
                    func.call(obj);
                }, wait);
            }
        }
    }
script>

你可能感兴趣的:(前端优化)