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

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

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

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

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

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

相关推荐
YeeWang2 分钟前
🎉 Eficy 让你的 Cherry Studio 直接生成可预览的 React 页面
前端·javascript
Orange30151118 分钟前
《深入源码理解webpack构建流程》
前端·javascript·webpack·typescript·node.js·es6
lovepenny40 分钟前
Failed to resolve entry for package "js-demo-tools". The package may have ......
前端·npm
超凌1 小时前
threejs 创建了10w条THREE.Line,销毁数据,等待了10秒
前端
车厘小团子1 小时前
🎨 前端多主题最佳实践:用 Less Map + generate-css 打造自动化主题系统
前端·架构·less
芒果1251 小时前
SVG图片通过img引入修改颜色
前端
海云前端11 小时前
前端面试ai对话聊天通信怎么实现?面试实际经验
前端
一枚前端小能手1 小时前
🔧 半夜被Bug叫醒的痛苦,错误监控帮你早发现
前端
Juchecar1 小时前
Vue 3 单页应用Router路由跳转示例
前端
这人是玩数学的1 小时前
在 Cursor 中规范化生成 UI 稿实践
前端·ai编程·cursor