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演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...
随机推荐
- java开源安全框架-------Apache Shiro--第二天
身份验证 即在应用中谁能证明他就是他本人.一般提供如他们的身份ID一些标志信息来表明他就是他本人,如提供身份证.用户名.密码来证明 在shiro中,用户需要提供principals(身份)和crede ...
- logback常用配置详解及logback简介
logback 简介(一) Ceki Gülcü在Java日志领域世界知名.他创造了Log4J ,这个最早的Java日志框架即便在JRE内置日志功能的竞争下仍然非常流行.随后他又着手实现SLF4J 这 ...
- 如何在win10查看wifi密码
tep1 找到wifi图标 step 2 右键点击打开网络共享中心 没有啦!!
- css3控制div上下跳动
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Maven学习笔记二
依赖范围 <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api&l ...
- (译文)掌握JavaScript基础--理解this关键字的新思路
普通函数 下面这种就是普通函数 function add(x, y) { return x + y; } 每个普通函数被调用的时候,都相当于有一个this参数传进来. 内部函数this不会是外部函数传 ...
- 解决办法:由于oracle版本不同导致导入数据时失败
在向一个数据库导入dmp文件时,出现了如下错误 经查询,是由于"导出的dmp文件与导入的数据库的版本不同造成的" 用notepad查看dmp文件的版本,看看是否和数据库版本一致 解 ...
- 实验MyOD
实验MyOD 编写MyOD.java 用java MyOD XXX实现Linux下od -tx -tc XXX的功能 提交测试代码和运行结果截图,加上学号水印,提交码云代码链接. 代码如下: (刚开始 ...
- Beta冲刺第五天
一.昨天的困难 没有困难. 二.今天进度 1.林洋洋:日程刷新重构. 2.黄腾达:创建协作日程当选择只触发一次时自动填充1,并禁用input. 3.张合胜:修复列表显示日程重复单位的格式化. 三.明日 ...
- 一个毕生难忘的BUG
记得以前接手过一个Java项目,服务器程序,直接让Jar在linux上跑的那种, 这个项目由两个web服务组成,也就是两条Java进程,主进程 xxx.jar,辅助进程 xxx_helper.jar. ...