JavaScript专题系列-防抖和节流

栏目: JavaScript · 发布时间: 5年前

内容简介:一般来说,这一段主要是讲一些知识的大体概况,都不是那么重要的,相当于文章的摘要。但是就是有不同寻常的,比如本文对于防抖以及节流的概念理解就很重要,非常重要。首先需要指出的是为什么会出现这2种思想。1.由于肉眼只能分辨出一定频率的变化,也就是说一种变化1s内变化1000次和变成60次对人的感官是一样的,同理,可以类推到js代码。在一定时间内,代码执行的次数不一定要非常多。达到一定频率就足够了。因为跑得越多,带来的效果也是一样。

一般来说,这一段主要是讲一些知识的大体概况,都不是那么重要的,相当于文章的摘要。但是就是有不同寻常的,比如本文对于防抖以及节流的概念理解就很重要,非常重要。

1.1 出现原因

首先需要指出的是为什么会出现这2种思想。

1.由于肉眼只能分辨出一定频率的变化,也就是说一种变化1s内变化1000次和变成60次对人的感官是一样的,同理,可以类推到js代码。在一定时间内,代码执行的次数不一定要非常多。达到一定频率就足够了。因为跑得越多,带来的效果也是一样。

2.客户端的性能问题。众所周知,就目前来说兼容对应前端来说还是相当重要的,而主要的兼容点在于低端机型,所以说我们有必要把js代码的执行次数控制在合理的范围。既能节省浏览器CPU资源,又能让页面浏览更加顺畅,不会因为js的执行而发生卡顿。

以上就是函数节流和函数防抖出现的主要原因。

1.2 概念理解

上面说了那么多,只是为了说明为什么会出现防抖和节流这2种实现,下面再来形象理解一下这两种思想的不同之处,很多时候我都会把这两种思想混淆,所以这次特意想了很好记住的办法。

1. 函数节流 是指一定时间内js方法只跑一次。

节流节流就是 节省水流的意思 ,就想水龙头在流水,我们可以手动让水流(在一定时间内)小一点,但是他会一直在流。

当然还有一个形象的比喻,开源节流,就比如我们这个月(在一定时间内)我们少花一点钱,但是我们每天还是都需要花钱的。

2. 函数防抖 只有足够的空闲时间,才执行代码一次。

比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。(其实只要记住了节流的思想就能通过排除法判断节流和防抖了)

2.代码

2.1 防抖

上面的解释都是为了形象生动地说明防抖和节流的思想以及区别,现在我们需要从代码层面来进一步探索防抖。

首先写代码之前最重要的事情就是想在脑子里面想这段代码需要实现什么逻辑,下面就是防抖的代码逻辑思路。

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

好了,根据上面的思路我们可以很轻松地写出第一版防抖的代码。

function debounce(func, waitTime) {
  var timeout;
  return function () {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(func, waitTime);
  }
}
document.querySelector('#app').onmousemove = debounce(fn, 1000);
复制代码

上面的一小段代码就是最原始的防抖代码。

可以看到上面这几行代码就用到了闭包的知识,主要的目的就是为了在函数执行后保留timeout这个变量。

想让一个函数执行完后,函数内的某个变量(timer)仍旧保留,就可以使用闭包把要保存的变量在父作用域声明,其他的语句放到子作用域里,并且作为一个function返回。下面的很大实例代码都用到了闭包来解决保留变量的问题。

还有一点也许有小伙伴会有疑惑。为什么这里要返回一个函数呢。其实很好理解,我们可以来看下面的代码

var timeout;
function debounce(func, waitTime) {
  if (timeout) {
    clearTimeout(timeout);
  }
  timeout = setTimeout(func, waitTime);
}
container.onmousemove = debounce(getUserAction, 1000);
复制代码

我手动删掉了debounce函数里面的return ,然后为了保留timeout,我把它放到了全局变量,这几行代码看起来和上面的很像,但是你可以直接跑一下这段代码,发现debounce只会执行一次!!!

