之前开发的时候遇到过防抖、节流这些问题,当时也就是解决了,谁知道他们的名字叫做防抖、节流呢,现在来做个总结吧。
防抖原理就是:使用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指向的是:
而我在使用了debounce()后,getUserAction()指向的是window
所以我们在setTimeout函数里面需要使用apply(或者call)来改变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会打印如下:
可是使用了debounce()函数后,打印如下:
所以需要使用arguments来传递需要的参数,更改后如下:
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)
具体方法:当触发事件,设置一个定时器,再触发这个事件的时候,如果存在定时器就不执行,直到定时器执行,然后执行函数,清空定时器,再设置下一次的定时器
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)
上面两种,存在在分析的时候,就知道存在一些问题:
就只有根据需求来进行优化更改了
如果想要鼠标移入能立刻触发,停止触发的时候还能再执行一次