【JS基础】通俗易懂的讲清楚去抖/防抖、节流。外加手写深度比较

去抖/防抖

去抖也叫防抖,为了照顾JS初学者的理解和记忆,我就简单的说明一下。

我们生活中很多出现抖动的现象,都是没有规律的,例如人的发抖、树叶在风中的抖动、海浪的摆动等。那么去抖,防抖这个概念能够见词答意的看出,就是为了抵消无规律的抖动。怎么个抵消法呢?就是在一段固定的时间内,抖动现象停止了,那么就立即发出信号。

抖动停止发出信号,抖动停止发出信号… 是不是,好像就变成了一种有规律的现象了。

好,我举个应用的例子你就更加明白了,页面上有个搜索框,用户在上面输入关键词,输入完后触发input事件,这时事件的处理逻辑就是发送异步请求去获取关键词的搜索结果。此时,用户只要一输入一个字,立马就会发送请求,当用户连续的输入时,就会连续的发送多个请求,会出现什么问题?

  • 异步请求的相应时间是无法确定的,可能第3个请求的数据量比较大返回的时间久,而最后一个请求数据量小一下子就返回了,那么最后一个请求的数据就会在第3个请求回来之前获取到,而第3个请求的结果最后才获取到,也就是说,最终显示的搜索结果变成了第3个请求的结果。蛋疼不?
  • 连续短时间内无规律的发送多个异步请求,对前端性能非常不友好(页面卡顿),对服务器也不友好(几万个用户同时这么去搜索请求服务器遭不住啊)

这时候就用到了防抖机制,我们在用户停止输入1s后,再去调用异步请求,是不是就大大减少了请求的数量。例如用户连续快速输入“广东省深圳市”,假如没有防抖机制,输入完后一共无规律的发送了6个接口,有了防抖机制,只发送了1次,就是用户输入完"市"停止1s的时候。

代码简单实现:

function debounce(fn, delay = 500) {
  let timer = null;

  return function() {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      fn(...arguments);
      timer = null;
    }, delay);
  };
}

input1.addEventListener(
  "keyup",
  debounce(function(event) {
    console.log(event.target);
    console.log(input1.value);
  }, 600)
);

下面看为什么这么写

思路解析

首先咱们要设计输入和输出,输入的话我们必定要传入需要防抖的函数本体,然后第二个参数咱们可以设置防抖的时长。输出我们就返回一个包了一层防抖机制的新函数。

function debounce(fn, delay = 500) {
  return function() {
  };
}

防抖机制的设计,只要涉及到了计数咱们只考虑setTimeOut,因为setInterVal当页面卡顿时计数也会受到影响。

function debounce(fn, delay = 500) {
  return function() {
  	let timer = setTimeout(() => {
      fn();
      timer = null;
    }, delay);
  };
}

使用的时候报错了,因为console.log(event.target)中的event为undefined。这是因为咱们的监听事件的默认参数并没有传入到fn里。所以我们要在返回的函数中,传入默认的参数。

function debounce(fn, delay = 500) {
  let timer = null;
  console.log('arguments', arguments) // 这里是debounce的默认参数
  return function() {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      fn(...arguments); // 这里是return function的默认参数,也就是keyup传进来的,可能一开始会有点难理解
      timer = null;
    }, delay);
  };
}

除了优化【查】的操作,在【增删改】的接口调用时,如果以按钮之类的形式去触发,也一定要考虑到去抖的操作,当然这种情况不一定要使用去抖的写法,用个局部loading也是不错的选择。


节流

节流,节流,节约流水(doge),哈哈,JS初学者完全可以这样子去理解。当我们去开水龙头的时候,水是不是就一直哗啦啦的流出,在一些场景下很浪费水资源,这时我们可以拧紧调节水龙头,让水滴每0.3s滴一滴,是不是就很节约流水啦。

说白了节流是当频繁触发时,保持一定的频率触发。

举例子吧,例如js中的drag、scroll、mousemove事件,就拿mousemove事件说明,当我们移动鼠标想获取实时x、y坐标处理业务逻辑的时候,mousemove事件被浏览器频繁的触发(像流水一样一发不可收拾)会有什么坏处?

  • 大量的x、y数据的处理并不是我们所需要的,多余的处理只会增加浏览器资源开销,这种情况出现多了会有浏览器卡顿的现象。

这时候,就需要保持一定的频率的获取x、y坐标,例如0.5s获取一次。这样能够大大减轻性能负担,又不失去时效性。

function throttle(fn, delay = 100) {
    let timer = null

    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments) // 把默认的event入参指向要执行的函数
            timer = null
        }, delay)
    }
}

div1.addEventListener('drag', throttle(function (e) {
    console.log(e.offsetX, e.offsetY)
}))

对比防抖可以看出,节流关注的是过程,而防抖关注的是结果。

注意!工作中尽量不要自己造轮子去使用防抖和节流,应该使用成熟的工具库,例如loadsh


两者在vue中结合计算属性使用

个人认为在vue中,把防抖和节流结合计算属性比较好用。

computed: {
	// 防抖的
	debounceBtnClick(){
		return this.debounce(this.btnClick, 300) // btnClick就是method中真正要触发的函数,在模板中的方法里绑定该计算属性即可
	},
	// 节流的未来补充
}

深度比较

就是比较两个变量是不是内容一样的,主要是考虑多层对象或者数组。

// 判断是否是对象或数组
function isObject(obj) {
    return typeof obj === 'object' && obj !== null
}
// 深度比较
function isEqual(obj1, obj2) {
    // 1 如果是值类型的话就直接比较
    if (!isObject(obj1) || !isObject(obj2)) {
        return obj1 === obj2
    }
    // 2 如果传了同一个变量进来直接返回true
    if (obj1 === obj2) {
        return true
    }
    // 3 两个都是对象或数组,而且不相等
    // a 为了性能,可以先比较属性个数或数组大小
    const obj1Keys = Object.keys(obj1)
    const obj2Keys = Object.keys(obj2)
    if (obj1Keys.length !== obj2Keys.length) {
        return false
    }
    // b 以obj1为基准,和obj2递归比较
    for (let key in obj1) {
        const res = isEqual(obj1[key], obj2[key])
        if (!res) { // 不一样的直接返回false
            return false
        }
    }
    // 相等就返回true
    return true
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值