哈哈哈,其实之所以在debounce函数里面返回一个函数,那是因为onmousemove需要的是绑定的 函数 ,我们的测试代码执行一遍后只会返回undefined ,相当于

container.onmousemove = debounce(getUserAction, 1000);
container.onmousemove = undefined;
复制代码

当然就没有正确绑定事件了。如果从好理解的角度来写,其实也是可以想下面这样绑定的

var timeout;
function debounce(func, waitTime) {
  if (timeout) {
    clearTimeout(timeout);
  }
  timeout = setTimeout(func, waitTime);
}
container.onmousemove = () => {
  debounce(getUserAction, 1000);
}
复制代码

下面所有方法的道理都是和第一个函数一样的。

但是这一版本的代码我们在fn中打印this以及event对象,发现有点不对。

JavaScript专题系列-防抖和节流

可以从上图中看到,fn中的this以及event对象,发现并不是希望的,所以我们需要手动把this以及event对象传递给fn函数。于是乎有了下面第二版的防抖函数。

function debounce(func, waitTime) {
  var timeout;
  return function () {
    var context = this,
        args = arguments;
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(function () {
      func.apply(context, args)
    },  waitTime);
  }
}
复制代码

其实也就是用了apply函数把this以及event对象传递给fn函数。

2.2 节流

下面让我们继续来看一下节流思想的代码逻辑。

使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 ),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。

ok,根据上面的逻辑,我们可以很轻松写出第一版节流函数。

function throttle(func, waitTime) {
    var context,
        args,
        previous = 0;
    return function() {
        var now = + new Date();
            context = this;
            args = arguments;
        if (now - previous > waitTime) {
            func.apply(context, args);
            previous = now;
        }
    }
}
复制代码

或者我们其实还可以借助定时器来实现节流。

当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。

function throttle(func, waitTime) {
    var timeout,
        previous = 0;
    return function() {
        context = this;
        args = arguments;
        if (!timeout) {
            timeout = setTimeout(function(){
                timeout = null;
                func.apply(context, args)
            }, waitTime)
        }

    }
}
复制代码

3. 知识转为技能

2018年,我最大的感悟就是尽量把所学的知识转为技能(来自老姚 [ juejin.im/post/5c34ab… ](2018年收获5条认知,条条振聋发聩 | 掘金年度征文))

知识是可以学到的,但是技能只能习得。

上面两部分我们都是在学防抖和节流出现的原因,对应的概念以及实现的思想逻辑,这些都是知识,现在就让我们一起把学到的知识转为技能,争取成为自己项目的一部分吧。

对于像防抖和节流这种 工具 性质的函数,我们大可以把他们放在公共文件里面,然后在需要的地方直接调用就可以了。

防抖和节流最大的核心用处在于优化代码性能,可以用在很多地方,比如输入框的验证,图片懒加载,各种频繁触发的DOM事件等等。

下面是我自己模拟写了一个百度搜索的按钮精灵,图一是没有用防抖搜索 我是 这个关键词发现发起了N多次请求,然后改了一行代码加入了防抖,请求的情况就变成了图二。效果显而易见。

this.debounce(this.getData, 1000)();
复制代码
JavaScript专题系列-防抖和节流
JavaScript专题系列-防抖和节流

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Node.js硬实战:115个核心技巧

Node.js硬实战:115个核心技巧

【美】Alex R. Young、【美】Marc Harter / 承竹、慕陶、邱娟、达峰 / 电子工业出版社 / 2017-1 / 109.9

《Node.js 硬实战:115 个核心技巧》是一本面向实战的Node.js 开发进阶指南。作为资深专家,《Node.js 硬实战:115 个核心技巧》作者独辟蹊径,将着眼点放在Node.js 的核心模块和网络应用,通过精心组织的丰富实例,向读者充分展示了Node.js 强大的并发处理能力,读者从中可真正掌握Node 的核心基础与高级技巧。《Node.js 硬实战:115 个核心技巧》总共有三部分......一起来看看 《Node.js硬实战:115个核心技巧》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具