在前端开发中会遇到一些频繁的事件触发,例如:
- window的resize、scroll
- mousedown、mousemove
- keyup、keydown
频繁的触发会造成卡顿现象,为了解决这个问题,一般有两种解决方案:
- debounce防抖
- throttle节流
防抖
防抖的原理是:你尽管触发事件,但是我一定在事件触发n秒后执行,如果一个事件触发n秒内又触发了这个事件,那我就以新的事件的时间为准,n秒后执行,总之就是要等你触发事件n秒内不再触发事件。
<html>
<head>
<meta charset="utf-8">
<style>
#container {
width: 100%;
height: 200px;
line-height: 200px;
text-align: center;
color: #000;
background-color: #ddd;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
<script>
var count = 1;
var container = document.getElementById('container')
function getUserAction() {
container.innerHTML = count++
}
container.onmousemove = getUserAction
</script>
</html>
第一版
function debounce(func, wait) {
var timeout
return function() {
clearTimeout(timeout)
timeout = setTimeout(func, wait)
}
}
this
getUserAction函数中,在不使用debounce时,this指向的是container的dom元素,在使用debounce时,this指向的是window,修改下代码修正this指向
function debounce(func, wait) {
var timeout
return function() {
var context = this
clearTimeout(timeout)
timeout = setTimeout(function() {
func.apply(context)
}, wait)
}
}
event对象
JavaScript在事件处理函数中会提供事件对象event,但在使用debounce时,event对象为undefined,修正下event对象问题
function debounce(func, wait) {
var timeout
return function() {
var context = this
var args = arguments
clearTimeout(timeout)
timeout = setTimeout(function() {
func.apply(context, args)
}, wait)
}
}
返回值
getUserAction函数可能存在返回值,所以我们要返回函数执行的结果
function debounce(func, wait) {
var timeout, reslut
return function() {
var context = this
var args = arguments
clearTimeout(timeout)
timeout = setTimeout(function() {
reslut = func.apply(context, args)
}, wait)
return reslut
}
}
立即执行
立即执行需求是,我不想非要等到事件停止触发后才执行,我希望能立即执行函数,然后等到停止触发n秒后,才可以重新执行
function debounce(func, wait, immediate) {
var timeout, reslut
return function() {
var context = this
var args = arguments
if(timeout) clearTimeout(timeout)
if(immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout
timeout = setTimeout(function() {
timeout = null
}, wait)
if(callNow) reslut = func.apply(context, args)
} else {
timeout = setTimeout(function() {
reslut = func.apply(context, args)
}, wait)
}
return reslut
}
}
取消
可以支持取消debounce函数,比如说我debounce的时间为10秒,immediate为true,这样的话,我只有等10秒之后才能重新触发事件,现在我希望能主动取消debounce,可以再次触发函数
function debounce(func, wait, immediate) {
var timeout, reslut
var debounced = function() {
var context = this
var args = arguments
if(timeout) clearTimeout(timeout)
if(immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout
timeout = setTimeout(function() {
timeout = null
}, wait)
if(callNow) reslut = func.apply(context, args)
} else {
timeout = setTimeout(function() {
reslut = func.apply(context, args)
}, wait)
}
return reslut
}
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
完整示例代码
<html>
<head>
<meta charset="utf-8">
<style>
#container {
width: 100%;
height: 200px;
line-height: 200px;
text-align: center;
color: #000;
background-color: #ddd;
}
</style>
</head>
<body>
<div id="container"></div>
<button id="cancel">取消</button>
</body>
<script>
var count = 1;
var container = document.getElementById('container')
var cancel = document.getElementById('cancel')
function getUserAction(e) {
container.innerHTML = count++
console.log(e)
}
var debounceFun = debounce(getUserAction, 10000, true)
container.onmousemove = debounceFun
cancel.onclick = function() {
debounceFun.cancel()
}
function debounce(func, wait, immediate) {
var timeout, reslut
var debounced = function() {
var context = this
var args = arguments
if(timeout) clearTimeout(timeout)
if(immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout
timeout = setTimeout(function() {
timeout = null
}, wait)
if(callNow) reslut = func.apply(context, args)
} else {
timeout = setTimeout(function() {
reslut = func.apply(context, args)
}, wait)
}
return reslut
}
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
</script>
</html>