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

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

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

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

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown

为了不让这下频繁的触发事件受到限制,防抖函数是必不可少。

我们通过滑动事件(mousemove)来复现一下整个过程。

创建一个index.html文件

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一直累加

js 复制代码
var count = 1;
var container = document.getElementById('container');

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

当我们在页面滑动的时候,它会一直高频率触发:

然后我们给它加一个最最最基础的防抖函数:

js 复制代码
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内部返回了一个函数呢?直接按照下面这样写不行吗?

js 复制代码
function debounce(func, wait) {
  var timer = null;
  clearTimeout(timer)
  timer = setTimeout(func, wait)
}

答案是不行,我们发现debounce函数在页面刷新以后只执行了一次,滑动事件发生以后,它再也不执行了。

这是为什么?

让我们来看看事件绑定的函数是如何执行的?回到最初的代码:

js 复制代码
var count = 1;
var container = document.getElementById('container');

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

getUserAction是绑定的事件,当滑动以后,getUserAction会执行,如果我们写成这样呢?

js 复制代码
container.onmousemove = getUserAction();

答案还是不行。只有在首次加载的时候getUserAction函数执行了一次。所以函数的绑定只能这么写:

js 复制代码
container.onmousemove = getUserAction;

这个跟我们正常写函数的时候不太一样,我们都知道执行一个函数需要加一个(),而在事件绑定中,不能这样写,它是在内部把绑定函数当成一个回掉函数,在事件发生后才去执行的。知道了这一点,让我们继续来探讨为什么防抖函数内部需要返回一个函数的问题。

让我们来看看下面这段代码:

js 复制代码
function add() {
  console.log(1);
  return function() {
    console.log(2);
  }
}

这个函数中,我们想要打印2,是不是得这么写?

js 复制代码
add()()

看到这里是不是有点明白了,为什么防抖函数内部需要返回一个函数了。

js 复制代码
function debounce(func, wait) {
  var timer = null;
  return function() {
    clearTimeout(timer)
    timer = setTimeout(func, wait)
  }
}
container.onmousemove = debounce(getUserAction, 300);

debounce()是不是相当于执行了add(),只执行一次。滑动事件发生以后,是不是相当于执行add()(),也就是说执行了debounce函数内部的函数,也就是:

js 复制代码
return function() {
  clearTimeout(timer)
  timer = setTimeout(func, wait)
}

每次滑动的时候都会触发这个函数,然后通过延时器去控制了func(也就是getUserAction)函数的执行频率。现在是不是想清楚了,为什么防抖函数内部需要返回一个函数了?

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

相关推荐
爱上好庆祝31 分钟前
学习js的第五天
前端·css·学习·html·css3·js
C澒43 分钟前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼981 小时前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴1 小时前
前端与后端的区别与联系
前端
yqcoder1 小时前
JavaScript 柯里化:把“大餐”拆成“小炒”的艺术
开发语言·javascript·ecmascript
每天吃饭的羊1 小时前
JSZip的使用
开发语言·javascript
EnCi Zheng2 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen2 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技2 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人2 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html