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

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. c++ --> 返回值分析

    返回值分析 函数不能通过返回指向栈内存的指针,返回指向堆内存的指针是可以的. 一.返回局部变量的值 可以有两种情况:返回局部自动变量和局部静态变量,比如: int func() { ; // 返回局部 ...

  2. 导入TensorFlow报错

    C:\....\Anaconda3\envs\py35\lib\site-packages\h5py\__init__.py:36: FutureWarning: Conversion of the ...

  3. PHP 引用是个坑,请慎用

    去年我参加了很多次会议,其中八次会议里我进行了相关发言,这其中我多次谈到了 PHP 的引用问题,因为很多人对它的理解有所偏差.在深入讨论这个问题之前,我们先回顾一下引用的基本概念,明确什么是" ...

  4. DotNetCore跨平台~Json动态序列化属性

    回到目录 Json动态序列化属性,主要为了解决一个大实体,在返回前端时根据需要去序列化,如果实体里的某个属性在任务情况下都不序列化,可以添加[JsonIgnore]特性,这种是全局的过滤,但是更多的情 ...

  5. 关于yaml语言

    yaml语言广泛用于书写配置文件. 主要特点如下: 1.使用缩进表示层级关系,缩进使用空格键(非Tab键) 2.缩进的空格数目不要求,只要相同层级的元素左侧对其即可 3.#之后的内容为注释 4.yam ...

  6. JavaWeb学习笔记六 JSP

    JSP技术 JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java s ...

  7. C++多线程学习之(一)——并发与多线程

    1 并发 计算机领域的并发指的是在单个系统里同时执行多个独立的任务,而非顺序地进行一些活动. 1.1 并发的途径 多进程并发:将应用程序分为多个独立的进程,它们在同一时刻运行,就像同时进行网页浏览和文 ...

  8. C#/AutoCAD 2018/ObjectArx/二次开发再说实体(六)

    这些函数对大家很有用,如果想获取详细源代码请加云幽学院yunyou.ke.qq.com报名免费课程,如果想学习更系统.更全面的知识请报名收费课程,有大量开发案例共享. 1.获取模型空间中所有实体 #r ...

  9. 关于DLL的创建与使用简单描述(C++、C#)

    前言 前一段时间在学关于DLL的创建与调用,结果发现网络上一大堆别人分享的经验都有点问题.现在整理分享一下自己的方法. 工具 Microsoft Visual Studio 2017 depends ...

  10. js定时刷新页面.

    //页面定时刷新.2017.09.27 $(document).ready(function () { self.setInterval(function () { var d = new Date( ...