likes
comments
collection
share

事件内部的匿名函数是如何执行的

作者站长头像
站长
· 阅读数 21

这节内容不是讲防抖函数的,而是事件内部的匿名函数是如何执行的。

在学习防抖函数的时候,我发现内部使用了一个匿名函数(闭包),为什么它内部需要一个闭包函数呢?

我们都知道,在前端开发中会遇到一些频繁的事件触发,比如:

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. 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)函数的执行频率。现在是不是想清楚了,为什么防抖函数内部需要返回一个函数了?

想清楚了这一点,会解决你大部分的疑问,为什么在很多情况,函数内部需要返回一个匿名函数,还有匿名函数是如何传递参数的等等问题,然后你在学习防抖、节流等等问题的时候,彻底的理解。