如何将手写Spirit防抖和lodash节流函数改写成长尾?

2026-04-03 06:311阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计3961个文字,预计阅读时间需要16分钟。

如何将手写Spirit防抖和lodash节流函数改写成长尾?

目录+前言+防抖函数和节流函数的区别+防抖函数的实现+基本实现+立即执行+取消功能+返回结果+节流函数的实现+基本实现+leading实现+trailing实现+取消功能和返回结果+前言+防抖函数和节流函数的区别+防抖函数的实现+基本实现+立即执行+取消功能+返回结果+节流函数的实现+基本实现+leading实现+trailing实现+取消功能和返回结果

目录
  • 前言
  • 防抖函数和节流函数的区别
  • 防抖函数的实现
    • 基本实现
    • 立即执行
    • 取消功能
    • 返回结果
  • 节流函数的实现
    • 基本实现
    • leading实现
    • trailing实现
  • 取消功能和返回结果

    前言

    防抖函数和节流函数,无论是写业务的时候还是面试的时候,想必大家已经听过很多次了吧.但是大家在用到的时候,有了解过他们之间的区别嘛,他们是如何实现的呢?还是说只是简单的调用下像lodashunderscore这种第三方库提供给我们的节流和防抖函数呢?

    如何将手写Spirit防抖和lodash节流函数改写成长尾?

    防抖函数和节流函数的区别

    防抖函数:是指触发了一个事件,在规定的时间内,如果没有第二次事件被触发,那么他就会执行.换句话讲,就是说,如果不断有事件被触发,那么规定的执行时间将会被不断推迟

    节流函数:指的是在规定时间内,你无论触发多少次事件,你也只会执行一次.我举个生活中的例子,就很好理解了.王者荣耀这个游戏可能很多人多玩过,每个英雄都有自己的技能,在我们点击一次后,该技能会进入冷却时间,即使我们点的再快,该技能在冷却时间好之前也只能触发一次(我们第一次点击的时候)

    防抖函数的实现

    我将实现防抖函数的四个功能,希望大家能一步步的跟着来,循序渐进,相信大家一定会有收获的

    基本实现

    我们可以想下,要想一个事件在规定时间过后执行,在JS中该怎么实现

    好 时间到

    定时器,小伙伴们肯定都知道的吧

    触发事件,在一定时间后执行,这个可以使用定时器解决了.

    那么 接下来还有一个问题 在触发事件后,再触发事件,该如何让他推迟执行呢?

    如果规定时间内,再触发的话,我们就把之前创建的定时器删除不就好了,对不对?

    这样是不是就解决了我们的问题,好,我们现在来写下代码,怕大家有点不明白

    function debounce(fn, delay) { //定义一个定时器 let timer = null; // 每次触发的时候 清空上一次的定时器 const _debounce = function () { if (timer) clearTimeout(timer); //根据传进来的延时 执行 timer = setTimeout(() => { fn(); }, delay) } return _debounce; }

    这段代码还是比较容易的吧,相信小伙伴们肯定都懂了

    但是这段代码还是有点问题,我们来调用下第三方库的underscore的防抖函数

    <body> <button>取消</button> <input type="text"> <script src="cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-umd-min.js"></script> <script> const btn = document.querySelector("button"); const input = document.querySelector("input"); let count = 0; function test(event) { // 注意这里的this 和 event console.log(`发送${++count}网络请求`, this, event); return "我是返回结果"; } input.oninput = _.debounce(test, 2000); </script> </body>

    我们打开浏览器调试,看下输出结果

    可以看到this和Event输出是没有任何问题的.

    再来看看我们的输出

    你会发现 this是window了 而Event是undefined.

    这是为什么呢?

    这是因为 我们写的代码没有对this进行一个绑定,同时也没有将DOM元素的event接收

    fn()直接执行 这时候的this是直接指向window的

    function debounce(fn, delay) { let timer = null; //使用剩余参数接收所有的参数 DOM在调用这个函数的时候,我们就能接收到event了 const _debounce = function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { //注意 我们使用apply进行一个换绑,绑到执行这个的DOM元素上 fn.apply(this,args); }, delay) } return _debounce; }

    至此,我们这个防抖函数的基本实现就没有任何问题了

    看到这里的小伙伴们,你们真不错

    这个基本实现 拿来应付面试已经够了,接下来我们还有一些额外的功能要实现,想看的可以继续往下看了,现在不想看的也可以收藏下,以后来看.

    立即执行

    在某些应用场景下,比如搜索的时候,你输入第一个字符的时候,他就会联想出一系列的字符,他不会等待一段时间后再去执行,而是会立马执行,我们接下来实现下这个功能

    首先,立即执行这个功能,我们可以将它交给用户来决定是不是要使用这个功能

    debounce(fn,delay,immediate=false)

    我们以参数的形式传递,默认是关闭的

    好,我们现在来看下代码实现

    function debounce(fn, delay, immediate = false) { let timer = null; //代码规范 我们最好不要修改 用户传递进来的参数 //所以我们在下面声明了一个变量 用于控制 let isInvoke = false; const _debounce = function (...args) { if (timer) clearTimeout(timer); //如果immdiate为true //isInvoke取反为true if (immediate && !isInvoke) { //会立马执行一次 fn.apply(this, args); //同时将isInvoke设置为true,防止下次触发的时候 又再次触发立即执行 isInvoke = true; } else { //第一次触发结束立即执行后 //isInvoke会限定在定时器中 输入结束后 才会重新刷新isInvoke timer = setTimeout(() => { //剩下的操作在规定时间内 会等待定时器结束 fn.apply(this, args); //同时重新刷新inInvoke isInvoke = false; }, delay) } } return _debounce; }

    好,这一块还是比较简单的吧,相比大家应该看懂了,如果有什么不懂的地方,欢迎评论区留言,我看见了就会回答的

    那么我们开始下一个篇章的 如果用户输入之后 不想让他请求呢 这时候我们就需要一个取消功能了,对,我们接下来就是要实现取消功能了

    取消功能

    我们该如何在剩余的时间内取消请求呢?

    对 没错! 清空定时器

    我们只需要在我们返回的函数上加个静态方法 给用户提供个取消功能即可

    我们来看下代码实现

    // 给返回的这个函数添加一个静态方法 用于取消请求 _debounce.cancel = function () { if (timer) clearTimeout(timer); }

    是不是很简单呢? 就这么简单一行代码 取消功能就完成了

    好,我们还有最后一个功能需要实现的 那就是如果开发者想要拿到请求后的返回结果的话,我们现阶段的防抖函数能不能做到呢? 貌似不行吧?

    所以接下来,我们来实现最后一个功能 取到返回结果

    返回结果

    我们思考一个问题 返回结果在哪呢?

    用户传递一个函数给我们 返回一个新的函数

    那么返回结果一定是在用户传递给我们的那个函数上的

    所以关键就是 将用户的那个函数的返回结果传递出来

    现在 我们这里有两个方案

    • 回调函数
    • Promise

    我们先来看下回调函数的版本

    // 回调函数版本 function debounce(fn, delay, immediate = false, resultCallBack) { let timer = null; let isInvoke = false; let result = null; const _debounce = function (...args) { if (timer) clearTimeout(timer); if (immediate && !isInvoke) { //接收结果 result = fn.apply(this, args); resultCallBack(result); isInvoke = true; } else { timer = setTimeout(() => { //接收结果 result = fn.apply(this, args); resultCallBack(result); isInvoke = false; }, delay) } } _debounce.cancel = function () { if (timer) clearTimeout(timer); timer = null; isInvoke = false; } return _debounce; }

    实际应用

    const _debounce = () => { debounce(test, 1000)().then(res => { console.log(res); }) } input.oninput = _debounce;

    回调函数的是不是比较简单? 我们来看下Promise版本的 在实际应用的时候要注意一些坑

    function debounce(fn, delay, immediate = false) { let timer = null; let isInvoke = false; let result = null; const _debounce = function (...args) { //在返回的函数中 直接整体返回一个Promsie对象 //将结果传入 resolve中 return new Promise((resolve, rejected) => { if (timer) clearTimeout(timer); if (immediate && !isInvoke) { result = fn.apply(this, args); resolve(result) isInvoke = true; } else { timer = setTimeout(() => { result = fn.apply(this, args); resolve(result); isInvoke = false; }, delay) } }) } _debounce.cancel = function () { if (timer) clearTimeout(timer); timer = null; isInvoke = false; } return _debounce; }

    实际调用

    const _debounce = function(...args){ debounce(test, 1000).apply(this,args).then(res => { console.log(res); }) }; input.oninput = _debounce;

    注意到了吧 我们对原来的函数又封装了一层 因为只有这样才能拿到promise的结果

    同时this和event不会出问题

    看到这里的小伙伴们真棒,相信你们防抖函数一定没问题了,待会我们就开始讲解 节流函数该如何实现

    节流函数的实现

    节流函数我们也是从几个方面逐步实现,带着大家一步步的解开节流函数的面纱.

    大家可以想下,节流函数该如何实现.

    一段时间内,只会触发一次操作,后续的操作就不会被触发.

    我们可以拿到当前的时间戳 来进行计算

    我们直接通过代码来讲吧 比较方便讲

    function throttle(fn, interval) { let lastTime = 0; const _throttle = function () { //首先拿到当前的时间 const nowTime = new Date().getTime(); //传递进来的时间间隔 用当前的时间减去上一次触发的时间 //得到最新的剩余时间 const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn(); //如果剩余时间小于0 说明已经达到一个间隔 //并且将现在的时间赋值给lastTime //在时间间隔内 这样无论执行多少次 都只会执行第一次的操作 //因为第一次的lastTime是0 而nowTime是比较大的 //减去之后一定是个负数 所以会执行第一次 //而不会执行后续的操作 lastTime = nowTime; } } return _throttle; }

    大家看下我上面这段代码 还是比较好理解的吧,面试的话能够写出这一部分已经很可以了,但是要想更加出彩,能和面试官多唠会的吧,我们接着看下面的实现

    leading实现

    我们在基本实现中,其实已经将这个功能已经实现了,但是并不是可以控制的,我们这个实现是将是否首次触发交给用户来决定,大家可以想下该如何实现

    基本实现中,我们是如何实现第一次触发的?

    是不是通过拿到的时间戳非常大,而lastTime为0所导致的呢?

    所以我们是不是可以让lastTime也能拿到当前的时间戳呢,这样子, nowTime和lastTime相减的时候,是不是就不会变成负数呢?

    代码实现

    // 考虑到 我们后面会有很多功能要实现 //所以我们使用选项来进行配置.避免造成更多参数 function throttle(fn, interval, option = { leading: true }) { let lastTime = 0; const { leading } = option; const _throttle = function () { const nowTime = new Date().getTime(); //在 leading和lastTime为false的情况下 //就将nowTime赋值给lastTime,这样就不会在第一次就执行操作了 if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn(); lastTime = nowTime; } } return _throttle; }

    大家是不是理解了呢? 我个人认为还是比较好懂的吧,不懂的可以在评论区留言,我看到就会给大家解答的

    接下来,我们看下和这个情况相反的一种状况,如果我们想要在最后一次操作的时候进行一次触发操作的话,该如何去做呢?

    trailing实现

    这一块是比较难的部分了,会有点难,大家不懂的话,要多看几遍,实在有不明白的,欢迎评论区留言

    首先最后一次触发操作时,我们该怎么样让它执行?

    我提供一个思路,当我们最后一次触发操作的时候,拿到距离间隔还有多少时间结束,加上一个定时器,让他根据这个剩余时间去按时执行

    代码实现

    function throttle(fn, interval, option = { leading: true, tralling: false }) { let lastTime = 0; let timer = null; const { leading, tralling } = option; const _throttle = function (...args) { const nowTime = new Date().getTime(); if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn.apply(this, args); lastTime = nowTime; if (timer) { clearTimeout(timer) timer = null; } // 如果执行了这一部分 那么后面的tralling就没有必要去执行 // 说明刚好执行到了这一步 后面的最后按下 就不需要 return; } if (tralling && !timer) { timer = setTimeout(() => { timer = null; /** ` * 首先 按下第一次的时候 这个定时器已经被加上了 * 每次进来的时候 等待一定时间 定时器会被置空 方便下次使用 * 根据剩余时间 来判断执行 * 如果leading为false lastTime会被设置为0 会在规定的剩余时间到达后 去执行这个函数 而remianTime那个部分就不会被执行 因为remainTime会一直保持在一个正数状态 * 如果leading为true lastTime会被设置为当前的时间 这样在下一次的操作下,remainTime才会发生变化 * */ lastTime = !leading ? 0 : new Date().getTime(); fn.apply(this, args); }, reamainTime) } } return _throttle; }

    是不是比较难懂呢? 我在来解释一下啊

    首先如果remainTime已经小于0了,那么fn就会去执行,我们也就不需要去执行后续的操作了 会直接返回

    那么如果remainTime没有小于0,我们会设置定时器,在定时器内部,我们需要先把timer清空,防止下一次触发的时候又触发了.

    其次,我们要将lastTime进行一个处理

    如果我们之前设置的leading是false的话,那么我们需要将lastTime置为0,这样在下一次的触发操作的时候,才能触发leading为false的情况下的逻辑语句

    leading为true的情况下,需要将lastTime设置为当前的时间戳,这样在下一次的操作的时候,才会remainTime才会发生变化,逻辑才能执行下去.

    大家有没有听明白呢? 可能是会有点难懂,但是好好多看几遍,还是能够理解的我相信!!!

    接下来的操作就比较简单了,大家可以安心食用,和防抖函数一样,是取消功能和返回结果

    取消功能和返回结果

    因为这个和防抖函数是一样的,所以我这里直接就放代码了

    function throttle(fn, interval, option = { leading: true, tralling: false, resultCallback }) { let lastTime = 0; let timer = null; let result = null; const { leading, tralling, resultCallback } = option; // 两种结果回调 //和防抖函数是一样的 //1. 通过传递一个回调函数 //2. 通过promise 进行结果回调 const _throttle = function (...args) { return new Promise((resolve, reject) => { const nowTime = new Date().getTime(); if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { result = fn.apply(this, args); resultCallback(result); resolve(result); lastTime = nowTime; if (timer) { clearTimeout(timer) timer = null; } return; } if (tralling && !timer) { timer = setTimeout(() => { timer = null; lastTime = !leading ? 0 : new Date().getTime(); result = fn.apply(this, args); resultCallback(result); resolve(result); }, reamainTime) } }) } //取消功能 _throttle.cancel = function () { if (timer) clearTimeout(timer); timer = null; lastTime = 0; } return _throttle; }

    大家可以看下是不是一摸一样的呢? 非常轻松吧

    以上就是手写Spirit防抖函数underscore和节流函数lodash的详细内容,更多关于pirit防抖函数underscore节流函数lodash的资料请关注自由互联其它相关文章!

    本文共计3961个文字,预计阅读时间需要16分钟。

    如何将手写Spirit防抖和lodash节流函数改写成长尾?

    目录+前言+防抖函数和节流函数的区别+防抖函数的实现+基本实现+立即执行+取消功能+返回结果+节流函数的实现+基本实现+leading实现+trailing实现+取消功能和返回结果+前言+防抖函数和节流函数的区别+防抖函数的实现+基本实现+立即执行+取消功能+返回结果+节流函数的实现+基本实现+leading实现+trailing实现+取消功能和返回结果

    目录
    • 前言
    • 防抖函数和节流函数的区别
    • 防抖函数的实现
      • 基本实现
      • 立即执行
      • 取消功能
      • 返回结果
    • 节流函数的实现
      • 基本实现
      • leading实现
      • trailing实现
    • 取消功能和返回结果

      前言

      防抖函数和节流函数,无论是写业务的时候还是面试的时候,想必大家已经听过很多次了吧.但是大家在用到的时候,有了解过他们之间的区别嘛,他们是如何实现的呢?还是说只是简单的调用下像lodashunderscore这种第三方库提供给我们的节流和防抖函数呢?

      如何将手写Spirit防抖和lodash节流函数改写成长尾?

      防抖函数和节流函数的区别

      防抖函数:是指触发了一个事件,在规定的时间内,如果没有第二次事件被触发,那么他就会执行.换句话讲,就是说,如果不断有事件被触发,那么规定的执行时间将会被不断推迟

      节流函数:指的是在规定时间内,你无论触发多少次事件,你也只会执行一次.我举个生活中的例子,就很好理解了.王者荣耀这个游戏可能很多人多玩过,每个英雄都有自己的技能,在我们点击一次后,该技能会进入冷却时间,即使我们点的再快,该技能在冷却时间好之前也只能触发一次(我们第一次点击的时候)

      防抖函数的实现

      我将实现防抖函数的四个功能,希望大家能一步步的跟着来,循序渐进,相信大家一定会有收获的

      基本实现

      我们可以想下,要想一个事件在规定时间过后执行,在JS中该怎么实现

      好 时间到

      定时器,小伙伴们肯定都知道的吧

      触发事件,在一定时间后执行,这个可以使用定时器解决了.

      那么 接下来还有一个问题 在触发事件后,再触发事件,该如何让他推迟执行呢?

      如果规定时间内,再触发的话,我们就把之前创建的定时器删除不就好了,对不对?

      这样是不是就解决了我们的问题,好,我们现在来写下代码,怕大家有点不明白

      function debounce(fn, delay) { //定义一个定时器 let timer = null; // 每次触发的时候 清空上一次的定时器 const _debounce = function () { if (timer) clearTimeout(timer); //根据传进来的延时 执行 timer = setTimeout(() => { fn(); }, delay) } return _debounce; }

      这段代码还是比较容易的吧,相信小伙伴们肯定都懂了

      但是这段代码还是有点问题,我们来调用下第三方库的underscore的防抖函数

      <body> <button>取消</button> <input type="text"> <script src="cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-umd-min.js"></script> <script> const btn = document.querySelector("button"); const input = document.querySelector("input"); let count = 0; function test(event) { // 注意这里的this 和 event console.log(`发送${++count}网络请求`, this, event); return "我是返回结果"; } input.oninput = _.debounce(test, 2000); </script> </body>

      我们打开浏览器调试,看下输出结果

      可以看到this和Event输出是没有任何问题的.

      再来看看我们的输出

      你会发现 this是window了 而Event是undefined.

      这是为什么呢?

      这是因为 我们写的代码没有对this进行一个绑定,同时也没有将DOM元素的event接收

      fn()直接执行 这时候的this是直接指向window的

      function debounce(fn, delay) { let timer = null; //使用剩余参数接收所有的参数 DOM在调用这个函数的时候,我们就能接收到event了 const _debounce = function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { //注意 我们使用apply进行一个换绑,绑到执行这个的DOM元素上 fn.apply(this,args); }, delay) } return _debounce; }

      至此,我们这个防抖函数的基本实现就没有任何问题了

      看到这里的小伙伴们,你们真不错

      这个基本实现 拿来应付面试已经够了,接下来我们还有一些额外的功能要实现,想看的可以继续往下看了,现在不想看的也可以收藏下,以后来看.

      立即执行

      在某些应用场景下,比如搜索的时候,你输入第一个字符的时候,他就会联想出一系列的字符,他不会等待一段时间后再去执行,而是会立马执行,我们接下来实现下这个功能

      首先,立即执行这个功能,我们可以将它交给用户来决定是不是要使用这个功能

      debounce(fn,delay,immediate=false)

      我们以参数的形式传递,默认是关闭的

      好,我们现在来看下代码实现

      function debounce(fn, delay, immediate = false) { let timer = null; //代码规范 我们最好不要修改 用户传递进来的参数 //所以我们在下面声明了一个变量 用于控制 let isInvoke = false; const _debounce = function (...args) { if (timer) clearTimeout(timer); //如果immdiate为true //isInvoke取反为true if (immediate && !isInvoke) { //会立马执行一次 fn.apply(this, args); //同时将isInvoke设置为true,防止下次触发的时候 又再次触发立即执行 isInvoke = true; } else { //第一次触发结束立即执行后 //isInvoke会限定在定时器中 输入结束后 才会重新刷新isInvoke timer = setTimeout(() => { //剩下的操作在规定时间内 会等待定时器结束 fn.apply(this, args); //同时重新刷新inInvoke isInvoke = false; }, delay) } } return _debounce; }

      好,这一块还是比较简单的吧,相比大家应该看懂了,如果有什么不懂的地方,欢迎评论区留言,我看见了就会回答的

      那么我们开始下一个篇章的 如果用户输入之后 不想让他请求呢 这时候我们就需要一个取消功能了,对,我们接下来就是要实现取消功能了

      取消功能

      我们该如何在剩余的时间内取消请求呢?

      对 没错! 清空定时器

      我们只需要在我们返回的函数上加个静态方法 给用户提供个取消功能即可

      我们来看下代码实现

      // 给返回的这个函数添加一个静态方法 用于取消请求 _debounce.cancel = function () { if (timer) clearTimeout(timer); }

      是不是很简单呢? 就这么简单一行代码 取消功能就完成了

      好,我们还有最后一个功能需要实现的 那就是如果开发者想要拿到请求后的返回结果的话,我们现阶段的防抖函数能不能做到呢? 貌似不行吧?

      所以接下来,我们来实现最后一个功能 取到返回结果

      返回结果

      我们思考一个问题 返回结果在哪呢?

      用户传递一个函数给我们 返回一个新的函数

      那么返回结果一定是在用户传递给我们的那个函数上的

      所以关键就是 将用户的那个函数的返回结果传递出来

      现在 我们这里有两个方案

      • 回调函数
      • Promise

      我们先来看下回调函数的版本

      // 回调函数版本 function debounce(fn, delay, immediate = false, resultCallBack) { let timer = null; let isInvoke = false; let result = null; const _debounce = function (...args) { if (timer) clearTimeout(timer); if (immediate && !isInvoke) { //接收结果 result = fn.apply(this, args); resultCallBack(result); isInvoke = true; } else { timer = setTimeout(() => { //接收结果 result = fn.apply(this, args); resultCallBack(result); isInvoke = false; }, delay) } } _debounce.cancel = function () { if (timer) clearTimeout(timer); timer = null; isInvoke = false; } return _debounce; }

      实际应用

      const _debounce = () => { debounce(test, 1000)().then(res => { console.log(res); }) } input.oninput = _debounce;

      回调函数的是不是比较简单? 我们来看下Promise版本的 在实际应用的时候要注意一些坑

      function debounce(fn, delay, immediate = false) { let timer = null; let isInvoke = false; let result = null; const _debounce = function (...args) { //在返回的函数中 直接整体返回一个Promsie对象 //将结果传入 resolve中 return new Promise((resolve, rejected) => { if (timer) clearTimeout(timer); if (immediate && !isInvoke) { result = fn.apply(this, args); resolve(result) isInvoke = true; } else { timer = setTimeout(() => { result = fn.apply(this, args); resolve(result); isInvoke = false; }, delay) } }) } _debounce.cancel = function () { if (timer) clearTimeout(timer); timer = null; isInvoke = false; } return _debounce; }

      实际调用

      const _debounce = function(...args){ debounce(test, 1000).apply(this,args).then(res => { console.log(res); }) }; input.oninput = _debounce;

      注意到了吧 我们对原来的函数又封装了一层 因为只有这样才能拿到promise的结果

      同时this和event不会出问题

      看到这里的小伙伴们真棒,相信你们防抖函数一定没问题了,待会我们就开始讲解 节流函数该如何实现

      节流函数的实现

      节流函数我们也是从几个方面逐步实现,带着大家一步步的解开节流函数的面纱.

      大家可以想下,节流函数该如何实现.

      一段时间内,只会触发一次操作,后续的操作就不会被触发.

      我们可以拿到当前的时间戳 来进行计算

      我们直接通过代码来讲吧 比较方便讲

      function throttle(fn, interval) { let lastTime = 0; const _throttle = function () { //首先拿到当前的时间 const nowTime = new Date().getTime(); //传递进来的时间间隔 用当前的时间减去上一次触发的时间 //得到最新的剩余时间 const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn(); //如果剩余时间小于0 说明已经达到一个间隔 //并且将现在的时间赋值给lastTime //在时间间隔内 这样无论执行多少次 都只会执行第一次的操作 //因为第一次的lastTime是0 而nowTime是比较大的 //减去之后一定是个负数 所以会执行第一次 //而不会执行后续的操作 lastTime = nowTime; } } return _throttle; }

      大家看下我上面这段代码 还是比较好理解的吧,面试的话能够写出这一部分已经很可以了,但是要想更加出彩,能和面试官多唠会的吧,我们接着看下面的实现

      leading实现

      我们在基本实现中,其实已经将这个功能已经实现了,但是并不是可以控制的,我们这个实现是将是否首次触发交给用户来决定,大家可以想下该如何实现

      基本实现中,我们是如何实现第一次触发的?

      是不是通过拿到的时间戳非常大,而lastTime为0所导致的呢?

      所以我们是不是可以让lastTime也能拿到当前的时间戳呢,这样子, nowTime和lastTime相减的时候,是不是就不会变成负数呢?

      代码实现

      // 考虑到 我们后面会有很多功能要实现 //所以我们使用选项来进行配置.避免造成更多参数 function throttle(fn, interval, option = { leading: true }) { let lastTime = 0; const { leading } = option; const _throttle = function () { const nowTime = new Date().getTime(); //在 leading和lastTime为false的情况下 //就将nowTime赋值给lastTime,这样就不会在第一次就执行操作了 if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn(); lastTime = nowTime; } } return _throttle; }

      大家是不是理解了呢? 我个人认为还是比较好懂的吧,不懂的可以在评论区留言,我看到就会给大家解答的

      接下来,我们看下和这个情况相反的一种状况,如果我们想要在最后一次操作的时候进行一次触发操作的话,该如何去做呢?

      trailing实现

      这一块是比较难的部分了,会有点难,大家不懂的话,要多看几遍,实在有不明白的,欢迎评论区留言

      首先最后一次触发操作时,我们该怎么样让它执行?

      我提供一个思路,当我们最后一次触发操作的时候,拿到距离间隔还有多少时间结束,加上一个定时器,让他根据这个剩余时间去按时执行

      代码实现

      function throttle(fn, interval, option = { leading: true, tralling: false }) { let lastTime = 0; let timer = null; const { leading, tralling } = option; const _throttle = function (...args) { const nowTime = new Date().getTime(); if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { fn.apply(this, args); lastTime = nowTime; if (timer) { clearTimeout(timer) timer = null; } // 如果执行了这一部分 那么后面的tralling就没有必要去执行 // 说明刚好执行到了这一步 后面的最后按下 就不需要 return; } if (tralling && !timer) { timer = setTimeout(() => { timer = null; /** ` * 首先 按下第一次的时候 这个定时器已经被加上了 * 每次进来的时候 等待一定时间 定时器会被置空 方便下次使用 * 根据剩余时间 来判断执行 * 如果leading为false lastTime会被设置为0 会在规定的剩余时间到达后 去执行这个函数 而remianTime那个部分就不会被执行 因为remainTime会一直保持在一个正数状态 * 如果leading为true lastTime会被设置为当前的时间 这样在下一次的操作下,remainTime才会发生变化 * */ lastTime = !leading ? 0 : new Date().getTime(); fn.apply(this, args); }, reamainTime) } } return _throttle; }

      是不是比较难懂呢? 我在来解释一下啊

      首先如果remainTime已经小于0了,那么fn就会去执行,我们也就不需要去执行后续的操作了 会直接返回

      那么如果remainTime没有小于0,我们会设置定时器,在定时器内部,我们需要先把timer清空,防止下一次触发的时候又触发了.

      其次,我们要将lastTime进行一个处理

      如果我们之前设置的leading是false的话,那么我们需要将lastTime置为0,这样在下一次的触发操作的时候,才能触发leading为false的情况下的逻辑语句

      leading为true的情况下,需要将lastTime设置为当前的时间戳,这样在下一次的操作的时候,才会remainTime才会发生变化,逻辑才能执行下去.

      大家有没有听明白呢? 可能是会有点难懂,但是好好多看几遍,还是能够理解的我相信!!!

      接下来的操作就比较简单了,大家可以安心食用,和防抖函数一样,是取消功能和返回结果

      取消功能和返回结果

      因为这个和防抖函数是一样的,所以我这里直接就放代码了

      function throttle(fn, interval, option = { leading: true, tralling: false, resultCallback }) { let lastTime = 0; let timer = null; let result = null; const { leading, tralling, resultCallback } = option; // 两种结果回调 //和防抖函数是一样的 //1. 通过传递一个回调函数 //2. 通过promise 进行结果回调 const _throttle = function (...args) { return new Promise((resolve, reject) => { const nowTime = new Date().getTime(); if (!leading && !lastTime) lastTime = nowTime; const reamainTime = interval - (nowTime - lastTime); if (reamainTime <= 0) { result = fn.apply(this, args); resultCallback(result); resolve(result); lastTime = nowTime; if (timer) { clearTimeout(timer) timer = null; } return; } if (tralling && !timer) { timer = setTimeout(() => { timer = null; lastTime = !leading ? 0 : new Date().getTime(); result = fn.apply(this, args); resultCallback(result); resolve(result); }, reamainTime) } }) } //取消功能 _throttle.cancel = function () { if (timer) clearTimeout(timer); timer = null; lastTime = 0; } return _throttle; }

      大家可以看下是不是一摸一样的呢? 非常轻松吧

      以上就是手写Spirit防抖函数underscore和节流函数lodash的详细内容,更多关于pirit防抖函数underscore节流函数lodash的资料请关注自由互联其它相关文章!