这节内容不是讲防抖函数的,而是事件内部的匿名函数是如何执行的。
在学习防抖函数的时候,我发现内部使用了一个匿名函数(闭包),为什么它内部需要一个闭包函数呢?
我们都知道,在前端开发中会遇到一些频繁的事件触发,比如:
- window 的 resize、scroll
- mousedown、mousemove
- keyup、keydown
为了不让这下频繁的触发事件受到限制,防抖函数是必不可少。
我们通过滑动事件(mousemove)来复现一下整个过程。
创建一个index.html
文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>debounce</title>
<style>
#container {
width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="debounce.js"></script>
</body>
</html>
然后创建一个debounce.js
文件,触发一个mousemove
事件,然后count
一直累加
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
container.onmousemove = getUserAction;
当我们在页面滑动的时候,它会一直高频率触发:
然后我们给它加一个最最最基础的防抖函数:
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
function debounce(func, wait) {
var timer = null;
return function() {
clearTimeout(timer)
timer = setTimeout(func, wait)
}
}
container.onmousemove = debounce(getUserAction, 300);
这个防抖函数里面添加了延时器,延时300ms以后触发getUserAction这个函数。效果跟我们想的一样。
那么,问题来了,为什么debounce内部返回了一个函数呢?直接按照下面这样写不行吗?
function debounce(func, wait) {
var timer = null;
clearTimeout(timer)
timer = setTimeout(func, wait)
}
答案是不行,我们发现debounce函数在页面刷新以后只执行了一次,滑动事件发生以后,它再也不执行了。
这是为什么?
让我们来看看事件绑定的函数是如何执行的?回到最初的代码:
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
container.onmousemove = getUserAction;
getUserAction是绑定的事件,当滑动以后,getUserAction会执行,如果我们写成这样呢?
container.onmousemove = getUserAction();
答案还是不行。只有在首次加载的时候getUserAction函数执行了一次。所以函数的绑定只能这么写:
container.onmousemove = getUserAction;
这个跟我们正常写函数的时候不太一样,我们都知道执行一个函数需要加一个(),而在事件绑定中,不能这样写,它是在内部把绑定函数当成一个回掉函数,在事件发生后才去执行的。知道了这一点,让我们继续来探讨为什么防抖函数内部需要返回一个函数的问题。
让我们来看看下面这段代码:
function add() {
console.log(1);
return function() {
console.log(2);
}
}
这个函数中,我们想要打印2,是不是得这么写?
add()()
看到这里是不是有点明白了,为什么防抖函数内部需要返回一个函数了。
function debounce(func, wait) {
var timer = null;
return function() {
clearTimeout(timer)
timer = setTimeout(func, wait)
}
}
container.onmousemove = debounce(getUserAction, 300);
debounce()是不是相当于执行了add(),只执行一次。滑动事件发生以后,是不是相当于执行add()(),也就是说执行了debounce函数内部的函数,也就是:
return function() {
clearTimeout(timer)
timer = setTimeout(func, wait)
}
每次滑动的时候都会触发这个函数,然后通过延时器去控制了func(也就是getUserAction)函数的执行频率。现在是不是想清楚了,为什么防抖函数内部需要返回一个函数了?
想清楚了这一点,会解决你大部分的疑问,为什么在很多情况,函数内部需要返回一个匿名函数,还有匿名函数是如何传递参数的等等问题,然后你在学习防抖、节流等等问题的时候,彻底的理解。