原文:函数防抖和节流;

序言:

  我们在平时开发的时候,会有很多场景会频繁触发事件,比如说搜索框实时发请求,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. Linux中的错误重定向你真的懂吗

    在很多定时任务里.shell里我们往往能看到 "2>&1",却不知道这背后的原理. 举个例子: * 1 * * * test.sh > /dev/null 2& ...

  2. Django专题之ORM

    ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述 ...

  3. 关于live2D的使用

    <script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidge ...

  4. iframe高度相关知识点整理

    IFRAME 元素也就是文档中的文档. contentWindow属性是指指定的frame或者iframe所在的window对象. 用iframe嵌套页面是,如果父页面要获取子页面里面的内容,可以使用 ...

  5. React Native 开发

    摘自:<React Native 开发之 IDE 选型和配置> 一个在不断更新的有关React Native讲解:<江清清的技术专栏> ES5和ES6的区别:<React ...

  6. 1.Jenkins基本安装及插件离线安装

    Jenkins基本安装 Jenkins官方地址:https://jenkins.io/zh/,下载最新版本,需要依赖对应的jdk版本才可以正常运行. dockerfile文件构建docker镜像 FR ...

  7. 《ECMAScript 6 入门教程 - 阮一峰著》学习笔记

    在刷LeetCode的过程中看到很多新的语法糖,系统学习一下以便代码更加规范,美观,健壮.

  8. 直击LG曲面OLED首发现场,高端品质更出众

    简直是太棒了,我可以去看LG曲面OLED电视新品发布会了.这可是LG向中国首次推出的曲面OLED电视.在网上我就已经看到其实曲面OLED电视已经在韩国.美国还有欧洲都上市了,听说现在反响还挺不错.真没 ...

  9. 1088. Rational Arithmetic (20)

    1.注意在数字和string转化过程中,需要考虑数字不是只有一位的,如300转为"300",一开始卡在里这里, 测试用例: 24/8 100/10 24/11 300/11 2.该 ...

  10. [从 0 开始的 Angular 生活]No.38 实现一个 Angular Router 切换组件页面(一)

    前言 今天是进入公司的第三天,为了能尽快投入项目与成为团队可用的战力,我正在努力啃官方文档学习 Angular 的知识,所以这一篇文章主要是记录我如何阅读官方文档后,实现这个非常基本的.带导航的网页应 ...