js高阶函数应用—函数防抖和节流
高阶函数指的是至少满足下列两个条件之一的函数:
1. 函数可以作为参数被传递;
2.函数可以作为返回值输出;
javaScript中的函数显然具备高级函数的特征,这使得函数运用更灵活,作为学习js必定会接触到的闭包也可以通过高阶函数构建,当然本文不打算介绍闭包,我们今天的主题是函数防抖和节流。
首先我们来简单看一下什么是函数防抖和节流,我们开发过程中常经常会用到一些dom事件,比如mouseover、keydown/keyup、input(处理中文输入还可能用到compositionstart/compositionend)、scroll和resize之类的事件,当然这些事件应用场景也很常见:
1.如实现一些拖拽功能(不用 H5 Drag&Drop API)和鼠标移动需要触发一些特殊需求的js操作会用mouseover;
2.处理输入时候可能很多校验,联想之类的可能会用到keydown/keyup、input
3.一些射击类游戏的点击可能会用到keydown/keyup
4.resize和scroll这两个不用我多说了,滚动和窗口大小监听
以上这些事件都有一个共同特点就是用户的操作会触发N多次事件,比如最典型的scroll事件,滚动页面会多次重复触发,导致很多我们写的回调函数被不停的往js的事件队列里添加,重复执行很多次回调里的逻辑(如果你的回调里还有很多操作DOM的逻辑那么页面可能会卡顿)。这些并不是我们希望的,所以我们得采取一定的措施控制这种情况:
1.减少事件触发的次数
2.减少回调的执行次数
显然第一条减少事件触发作为写前端的我们应该是做不到的,所以解决方案就是减少回调的执行,因为我们能控制是否往事件循环队列里添加回调函数和以什么频率去添加,
故而我们要做的事让回调函数不要执行得太频繁,减少一些过快的调用以固定频率(这个频率我们可以控制)触发来节流,或者让短时间内重复触发的事件回调在事件停止触发的一定时间后(这个时间我们可以控制)只执行一次回调函数。
好了,简单的介绍了节流和防抖之后我们开始来写代码演示一下,首先我们先介绍函数防抖,因为防抖比节流更容易理解一些。
我们现在打开控制台给当前页面绑定一个scroll事件:
function test() {
console.log('test')
}
window.addEventListener('scroll', test)
然后吧页面滚到底发现控制台打印了很多个"test",如果我们只希望页面滚到底才触发一些操作,那么我们只需要在最后只执行一次回调,这就需要防抖函数来实现,我们这里来一个简化版本,其实这就是防抖的核心代码,只要理解这点其他的是复杂实现不过都是考虑更多的场景而已(《js高程》中说是节流,实则为防抖)
function debounce(method, context, time) {
clearTimeout(method.timeoutId);
method.timeoutId = setTimeout(() => {
method.apply(context,arguments);
}, time);
}
我们再来试验一下:
function test() {
console.log('test')
}
window.addEventListener('scroll', function() {
debounce(test, null, 200);
})
这里只要滚动事件的触发频率大于200ms就一直不会被执行,只有最后执行一次,如果你在写的页面测试,可能会在一开始执行了一次test因为刷新页面触发了scroll。
这里给出underscore里的防抖函数的实现(加粗地方对应上边介绍的核心代码逻辑):
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result; var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
}; var debounced = restArgs(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
} return result;
}); debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
}; return debounced;
};
接下来我们说一说节流函数,在开始这之前我们先来个简单的代码:
console.time('test throttle');
for (let i = 0; i < 5000; i++) {
test();
}
console.timeEnd('test throttle');

执行代码后我这里基本是在1.9s内打印了5000个“test”字符串,就是在很短时间内调用了5000次,这5000次调用我们希望每隔10ms的函数才被执行,
这里我们先来实现一个简单版本的节流函数:
var throttle = (function() {
var last = 0;
return function(method, context, time) {
var current = +new Date();
if (current - last > time) {
method.apply(context, arguments);
last = current;
}
}
})();
再次执行我们可以得到:

