JavaScript 防抖、节流

之前开发的时候遇到过防抖、节流这些问题,当时也就是解决了,谁知道他们的名字叫做防抖、节流呢,现在来做个总结吧。

防抖:

防抖原理就是:使用setTimeout来辅助实现,延迟运行需要执行的代码。如果方法多次触发,就把上次记录的延迟执行代码清掉,重新开始计时。若计时器件事件没有被重新触发,等延迟时间计时完毕,则执行目标代码。

如果没有防抖的话,我们举个demo:

// 首先一个container
<div id='container' style="width: 500px; height: 100px;background-color: grey; text-align: center;"></div>


// 关于js的操作也就是鼠标移入count++
var count = 1;
var container = document.getElementById('container');

function getUserAction(e) {
    container.innerHTML = count++;
};

container.onmousemove = getUserAction

实现的结果就是疯狂的触发这个事件,这肯定是不好的,会造成性能问题,页面卡顿之类。关于解决:就是使用setTimeOut来设置一个定时器。来保证触发事件n秒内不再触发事件。

版本一:
var count = 1;
var container = document.getElementById('container');

function getUserAction() {
    container.innerHTML = count++;
    console.log('3', this)
};
function debounce(func, wait) {
	var timer
	console.log('1',this)
	return function() {
		console.log('2',this)
		clearTimeout(timer)
		timer = setTimeout(func, wait)
	}
}

container.onmousemove = debounce(getUserAction, 1000)

想要的效果是实现了,但是我console了this的指向。在我直接使用getUserAction()
时的this指向的是:
JavaScript 防抖、节流_第1张图片
而我在使用了debounce()后,getUserAction()指向的是window
JavaScript 防抖、节流_第2张图片
所以我们在setTimeout函数里面需要使用apply(或者call)来改变this的指向,更改后如下:

第二版(更改了this指向)
var count = 1;
var container = document.getElementById('container');

function getUserAction(e) {
	console.log(e)
    container.innerHTML = count++;
    console.log('3', this)
};
function debounce(func, wait) {
	var timer
	console.log('1',this)
	return function() {
		var context = this
		console.log('2',this)
		clearTimeout(timer)
		timer = setTimeout(function(){
			func.apply(context)
		}, wait)
	}
}

container.onmousemove = debounce(getUserAction, 1000)

可是我们业务中是需要提供事件对象event的,在这个代码下,如果直接使用getUserAction() e会打印如下:
JavaScript 防抖、节流_第3张图片
可是使用了debounce()函数后,打印如下:
JavaScript 防抖、节流_第4张图片
所以需要使用arguments来传递需要的参数,更改后如下:

第三版(修复event对象的传递)
function debounce(func, wait) {
	var timer
	console.log('1',this)
	return function() {
		var context = this
		var args = arguments
		console.log('2',this)
		clearTimeout(timer)
		timer = setTimeout(function(){
			func.apply(context, args)
		}, wait)
	}
}

其实这样就算是很完善了,以后再有关于其他的需求就再说 ,先就写到这里

节流:

节流原理:如果持续触发事件,每隔一段时间,只执行一次事件

我继续在防抖的基础上面进行更改

方法一:使用时间戳计时

具体方法:触发事件的时候,取出当前的时间戳,减去之前的时间戳(before开始设为0),如果大于设置的时间间隔,就执行函数,然后更新现在的时间戳为之前的时间戳;如果小于时间间隔就不执行函数

function throtte(func, wait) {
	var context, args
	var before = 0
	return function() {
		var now = +new Date() // 隐式转换,相当于ToNumber。将字符串直接转化为number类型
		context = this
		args = arguments
		if (now - before > wait) {
			func.apply(context, args)
			before = now
		}

	}
}

container.onmousemove = throtte(getUserAction, 3000)

关于隐式转化:
JavaScript 防抖、节流_第5张图片

方法二:使用定时器

具体方法:当触发事件,设置一个定时器,再触发这个事件的时候,如果存在定时器就不执行,直到定时器执行,然后执行函数,清空定时器,再设置下一次的定时器

function throtte(func, wait) {
	var timer
	var context, args
	return function() {
		context = this
		args = arguments
		if (!timer) {
			timer = setTimeout(function(){
				timer = null
				func.apply(this, args)
			}, wait)
		}
	}
}

container.onmousemove = throtte(getUserAction, 3000)

上面两种,存在在分析的时候,就知道存在一些问题:

  1. 使用时间戳鼠标移入就会立刻执行一次事件,使用定时器 鼠标移入后会在3秒后再执行
  2. 第一种方法事件停止触发之后不会再执行函数,第二种方法如果在他刚刚变化,移出去就会再执行一次事件

就只有根据需求来进行优化更改了

如果想要鼠标移入能立刻触发,停止触发的时候还能再执行一次

你可能感兴趣的:(JavaScript)