JavaScript专题之underscore防抖实例学习

目录
  • 前言
  • 防抖
  • 第一版
  • this
  • event 对象
  • 返回值
  • 立刻执行
  • 取消

JavaScript 专题系列第一篇,讲解防抖,带你从零实现一个 underscore 的 debounce 函数

前言

在前端开发中会遇到一些频繁的事件触发,比如:

  • window 的 resize、scroll
  • mousedown、mousemove
  • keyup、keydown
    ……

为此,我们举个示例代码来了解事件如何频繁的触发:

我们写个 index.html 文件:

<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
    <title>debounce</title>
    <style>
        #container{
            width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <script src="debounce.js"></script>
</body>
</html>

debounce.js 文件的代码如下:

var count = 1;
var container = document.getElementById('container');
function getUserAction() {
    container.innerHTML = count++;
};
container.onmousemove = getUserAction;

我们来看看效果:

debounce

从左边滑到右边就触发了 165 次 getUserAction 函数!

因为这个例子很简单,所以浏览器完全反应的过来,可是如果是复杂的回调函数或是 ajax 请求呢?假设 1 秒触发了 60 次,每个回调就必须在 1000 / 60 = 16.67ms 内完成,否则就会有卡顿出现。

为了解决这个问题,一般有两种解决方案:

  • debounce 防抖
  • throttle 节流

防抖

今天重点讲讲防抖的实现。

防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!

第一版

根据这段表述,我们可以写第一版的代码:

// 第一版
function debounce(func, wait) {
    var timeout;
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}

如果我们要使用它,以最一开始的例子为例:

container.onmousemove = debounce(getUserAction, 1000);

现在随你怎么移动,反正你移动完 1000ms 内不再触发,我再执行事件。

顿时就从 165 次降低成了 1 次!

棒棒哒,我们接着完善它。

this

如果我们在 getUserAction 函数中 console.log(this),在不使用 debounce 函数的时候,this 的值为:

<div id="container"></div>

但是如果使用我们的 debounce 函数,this 就会指向 Window 对象!

所以我们需要将 this 指向正确的对象。

我们修改下代码:

// 第二版
function debounce(func, wait) {
    var timeout;
    return function () {
        var context = this;
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context)
        }, wait);
    }
}

现在 this 已经可以正确指向了。让我们看下个问题:

event 对象

JavaScript 在事件处理函数中会提供事件对象 event,我们修改下 getUserAction 函数:

function getUserAction(e) {
    console.log(e);
    container.innerHTML = count++;
};

如果我们不使用 debouce 函数,这里会打印 MouseEvent 对象,如图所示:

MouseEvent

但是在我们实现的 debounce 函数中,却只会打印 undefined!

所以我们再修改一下代码:

// 第三版
function debounce(func, wait) {
    var timeout;
    return function () {
        var context = this;
        var args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

返回值

再注意一个小点,getUserAction 函数可能是有返回值的,所以我们也要返回函数的执行结果

// 第四版
function debounce(func, wait) {
    var timeout, result;
    return function () {
        var context = this;
        var args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            result = func.apply(context, args)
        }, wait);
        return result;
    }
}

到此为止,我们修复了三个小问题:

  • this 指向
  • event 对象
  • 返回值

立刻执行

这个时候,代码已经很是完善,但是为了让这个函数更加完善,我们接下来思考一个新的需求。

这个需求就是:

我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发n秒后,才可以重新触发执行。

想想这个需求也是很有道理的嘛,那我们加个 immediate 参数判断是否是立刻执行。

// 第五版
function debounce(func, wait, immediate) {
    var timeout, result;
    return function () {
        var context = this;
        var args = arguments;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                result = func.apply(context, args)
            }, wait);
        }
        return result;
    }
}

取消

最后我们再思考一个小需求,我希望能取消 debounce 函数,比如说我 debounce 的时间间隔是 10 秒钟,immediate 为 true,这样的话,我只有等 10 秒后才能重新触发事件,现在我希望有一个按钮,点击后,取消防抖,这样我再去触发,就可以又立刻执行啦,是不是很开心?

为了这个需求,我们写最后一版的代码:

// 第六版
function debounce(func, wait, immediate) {
    var timeout, result;
    var debounced = function () {
        var context = this;
        var args = arguments;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                result = func.apply(context, args)
            }, wait);
        }
        return result;
    };
    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    };
    return debounced;
}

