高阶函数指的是至少满足下列两个条件之一的函数:

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;
}
}
})();
      for (let i = 0; i < 50000; i++) {
        throttle(test, null, 10);
      }
                                  

再次执行我们可以得到:

调用共用了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高阶函数应用—函数防抖和节流的更多相关文章

  1. JS高阶---变量与函数提升

    大纲: 主体: 案例1: 接下来在控制台source里进行断点测试 打好断点后,在控制台测试window .

  2. JS高阶函数的理解(函数作为参数传递)

    JS高阶函数的理解 高阶函数是指至少满足下列条件之一的函数. · 函数可以作为参数被传递 · 函数可以作为返回值输出 一个例子,我们想在页面中创建100个div节点,这是一种写法.我们发现并不是所有用 ...

  3. React.js高阶函数的定义与使用

    /* 高阶函数的简单定义与使用 一: 先定义一个普通组件 二: 用function higherOrder(WrappendComponent) { return } 将组件包裹起来,并用export ...

  4. js高阶函数

    我是一个对js还不是很精通的选手: 关于高阶函数详细的解释 一个高阶函数需要满足的条件(任选其一即可) 1:函数可以作为参数被传递 2:函数可以作为返回值输出 吧函数作为参数传递,这代表我们可以抽离一 ...

  5. js 高阶函数 闭包

    摘自  https://www.cnblogs.com/bobodeboke/p/5594647.html 建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bob ...

  6. 浅谈JS高阶函数

    引入 我们都知道函数是被设计为执行特定任务的代码块,会在某代码调用它时被执行,获得返回值或者实现其他功能.函数有函数名和参数,而函数参数是当调用函数接收的真实的值. 今天要说的高阶函数的英文为High ...

  7. JS高阶函数的使用

    1.何为高阶函数呢? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数.简单来说,就是对其他 ...

  8. JS 高阶函数

    笔记整理自:廖雪峰老师的JS教程 目录 概述 Array中的高阶函数 map(返回新的Array) reduce(返回新的Array) filter(返回新的Array) sort(返回同一Array ...

  9. js高阶函数应用—函数柯里化和反柯里化

    在Lambda演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...

随机推荐

  1. Oracle安装11.2.0.4.180116补丁及如何检查数据库安装补丁

    最近做了一个安装11.2.0.4.180116补丁的实验,突然想起之前和同事讨论的一个问题:如何检查数据库安装补丁的版本,之前搜到的是去查dba_registry_history,有的说在操作系统中执 ...

  2. 初始CSS3

    初始CSS31.1.什么是CSSCSS全程为层叠样式表(Cascading Style Sheet),通常又称为风格样式表(Style Sheet)它是用来进行网页风格设计的.1.CSS在网页中的应用 ...

  3. 【Java】0X001.配置开发环境,JDK、classpath等

    [Java]0x01 配置开发环境,JDK.CLASSPATH等 一. 下载JDK安装文件 首先,进入Oracle官网Java页面. 注意,要下载的是JDK而不是JRE,这点很重要,因为JRE并不包含 ...

  4. JAVA入门——Generic/泛型

    在台科大的第二次JAVA作业,老师课上讲的内容是泛型. 泛型(generic),泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以 ...

  5. Beta 第七天

    今天遇到的困难: 构造新适配器的时候出现了某些崩溃的问题 ListView监听器有部分的Bug 今天完成的任务: 陈甘霖:完成相机调用和图库功能,完成阿尔法项目遗留下来的位置调用问题,实现百度定位 蔡 ...

  6. 关于python词典(Dictionary)的get()用法

    先贴出参考链接:http://www.runoob.com/python/att-dictionary-get.html get()方法语法: dict.get(key, default=None) ...

  7. 翻译:CREATE FUNCTION语句(已提交到MariaDB官方手册)

    本文为mariadb官方手册:CREATE FUNCTION的译文. 原文:https://mariadb.com/kb/en/library/create-function/我提交到MariaDB官 ...

  8. 【技巧】Java工程中的Debug信息分级输出接口

    也许本文的标题你们没咋看懂.但是,本文将带大家领略输出调试的威力. 灵感来源 说到灵感,其实是源于笔者在修复服务器的ssh故障时的一个发现. 这个学期初,同袍(容我来一波广告产品页面,同袍官网)原服务 ...

  9. HDFS的7个设计特点

    1.Block的放置:默认不配置.一个Block会有三份备份,一份放在NameNode指定的DataNode,另一份放在与指定DataNode非同一Rack上的DataNode,最后一份放在与指定Da ...

  10. Java中Math类的常用方法

    public class MathDemo { public static void main(String args[]){ /** * abs求绝对值 */ System.out.println( ...