原文:函数防抖和节流;

序言:

  我们在平时开发的时候,会有很多场景会频繁触发事件,比如说搜索框实时发请求,onmousemove, resize, onscroll等等,有些时候,我们并不能或者不想频繁触发事件,咋办呢?这时候就应该用到函数防抖和函数节流了!

准备材料:

<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>

<script>
let num = 1;
let content = document.getElementById('content'); function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>

  这段代码, 在灰色区域内鼠标随便移动,就会持续触发 count() 函数,导致的效果如下:

  接下来我们通过防抖和节流限制频繁操作。

函数防抖(debounce)

  短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。

// 非立即执行版
function debounce(func, wait) {
let timer;
return function() {
let context = this; // 注意 this 指向
let args = arguments; // arguments中存着e if (timer) clearTimeout(timer); timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}

  我们是这样使用的:

content.onmousemove = debounce(count,1000);

  非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。效果如下:

// 立即执行版
function debounce(func, wait) {
let timer;
return function() {
let context = this; // 这边的 this 指向谁?
let args = arguments; // arguments中存着e if (timer) clearTimeout(timer); let callNow = !timer; timer = setTimeout(() => {
timer = null;
}, wait) if (callNow) func.apply(context, args);
}
}

  立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。用法同上,效果如下:

  

// 合成版
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
function debounce(func, wait, immediate) {
let timer;
return function() {
let context = this,
args = arguments; if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply
}, wait)
}
}
}

节流(throttle)

  指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次... 。节流如字面意思,会稀释函数的执行频率。

  同样有两个版本,时间戳和定时器版。

// 时间戳版
function throttle(func, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}

  使用方式如下:

content.onmousemove = throttle(count,1000);

  效果如下:

  可以看到,在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次。

// 定时器版
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}

  用法同上,效果如下:

  可以看到,在持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。

  我们应该可以很容易的发现,其实时间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。

  同样地,我们也可以将时间戳版和定时器版的节流函数结合起来,实现双剑合璧版的节流函数。

/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttle(func, wait, type) {
if (type === 1) {
let previous = 0;
} else if (type === 2) {
let timeout;
}
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now(); if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}

附录:

  关于节流/防抖函数中 context(this) 的指向解析:

  首先,在执行  throttle(count, 1000) 这行代码的时候,会有一个返回值,这个返回值是一个新的匿名函数,因此  content.onmousemove = throttle(count,1000); 这句话最终可以这样理解:

content.onmousemove = function() {
let now = Date.now();
let context = this;
let args = arguments;
...
console.log(this)
}

  到这边为止,只是绑定了事件函数,还没有真正执行,而 this 的具体指向需要到真正运行时才能够确定下来。所以这个时候如果我们把前面的  content.onmousemove  替换成  var fn  并执行 fn  fn()  ,此时内部的 this 打印出来就会是 window 对象。

  其次,当我们触发 onmousemove 事件的时候,才真正执行了上述的匿名函数,即  content.onmousemove()  。此时,上述的匿名函数的执行是通过  对象.函数名()  来完成的,那么函数内部的 this 自然指向 对象。

  最后,匿名函数内部的 func 的调用方式如果是最普通的直接执行  func()  ,那么 func 内部的 this 必然指向 window ,虽然在代码简单的情况下看不出什么异常(结果表现和正常一样),但是这将会是一个隐藏 bug,不得不注意啊!所以,我们通过匿名函数捕获 this,然后通过 func.apply() 的方式,来达到 content.onmousemove = func 这样的效果。

  可以说,高阶函数内部都要注意 this 的绑定。

