js函数节流和函数防抖的封装、使用以及应用场景

函数节流和函数防抖算是性能优化的范畴,说起来是可用可不用的东西,但是,作为一个有追求的前端,当然是容忍不了的~~。

一、函数节流

函数节流,就是一定周期内最多只执行一次。
形象点说,就像公交车行驶,以车子行驶作为触发事件,以乘客上车作为执行事件,公交会分成很多个站点,控制乘客只能在各个站点上车,不能中途上。

1、封装

(函数节流的封装其实网上各种版本都有,有些版本明显是有问题的(例如没有立即执行),所以建议各位还是花时间去理解一下封装的原理和细节,这样才能知其然并知其所以然。)

/**
 * 函数节流
 * @param {function} fn 函数
 * @param {number} t 间隔时间(毫秒)
 * @return {function}
 */
function throttle (fn, t) {
  let flag = true
  let interval = t || 500
  return function () {
    let args = arguments
    if (flag) {
      fn.apply(this, args)
      flag = false
      setTimeout(() => {
        flag = true
      }, interval)
    }
  }
}

2、使用方式

无论是函数节流还是函数防抖,都是利用函数闭包的原理共用同一函数作用域进行封装的,而函数是每执行一次就会生成一个新的函数作用域,所以使用时需格外注意,封装的节流或防抖方法是用来对我们真正要执行的函数做一层包装的,而且要只包装一次,保证包装后的函数能共用一个作用域。
例如:

import throttle from './throttle'

// 定义我们要执行的方法
const myFn = () => {
	console.log('Neo')
}

// 对myFn做一次节流包装,生成了一个父级作用域以及使用了该作用域变量的函数throttledFn
const throttledFn = throttle(myFn, 1000)

// 应用节流,正确方式:使用throttledFn
setInterval(() => {
	throttledFn()
}, 10)

// 应用节流,错误示例:
setInterval(() => {
	throttle(myFn, 1000) // 这样使用每次都会生成一个独立的父级作用域,无法节流
}, 10)

vue中的使用技巧:

import throttle from './throttle'

export default {
	computed: {
		// 利用computed具有缓存的特性,对methods里的方法做节流包装,也能保证只会包装一次
		throttledFn () {
			return throttle(this.myFn, 1000)
		}
	},
	methods: {
		myFn () {
			console.log('Neo')
		}
	},
	mounted () {
		// 使用示例
		setInterval(() => {
			this.throttledFn()
		}, 10)
	}
}

或者直接写在methods里:

methods: {
	myFn () {
		console.log('Neo')
	},
	// 关键点:不要再使用function包装throttle
	throttledFn: throttle(function () {
		this.myFn()
	}, 1000)
},

3、应用场景

(1) 页面滚动事件

  • 例如判断页面滚动位置来控制返回顶部按钮的显示隐藏,就可以在滚动事件里做持续的节流处理。

(2) hybrid app 里h5跳转app原生页事件

  • 这里的跳转事件如果不做节流处理,可能会出现快速双击多次会打开多个相同的原生页面,我的做法就是节流一秒,即一秒内只能触发一次跳转事件,一秒内页面差不多已经跳转过去了,基本能满足需求。

二、函数防抖

函数防抖,就是停下来一定时间后再执行。
形象点说,就像公交车行驶,以乘客上车作为触发事件,以车子行驶作为执行事件,司机在站点会停下来等乘客上车,在不再有乘客上车后才会发动车子行驶。

1、封装

/**
 * 函数防抖
 * @param {function} fn 函数
 * @param {number} t 等待时间(毫秒)
 * @return {function}
 */
function debounce (fn, t) {
  let timeId
  let delay = t || 500
  return function () {
    let args = arguments
    if (timeId) {
      clearTimeout(timeId)
    }
    timeId = setTimeout(() => {
      timeId = null
      fn.apply(this, args)
    }, delay)
  }
}

2、使用方式

(略,同函数节流)

3、应用场景

(1) 典型的应用场景:搜索词联想

  • 比如在搜索框里输入小米,会请求接口获取小米这个词的联想词,小米手机、小米笔记本、小米10什么什么的,如果不做防抖处理,直接监听input事件就请求接口,那这里就会同时请求了“小”和“小米”两个搜索词的接口,其实在你正在输入时是不需要实时请求接口的,这时候用防抖处理,在用户停止输入一秒后再请求接口,能大大减少不必要的请求,降低服务器压力。

三、综合运用

  • 比如要做个鼠标移动盒子时让盒子跟随鼠标一起移动的需求,这个会有什么问题呢?
  • 首先移动事件mousemove触发频率很高,需要做节流处理,但是如果鼠标瞬间移动,而且这个移动时间间隔小于节流时间间隔,就会出现最终盒子没在鼠标位置的bug
  • 这时候我们其实只是需要一个兜底,就是保证mousemove事件的最后一次触发时会执行我们的处理函数,怎么解决?
  • 其实只需要节流+防抖一起上,并设置防抖的时间间隔大于节流的时间间隔,节流提升性能,防抖保证兜底,就ok了。

你可能感兴趣的:(js)