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演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...
随机推荐
- 常用linux日志查询命令
1.查看实时日志: tail -f nohup.out 2.分页查看所有日志: cat nohup.out | more 4.分页查看前N行日志: tail -n 1000 nohup.out | m ...
- [poj2585]Window Pains_拓扑排序
Window Pains poj-2585 题目大意:给出一个4*4的方格表,由9种数字组成.其中,每一种数字只会出现在特定的位置,后出现的数字会覆盖之前在当前方格表内出现的.询问当前给出的方格表是否 ...
- python 函数基础 定义
一.函数介绍 1.为什么要有函数? 没有函数的代码组织结构不清晰,可读性差. 代码冗余 管理维护难度大,扩展性 2.什么是函数? 具备某一个功能的工具就是程序中的函数. 事先准备工具的过程就是:函数的 ...
- 使用Python的requests模块编写请求脚本
requests模块可用来编写请求脚本. 比如,使用requests的post函数可以模拟post请求: resp = requests.post(url, data = content) url即为 ...
- [日常] Codeforces Round #440 Div.2 大力翻车实况
上次打了一发ABC然后大力翻车...上午考试又停电+Unrated令人非常滑稽...下午终于到了CF比赛... 赛前大力安利了一发然后拉了老白/ $ljm$ / $wcx$ 一起打, 然后搞了个 TI ...
- ORA-03206,当表空间不够时,如何以添加数据文件的方式扩展表空间
准备导入一个数据库,大约为33G,开始创建的空库表空间为自增到20G,结果自然不够,然后就开始自动扩展表空间大小 使用的如下语句 --自动扩展表空间大小 ALTER DATABASE DATAFILE ...
- C语言程序设计(基础)最后一次作业-- 总结报告
本次作业是本学期的最后一次作业,有始有终,本次作业回顾下本学期的第0次作业,回答下面几个问题. 注意:在回答问题时请不要简单的回答 "不是","是".请把这当成 ...
- Beta冲刺 第一天
Beta冲刺 第一天 1. 昨天的困难 由于今天还是第一天,所以暂时没有昨天的困难. 2. 今天解决的进度 潘伟靖: 对代码进行了review 1.将某些硬编码改为软编码 2.合并了一些方法,简化代码 ...
- TOTP算法 基于时间的一次性密码
/** Copyright (c) 2011 IETF Trust and the persons identified as authors of the code. All rights rese ...
- 技术文档分享_linux中生成考核用的GPT分区表结构修复
注:历史版本,后期改用python实现了 实验一: 目的:用于生成大量模拟破坏GPT分区结构案例,并生成唯一方式修复后的评判方法.故障:在一个完整的GPT分区磁盘上,丢失了GPT主分区表,或备份分区表 ...