调用共用了138ms(时间比前边少很多,这是因为我们前边实际都执行了打印的操作),而这里throttle函数知识把test()函数放进js函数执行队列,并没有执行打印,所以执行时间要少很多,不过我们还是得到了我们期望的效果:函数在多次调用的时候我们期望固定频率执行函数,而忽略其他函数不执行。
这里我们同样给出underscore里的节流函数版本:
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {}; var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
}; var throttled = function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
}; throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
}; return throttled;
};
js高阶函数应用—函数防抖和节流的更多相关文章
- JS高阶---变量与函数提升
大纲: 主体: 案例1: 接下来在控制台source里进行断点测试 打好断点后,在控制台测试window .
- JS高阶函数的理解(函数作为参数传递)
JS高阶函数的理解 高阶函数是指至少满足下列条件之一的函数. · 函数可以作为参数被传递 · 函数可以作为返回值输出 一个例子,我们想在页面中创建100个div节点,这是一种写法.我们发现并不是所有用 ...
- React.js高阶函数的定义与使用
/* 高阶函数的简单定义与使用 一: 先定义一个普通组件 二: 用function higherOrder(WrappendComponent) { return } 将组件包裹起来,并用export ...
- js高阶函数
我是一个对js还不是很精通的选手: 关于高阶函数详细的解释 一个高阶函数需要满足的条件(任选其一即可) 1:函数可以作为参数被传递 2:函数可以作为返回值输出 吧函数作为参数传递,这代表我们可以抽离一 ...
- js 高阶函数 闭包
摘自 https://www.cnblogs.com/bobodeboke/p/5594647.html 建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bob ...
- 浅谈JS高阶函数
引入 我们都知道函数是被设计为执行特定任务的代码块,会在某代码调用它时被执行,获得返回值或者实现其他功能.函数有函数名和参数,而函数参数是当调用函数接收的真实的值. 今天要说的高阶函数的英文为High ...
- JS高阶函数的使用
1.何为高阶函数呢? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数.简单来说,就是对其他 ...
- JS 高阶函数
笔记整理自:廖雪峰老师的JS教程 目录 概述 Array中的高阶函数 map(返回新的Array) reduce(返回新的Array) filter(返回新的Array) sort(返回同一Array ...
- js高阶函数应用—函数柯里化和反柯里化
在Lambda演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...
随机推荐
- STL --> string类字符串
基本使用方法 一.输入 string s: cin >> s; getline(cin, s) ; //使用默认的'\n'作为终止符 getline(cin, s, '!') ; //以' ...
- [poj2342]Anniversary party_树形dp
Anniversary party poj-2342 题目大意:没有上司的舞会原题. 注释:n<=6000,-127<=val<=128. 想法:其实就是最大点独立集.我们介绍树形d ...
- Object类-----java
Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另一个类,那么这类默认继承object类 Object类中的方法,适合所有子类. 1 toString()方法在Obje ...
- <经验杂谈>C#对CA证书加密解密的简单介绍
最近做项目接触了一些关于用CA证书加密解密的知识,现在分享一下,加密主要分为对称加密和非对称加密以及单项加密这三种,CA是一个权威的第三方认证机构,CA加密有公钥和私钥之分. 以下是C#读取证书文件进 ...
- 结对作业NO.2
结对NO.2 1. 引言 1.1 项目地址 github 生成的一组好数据 1.2 项目简介 按照老师给的项目要求:"编码实现一个部门与学生的智能匹配的程序".由于数据需要自己生成 ...
- 一起happy--C++小组Alpha版本发布说明
1 功能介绍 该PC端APP,是一个同行者的信息搜索平台,旨在为喜欢游玩,但是身边同学朋友时间冲突,想找人结伴的年轻人提供一个检索平台,让他们尽量能够快速便捷的寻找合适同行者.该APP有登录.注册.主 ...
- TCP和UDP的最完整的区别
TCP UDP TCP与UDP基本区别 1.基于连接与无连接 2.TCP要求系统资源较多,UDP较少: 3.UDP程序结构较简单 4.流模式(TCP)与数据报模式(UDP); ...
- 201621123044《JAVA程序设计》第一周学习总结
1. 本周学习总结 1.了解了JAVA的诞生以及发展历史简介.JAVA语言的特点,以及JAVA的电脑安装以及环境配置. 2.JAVA不仅可以用eclipse进行编写,也可以在记事本和notepad++ ...
- 构建微服务开发环境4————安装Docker及下载常用镜像
[内容指引] 下载Docker: Mac下安装Docker: Windows下安装Docker; 下载常用docker镜像. 一.下载Docker 1.Mac适用Docker下载地址:https:// ...
- 坑爹了多少年的html元素垂直居中问题
原文章:https://www.w3cplus.com/css3/a-guide-to-flexbox.html 如果你的元素有固定高度的话 父元素用display: flex;height:100p ...