JavaScript 中的防抖和节流(性能优化)

前言

  • 学习防抖节流前,必须非常熟悉闭包的概念
  • 防抖和节流是针对窗口、鼠标、键盘等事件,防止高频率触发,从而进行限制触发次数的手段,这并不会影响实际体验。如果无限制触发会降低浏览器性能。
  • 以下方法都是借鉴《现代javascript教程》

防抖函数 debounce(f,ms)

  • 作用:当持续触发事件时,一段固定时间(ms)内未再触发事件,才调用事件处理函数
// 直接复制所有代码去浏览器控制台即可查看效果
// 具体有什么用呢?直接上代码,假如是给滚动事件scroll加防抖
function debounce (f, ms) {
	let timeout
	return function () { //返回一个函数,涉及到了 “闭包”知识点
		clearTimeout(timeout) // 清除之前定时器
		// 重新设置定时器,将this绑定到要执行的函数f上
		// 此处的this是这个返回函数的,箭头函数没有this
		timeout = setTimeout(() => f.apply(this, arguments), ms)
	}
}
function fn () {
	console.log("已经停止滚动1000ms了!")
} 


window.addEventListener("scroll", debounce(fn, 1000))
  • addEventListener 绑定是一次性的。debounce 只在 scroll 事件回调被绑定的时候执行了一次,所以timeout 全局都只有一个,所以重新调用时得清除timeout上的定时器。回调函数是闭包,所以能访问到timeout
  • arguments此处并未用到,但实际应用防抖时可能涉及函数有参数,此处为完整写法

节流函数 thtottle(f,ms)

  • 作用:当持续触发事件时,让固定时间内只调用一次事件处理函数
  • 查了些许博客,里面有些节流实现会导致事件不会立即触发或最后一次函数调用不被触发。什么意思呢?
    • 第一种情况:本该在 0s 的时候就先立即(第一次)触发一次函数,但是有些方法会使得到达 5s 时才触发第一次
    • 第二种情况:就比如每 5s 触发一次函数在控制台输出 “1”,假设实际不间断滚动了 13s,控制台输出了两次 “1”,但第三次没有输出。而 13s 是包含在 15s 里的,虽然停止了滚动,但过 2s 后理应要再输出一次,不能因为滚动还没到5s而不给出反馈
// 直接复制所有代码去浏览器控制台即可查看效果
// 该方法解决了上述问题
function throttle (f, ms) {
	let isThrottled = false, // 判断是否进行节流
		args, // 用于保存参数
		that; // 用于保存this(上下文)
		
	function fn () {
		if (isThrottled) { // T2
			args = arguments
			that = this
			return
		}
		// 这个语句无返回值,但会执行函数f
		f.apply(this, arguments) // T1
		
		isThrottled = true
		
		setTimeout (function () { // T3
			isThrottled = false
			if (args) { 
				// 调用fn
				fn.apply(that, args)
				args = that =null
			}
		},ms)
	}
	return fn // 闭包
}

function func () {
	console.log(Math.random()) // 在控制台每次打印一个随机数,方便理解
} 

// throttlo在绑定时已执行,且isThrottled为假,返回了fn函数(并未执行)
// 每次scroll事件都调用的是fn函数
window.addEventListener("scroll", throttle(func, 1000))
  • 假设在页面只滚动了一瞬间,还没到1000ms(但此时已经触发了很多次滚动事件

    • 首先执行第一次滚动事件(fn函数),此时isThrottled为假,T2步骤跳过,直接执行T1步骤(func函数被调用,控制台瞬间打印第一次结果),且令isThrottled为真,这样,事件就立即触发了
    • 而定时器(T3)得经过1000ms时间才执行,放一边,先执行第二次滚动事件。此时isThrottled为真,执行T2步骤(必须执行了T2步骤,T3步骤才能完整执行,args为真)
    • 执行了许多次滚动事件后,到时间了(1000ms),执行第一次事件的定时器(T3,),令isThrottled为假,且调用了fn函数,fn函数又调用了func,但此时args为空,本次fn中的定时器变无效了,同时由于其他定时器共享args变量(闭包),其他定时器也无效了,显然,定时器使得最后一次函数调用被执行
    • 留个思考:滚动时间超过1000ms,定时器又是如何启动?
  • 节流实现比较复杂,初次接触容易感到它反复横跳,摸不着头脑,得捋清思路,多思考

差异

  • 举个例子:泡澡的时候,水凉了,才加热水,是防抖;不管水温如果,反正每过5分钟就加热水,是节流

你可能感兴趣的:(js基础,js,javascript)