演示效果如下:

debounce-cancel

至此我们已经完整实现了一个 underscore 中的 debounce 函数,恭喜,撒花!

专题系列

JavaScript专题系列:https://www.jb51.net/list/list_3_1.htm

JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。

以上就是JavaScript专题之underscore防抖实例学习的详细内容,更多关于JavaScript underscore防抖的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaScript中防抖和节流的区别及适用场景

    目录 前言 防抖 例如 代码演示 节流 例如 代码演示 前言 防抖和节流,这个在我们的前端生涯中,这两个名词肯定不陌生,甚至经常被人问起: 两者有什么区别? 分别用于什么场景? ps:这就是高频的面试题了吧! 防抖 防抖是什么呢? 形象的的说就是:防止抖动(防抖函数内心独白:“你就抖动吧!等你不抖动了,我们在进行下一步”) 例如 一个搜索输入框, 用户不停的进行输入(这个时候就是抖动的过程), 等用户输入停止之后,再触发搜索. 代码演示 function debounce(fn, delay =

  • 详细聊一聊js防抖节流到底是什么

    目录 前言 场景 防抖 核心 解释 修复场景例子 节流 核心 解释 修复场景例子 总结 前言 防抖和节流,这是前端防止用户频繁调用同一个接口的方法,比如短时间重复点击上传同一个文件,短时间重复点击提交同一个评论,异步的操作还没给你带来反馈,于是你重复上传了多个文件,重复提交了多个评论. 本文以常见的使用场景与解决方案,一篇教会你如何使用防抖节流. 场景 为了例子更加简单,我们就用延迟来模拟一个后端接口返回的过程. <body> <button onclick="comment(

  • JavaScript中函数的防抖与节流详解

    目录 一.函数的节流 1.1定义 1.2解决方法 1.3案例演示 1.3.1 代码演示 1.3.2 运行结果 1.3.3 添加函数节流操作 1.3.4 运行结果 二.函数的防抖 2.1 定义 2.2 解决方法 2.3 案例演示 2.3.1 代码展示 2.3.2 运行结果 2.3.3添加函数防抖操作 2.3.4 运行结果 总结 一.函数的节流 1.1 定义 同时触发多次函数执行,执行的是相同内容,要求只执行第一次请求. 例如scroll事件,鼠标滚动一次触发多次函数执行,只需要执行一次. 1.2

  • JS中节流和防抖函数的实现及区别示例

    目录 引言 一.概念 二.实现 三.区别 四.Lodash 4-1.throttle 4-2.debounce 五.使用场景 六.总结 引言 在前端开发中,经常和DOM.BOM打交道,例如:窗口的resize.scroll,输入框内容校验,按钮点击等等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕. 此时我们可以采用throttle(节流)和debounce(防抖)的方式来减少调用频率,提高性能的同时又不影响实际效果. 一.概念 函数节流( throttle

  • js防抖具体实现以及详细原理步骤说明(附实例)

    目录 Why?为啥要有防抖? What? 啥是防抖? How? 防抖咋用啊? 什么是延迟debounce?? 总结 ps:本文将从一个案例出发循序渐进,在其中你不仅能知道防抖是如何实现的,还可以学习到关于 this .apply.arguments 等知识的具体应用. Why?为啥要有防抖? 因为有时候频繁的事件触发是没有意义的,不仅影响性能还可能造成卡顿. 为了避免这种情况,我们需要用防抖来解决这个问题. What? 啥是防抖? 防抖debounce: 简单来说,防抖的效果就是在一定的时间间隔

  • JavaScript专题之underscore防抖实例学习

    目录 前言 防抖 第一版 this event 对象 返回值 立刻执行 取消 JavaScript 专题系列第一篇,讲解防抖,带你从零实现一个 underscore 的 debounce 函数 前言 在前端开发中会遇到一些频繁的事件触发,比如: window 的 resize.scroll mousedown.mousemove keyup.keydown…… 为此,我们举个示例代码来了解事件如何频繁的触发: 我们写个 index.html 文件: <!DOCTYPE html> <ht

  • Javascript查询DBpedia小应用实例学习

    Javascript查询DBpedia小应用 上一篇文章中,我们了解了SPARQL.SPARQL Endpoint,并简单做了一个SPARQLWrapper.js,来从DBpedia中查询了一些数据. 本文则尝试利用SPARQLWrapper.js来读取DBpedia的数据,并显示出来. 目标 通过输入一个英文单词,然后返回WIKI中相关的信息. 那么主要要解决的问题就是怎样的SPARQL语句能够查询到我们需要的东西. 先看一段简单的SPARQL查询语句 复制代码 代码如下: PREFIX :

  • JavaScript之数组扁平化详解

    扁平化 数组的扁平化,就是将一个嵌套多层的数组 array (嵌套可以是任何层数)转换为只有一层的数组. 举个例子,假设有个名为 flatten 的函数可以做到数组扁平化,效果就会如下: var arr = [1, [2, [3, 4]]]; console.log(flatten(arr)) // [1, 2, 3, 4] 知道了效果是什么样的了,我们可以去尝试着写这个 flatten 函数了 递归 我们最一开始能想到的莫过于循环数组元素,如果还是一个数组,就递归调用该方法: // 方法 1

  • JavaScript的Backbone.js框架入门学习指引

    1.简介 最近在做一个大型网上银行项目前端的优化,需要使用一个胖客户端的优化,大概思路就是前端通过Ajax 请求去后端获取数据,以Jason的格式返回,然后显示在页面上.由于这个系统非常庞大,胖客户端方案难免需要在客户端写大量的JS代码.我想对于任何团队来说,大量的,非结构化的代码维护起来都非常的不方便.所以BackBone进入了我的视线. 它提供了一种途径可以让你结构化你的JS代码,让你以面向对象的方式来组织你的前端JS代码.这就好比我们在前端应用Domain Driven Design. 我

  • 每个 JavaScript 工程师都应懂的33个概念

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项目地址在这里. 由于原版资源都要翻墙,所以本人创立一个中文版,附上关于这些概念在国内的一些文章和视频. 若有觉得更好的文章或者视频,可以贡献出来,觉得有误的,请联系我删除. 若有觉得更好的文章或者视频,可以贡献出来,觉得有误的,请联系我删除. 文章的排序优化,前面的文章是介绍概念,后面的文章是深入解

  • 利用jquery实现瀑布流3种案例

    一.瀑布流是我们常见的案例,这里主要讲述,用jquery的方式实现瀑布流的功能! 引言:我们经常见到很多网站的瀑布流功能,如淘宝.京东这些商品等等.. 实现它我们首先考虑几个问题:1.获取到数据 2.排列的方式 3.如何实现排列 其实,在瀑布流里有个核心的功能就是用到了绝对定位 我们逐步深入分析: 这是html的布局,css布局可以自己设置,只要保证grid盒子有绝对定位,其父元素有相对定位即可!后面有参考代码 下面是script部分 这种方式,可以通过for循环按照顺序获取图片的索引值,然后不

  • JS数组及对象遍历方法代码汇总

    文中的范例基于以下数组和对象. var arrTmp = ["value1","value2","value3"]; var objTmp = { aa:"value1", bb:"value2", cc:function(){ console.log("value3") } } 一.JS原生方法 1. javascript遍历的常用的遍历方法是for循环和for-in,ES5的时候加上

  • javascript防抖函数debounce详解

    定义及解读 防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次.假如我们设置了一个等待时间 3 秒的函数,在这 3 秒内如果遇到函数调用请求就重新计时 3 秒,直至新的 3 秒内没有函数调用请求,此时执行函数,不然就以此类推重新计时. 举一个小例子:假定在做公交车时,司机需等待最后一个人进入后再关门,每次新进一个人,司机就会把计时器清零并重新开始计时,重新等待 1 分钟再关门,如果后续 1 分钟内都没有乘客上车,司机会认为乘客都上来了,将关门发车. 此

  • 浅谈JavaScript节流和防抖函数

    概念 节流函数 间隔固定的时间执行传入的方法 目的是防止函数执行的频率过快,影响性能.常见于跟滚动,鼠标移动事件绑定的功能. 防抖函数 对于接触过硬件的人也许更好理解,硬件按钮按下时,由于用户按住时间的长短不一,会多次触发电流的波动,加一个防抖函数就会只触发一次,防止了无意义的电流波动引起的问题. 按键防反跳(Debounce)为什么要去抖动呢?机械按键在按下时,并非按下就接触的很好,尤其是有簧片的机械开关,会在接触的瞬间反复的开合多次,直到开关状态完全改变. 应用在前端时,常见的场景是,输入框

随机推荐