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

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

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

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

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

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

相关推荐
爱生活的苏苏19 分钟前
vue生成二维码图片+文字说明
前端·vue.js
拉不动的猪21 分钟前
安卓和ios小程序开发中的兼容性问题举例
前端·javascript·面试
炫彩@之星27 分钟前
Chrome书签的导出与导入:步骤图
前端·chrome
贩卖纯净水.38 分钟前
浏览器兼容-polyfill-本地服务-优化
开发语言·前端·javascript
前端百草阁44 分钟前
从npm库 Vue 组件到独立SDK:打包与 CDN 引入的最佳实践
前端·vue.js·npm
夏日米米茶1 小时前
Windows系统下npm报错node-gyp configure got “gyp ERR“解决方法
前端·windows·npm
且白1 小时前
vsCode使用本地低版本node启动配置文件
前端·vue.js·vscode·编辑器
程序研1 小时前
一、ES6-let声明变量【解刨分析最详细】
前端·javascript·es6
疯狂的沙粒2 小时前
在uni-app中如何从Options API迁移到Composition API?
javascript·vue.js·uni-app
siwangqishiq22 小时前
Vulkan Tutorial 教程翻译(四) 绘制三角形 2.2 呈现
前端