js 函数的防抖(debounce)与节流(throttle)的更多相关文章

  1. js 函数的防抖(debounce)与节流(throttle) 带 插件完整解析版 [helpers.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         函数防抖与节流是做什么的?下面进行通俗的讲解. 本文借鉴:h ...

  2. 防抖debounce和节流throttle

    大纲 一.出现缘由 二.什么是防抖debounce和节流throttle 三.应用场景 3.1防抖 3.2节流 一.出现缘由 前端开发中,有一部分用户行为会频繁触发事件,而对于DOM操作,资源加载等耗 ...

  3. Java版的防抖(debounce)和节流(throttle)

    概念 防抖(debounce) 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时. 防抖,即如果短时间内大量触发同一事件,都会 ...

  4. js 防抖 debounce 与 节流 throttle

    debounce(防抖) 与 throttle(节流) 主要是用于用户交互处理过程中的性能优化.都是为了避免在短时间内重复触发(比如scrollTop等导致的回流.http请求等)导致的资源浪费问题. ...

  5. 防抖(Debounce)与节流( throttle)区别

    http://www.cnblogs.com/ShadowLoki/p/3712048.html http://blog.csdn.net/tina_ttl/article/details/51830 ...

  6. JavaScript 防抖(debounce)和节流(throttle)

    防抖函数 触发高频事件后,n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间 /** * * @param {*} fn :callback function * @param {* ...

  7. C#.Net下的防抖-Debounce和节流阀-Throttle功能实现

    C#下的防抖-Debounce.节流阀-Throttle功能实现 防抖-Debounce 连续的多次调用,只有在调用停止之后的一段时间内不再调用,然后才执行一次处理过程. 节流阀-Throttle 连 ...

  8. [JavaScript] 函数节流(throttle)和函数防抖(debounce)

    js 的函数节流(throttle)和函数防抖(debounce)概述 函数防抖(debounce) 一个事件频繁触发,但是我们不想让他触发的这么频繁,于是我们就设置一个定时器让这个事件在 xxx 秒 ...

  9. “浅入浅出”函数防抖(debounce)与节流(throttle)

    函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭<JS高级程序设计>书中所述"函数节流" ...

随机推荐

  1. Go语言集成开发环境之GoLand安装使用

    下载Go语言开发包 大家可以在Go语言官网(https://golang.google.cn/dl/)下载 Windows 系统下的Go语言开发包,如下图所示. 这里我们下载的是 64 位的开发包,如 ...

  2. Maven--归类依赖

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  3. SVN常见错误解决办法和批量add等命令

    批量添加所有更改文件 svn add . --no-ignore --force 提交文件 svn commit -m "up" File already exists: file ...

  4. Java利用DES/3DES/AES这三种算法分别实现对称加密

    转载地址:http://blog.csdn.net/smartbetter/article/details/54017759 有两句话是这么说的: 1)算法和数据结构就是编程的一个重要部分,你若失掉了 ...

  5. 巧用函数,使Sql中in的用法更多变

    在Sql中我们经常会用到in 普遍的写法为 where xx in ('1','2','3') 通过函数写法为: IF EXISTS ( SELECT * FROM sys.objects WHERE ...

  6. springMVC的注解@PathVariable是什么?详情及用法解析

    在路由中定义变量规则后,通常我们需要在处理方法(也就是@RequestMapping注解的方法)中获取这个URL变量的具体值,并根据这个值(例如用户名)做相应的操作,Spring MVC提供的@Pat ...

  7. Java之接口(java8的新特性)

    public class SubClassTest { public static void main(String[] args) { SubClass s = new SubClass(); // ...

  8. vue打包成app后,背景图片不显示

    问题: 在使用npm run build 打包后, 如果在页面中使用img标签引入,打包后的路径是由index.html开始访问的,真正访问的是Static/img/图片名, 是正确的, 但是写在cs ...

  9. 2018-10-09-Pser

    title date tags layout Pser 2018-10-09 杂谈 post ### 踏雪无痕![踏雪无痕](http://da1sy.github.io/assets/images/ ...

  10. 奇点云数据中台技术汇(一) | DataSimba——企业级一站式大数据智能服务平台

    在这个“数据即资产”的时代,大数据技术和体量都有了前所未有的进步,若企业能有效使用数据,让数据赚钱,这必将成为企业数字化转型升级的有力武器. 奇点云自研的一站式大数据智能服务平台——DataSimba ...