Giter VIP home page Giter VIP logo

Comments (242)

wcflmy avatar wcflmy commented on May 2, 2024 58

建议将第四版和第五版换个顺序,在第四版中,由于func始终是异步执行的,return result返回一直是undefined,只有在第五版中immediate参数为true的情况下,result才会取到结果,所以建议换个顺序更加严谨一些。

from blog.

xxxgitone avatar xxxgitone commented on May 2, 2024 56

谢谢您的文章,最近想梳理自己的知识,但是却不知道从哪里入手好,跟着您的文章走真的是事半功倍啊,很多以前的疑点豁然开朗,再次谢谢!

from blog.

hujiulong avatar hujiulong commented on May 2, 2024 55

写得很好,在做动画时也经常用到这种方式,防止在一帧时间中(大概16ms)渲染多次。

function debounce(func) {
    var t;
    return function () {
        cancelAnimationFrame(t)
        t = requestAnimationFrame(func);
    }
}

from blog.

lynn1824 avatar lynn1824 commented on May 2, 2024 23

学以致用,感谢楼主!

/**
 * Created by Administrator on 2017/10/30.
 */
function setResult(tag, content, color) {
    if(tag && typeof tag == 'object') {
        tag.innerHTML = content;
        tag.style.color = color;
    }
}

var validateEmail = function (e) {
    // 邮箱正则
    var reg = /^[a-z0-9]+(\w|_)+@+([a-z0-9]){2,4}.[a-z]{2,4}$/;
    var currentValue = e.target.value;
    var resultTag = document.getElementById('resultEmail'),
        content = reg.test(currentValue) ? '邮箱正确' : '请输入正确的邮箱',
        color = reg.test(currentValue) ? 'green' : 'red';
    setResult(resultTag, content, color);
}

var validateMobile = function (e) {
    // 手机号正则
    var reg = /^1(3|4|5|7|8){1}[0-9]{9}$/
    var currentValue = e.target.value;
    var resultTag = document.getElementById('resultMobile'),
        content = reg.test(currentValue) ? '手机号正确' : '请输入正确的手机号',
        color = reg.test(currentValue) ? 'green' : 'red';
    setResult(resultTag, content, color);
}

// 防抖
function debounce(func, wait) {
    var timeOut;

    return function () {
        if(timeOut) {
            clearTimeout(timeOut);
        }
        // 保存this上下文,参数
        var that = this, args = arguments;
        timeOut = setTimeout(function () {
            func.apply(that, args);
        }, wait)
    }
}

document.getElementById('emailIpt').onkeyup = debounce(validateEmail, 1000);
document.getElementById('mobileIpt').onkeyup = debounce(validateMobile, 1000);

from blog.

jawil avatar jawil commented on May 2, 2024 13

@chenxiaochun 推荐一下我用的。mac 上,我在用Gifox, snagit。但其实,giphy 也不错
https://giphy.com/apps/giphycapture
App Store 直接下

http://recordit.co/ 这个也不错,简单粗暴,就是没有配置选项

还有这个也是免费的,而且 windows mac 都支持。。收费除了 SnagIt(支持 windows 和 mac),还有 Gifox(只支持 mac)

from blog.

xietao91 avatar xietao91 commented on May 2, 2024 10

跟着大神涨姿势了,我看了一两个小时才算看明白😂

from blog.

YeaseonZhang avatar YeaseonZhang commented on May 2, 2024 9

第五版有一点不解,为什么要return result

 if (callNow) func.apply(context, args)

直接执行不可以么,望解答,谢谢

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 6

@YeaseonZhang 直接执行当然可以呀,之所以 return result ,是考虑到 func 这个函数,可能有返回值,尽管这个功能,我们在实际的开发中基本用不到……但是作为一个工具库,underscore 考虑得会更齐全一点~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 6

@chenxiaochun 我用的是一个 mac 下的叫做 licecap 的录制 GIF 的小软件,免费而且不需要安装,直接打开就能用

from blog.

zhouyingkai1 avatar zhouyingkai1 commented on May 2, 2024 3

学习了 点赞, 一路学到这里 收获颇丰

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 3

@SuperRay3 非常抱歉,我之前没有看到,现在才回复~

先看看如果 return function 我们怎么使用:

var fn = debounce(getUserAction, 1000);
container.onmousemove = f;

debounce(getUserAction, 1000) 会返回一个函数,然后 container mouseover 的时候就会持续的触发这个返回的函数。也可以在 getUserAction 函数中直接写啦,但是直接写的话,改的 getUserAction 这个函数的代码,如果再有一个 getOtherUserAction 函数需要使用防抖,我们就还需要再修改 getOtherUserAction 函数,所以 return function 其实是为了将防抖的代码与原函数代码抽离出来,方便复用。

from blog.

wbcs avatar wbcs commented on May 2, 2024 3
function debounce(fn, wait, immediate) {
  let timeout;
  let callback;

  const action = function(...args) {   //节流核心代码
    clearTimeout(timeout);
    timeout = setTimeout(fn.bind(this), wait, ...args);  //解决fn中this、event参数问题
  };

  callback = immediate ?  //第一次是否立即执行
    function(...args) { 
      //解决返回值问题
      return typeof timeout === 'undefined' ?  // timeout是undefined,表示是第一次执行
        (timeout = 0, fn.call(this, ...args)) :  //timeout设为0, 调用fn(this、event问题)
        action.call(this, ...args); //后续调用
    } :
    action;  //否
  
  callback.cancel = function() {
    clearTimeout(timeout);
    timeout = undefined;
  };

  return callback;
}

这样实现是不是能更好一点, 把immediate的判断放在debounce中,这样就不需要连续触发的时候还判断了。

from blog.

inottn avatar inottn commented on May 2, 2024 3

@wb421768544
你的这段代码和楼主的代码功能上还是有一点区别。
这段代码核心是调用 action 函数

const action = function(...args) {   //节流核心代码
  clearTimeout(timeout);
  timeout = setTimeout(fn.bind(this), wait, ...args);  //解决fn中this、event参数问题
};

action 函数中 setTimeout 超时则会执行fn.bind(this)

举个例子,debounce(fn, 5000, true) 第一次触发时立即执行 fn 函数。第二次触发时,会注册一个定时器,定时器会在 5s 后执行 fn 函数。第三次触发时,如果上一个定时器还未超时,则取消上一个定时器,重新注册一个 5s 的定时器。

简单来说,如果快速地触发该函数两次,第一次立即执行,第二次会延迟执行

而在楼主的代码中,当 immediate 为true时,会执行下面这段代码

timeout = setTimeout(function(){
  timeout = null;
}, wait);

同样举个例子,debounce(fn, 5000, true) 第一次触发时立即执行 fn 函数。并且每次触发时都会取消上一个定时器并再注册一个定时器,定时器会在 5s 后执行 timeout = null。从下面的代码可以看出,停止触发5秒后,才可以重新触发执行 fn 函数

var callNow = !timeout;
...
...
if (callNow) func.apply(context, args)

简单来说,如果快速地触发该函数两次,第一次立即执行,第二次不会执行也不会延迟执行

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 2

@Derrys 这个效果跟逻辑是符合的呀,当 immediate 为 true 的时候,需求就是直接触发,只有等待 n 秒后不再触发,你再移动,才会触发的~

default

from blog.

baixiaoji avatar baixiaoji commented on May 2, 2024 2

@mqyqingfeng 不是很明白注释的这句话「// 如果已经执行过,不再执行」

...
     if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            //  不明白这个定时器   没有的话也是可以的呀
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
           
            if (callNow) func.apply(context, args)
        }
...

哈哈,明白了还是要给定时器赋值,在一直触发事件的时候让定时器都true值,定时器还一直排在任务队列的末尾,这样就能做到要等wait的这段时间了。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@xxxgitone 我也在梳理自己的知识,与你共勉哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@wuyouxin 真是抱歉哈,没有及时回复,让你看了这么久……

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@suminhohu 我测试了下效果,没有问题,效果是会第一次触发会立刻执行,然后不再触发 1 秒后会再执行一次,如果非要说些问题的话,就是 debounce 这个函数并没有完全的被抽离出来,如果用在其他的函数上面,我们还需要修改 debounce 函数内部的代码。我们可以一起修改一下:

第一步:deounce 函数内部的 count === 1 可以使用 !time 替代,可以避免与 count 的耦合

var time;
function debounce() {
    !time ? time = setTimeout(getUserAction, 0) : clearTimeout(time); 
    time = setTimeout(getUserAction, 1000)
}

第二步:提取需要做 deounce 的函数和时间作为变量,适应更多的情况:

var time;
function debounce(fn, timer) { 
 return function(){
    !time ? time = setTimeout(fn, 0) : clearTimeout(time); 
    time = setTimeout(fn, timer)
 }
}

第三步:避免 time 成为全局变量,防止意外修改:

    function debounce(fn, timer) {
        var time
        return function() {
            !time ? time = setTimeout(fn, 0) : clearTimeout(time);
            time = setTimeout(fn, timer)
        }
    }

其实跟这篇文章的第一版还蛮像的……

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@happytoTom

var res = debounce(function(){
    getUserAction(在这里输入参数)
}, 1000);
container.onmousemove = res;

from blog.

minghu0902 avatar minghu0902 commented on May 2, 2024 1

我有一个疑问,下面这种方法的简单实现,是防抖吗,或者说有什么不好?因为我看到好多是用定时器实现的,所以又这个疑问。

function debounce(fn, wait) {
            var time = 0;
            return function() {
                var context = this;
                var args = [].slice.call(arguments);
                var nowTime = Date.now();
                if(nowTime - time >= wait) {
                    fn.apply(context, args);
                }
                time = Date.now();
            }
        }

from blog.

LeeChar avatar LeeChar commented on May 2, 2024 1

第四版中的定时器里,timout = null 上面少了一句 func.apply(context, args),不然不会执行最后一次的触发

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@wcflmy 确实存在这个问题,非常感谢指出~ o( ̄▽ ̄)d

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@hujiulong requestAnimationFrame 确实是神器呐~

from blog.

Junrui-L avatar Junrui-L commented on May 2, 2024

大神厉害了,学习了

from blog.

chenxiaochun avatar chenxiaochun commented on May 2, 2024

请问,您用的什么录屏软件啊,QuickTime吗?

from blog.

chenxiaochun avatar chenxiaochun commented on May 2, 2024

@jawil@mqyqingfeng 发现了另一款截屏录屏神器,推荐给两位啊。不仅功能强大,颜值也很高。http://jietu.qq.com/

from blog.

stormqx avatar stormqx commented on May 2, 2024

用promise应该可以也可以返回setTimeout中回调函数的结果。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@stormqx 确实如此,用 promise 可以实现这个效果,不过 underscore 中没有实现 promise,所以这里也就没有使用 promise,不过说起来,ES6 系列中会讲到从零实现一个 promise ,欢迎关注哈~

from blog.

codingwesley avatar codingwesley commented on May 2, 2024

写的很好 感谢赐教!

from blog.

zhufangmin1990 avatar zhufangmin1990 commented on May 2, 2024

非常感谢分享,受教了!

from blog.

lynn1824 avatar lynn1824 commented on May 2, 2024

good!

from blog.

dengnan123 avatar dengnan123 commented on May 2, 2024

为什么不来讲解 lodash 呢

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@lynn1824 感谢分享~ 这个例子非常适合使用防抖~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@dengnan123 因为 underscore 更适合我这种新手读呀~

from blog.

qhx123 avatar qhx123 commented on May 2, 2024

第五版有一点不解,return result,为什么我得不到返回值?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@qhx123 只有当 immediate 为 true 的时候,才有可能获取返回值,我写个 demo,你可以参照这个例子获取返回值

<!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>
// 第五版
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(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    }
}

var count = 1;
var container = document.getElementById('container');

function getUserAction() {
    container.innerHTML = count++;
    // 函数有返回值
    return '111';
};

container.onmousemove = function(){
   // result 表示返回值
    var result = debounce(getUserAction, 1000, true)();
    console.log(result)
}

from blog.

Derrys avatar Derrys commented on May 2, 2024

代码有问题,当设置了 immediate 为 true的时候,里面代码的逻辑会出现问题, 你这里的 setTimeout 把 timeout = null, 而不会触发最后一次滑动后间隔 一秒之后的那次逻辑。 导致进入执行一次打印 1,结果停下来并不会打印2,而是要再滑动的时候立马打印2,停下来也不会打印3。 跟原本逻辑相悖了。

from blog.

qhx123 avatar qhx123 commented on May 2, 2024

@Derrys 代码是对的,冴羽大神他是设置了时间,在这段时间之内触发事件是不执行的。我才反应过来。

from blog.

ezewu avatar ezewu commented on May 2, 2024

@mqyqingfeng 一楼的说:建议将第四版和第五版换个顺序,在第四版中,由于func始终是异步执行的,return result返回一直是undefined,只有在第五版中immediate参数为true的情况下,result才会取到结果,所以建议换个顺序更加严谨一些。现在文章是换过的吗?还是我没看懂,看不明白你们二个在说换什么?还有这个:立刻执行

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

这个需求就是:

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

想想这个需求也是很有道理的嘛,那我们加个 immediate 参数判断是否是立刻执行。
这里我看您的图片是鼠标愰几下,停下来一会儿,数字就加1了,为什么原样复制代码,也没出现这效果,还是gif图有掉帧

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@wuyouxin 文章中的内容已经换过了哈。第二个问题,并不是停下来一会,数字就加 1 了,其实是停下来一会后,超过了 n 秒后,你再移动,就执行了下一次的 immediate ,数字才会加 1

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@wuyouxin 其实是我把鼠标移出区域这一个操作,导致触发了加 1

from blog.

ezewu avatar ezewu commented on May 2, 2024

@mqyqingfeng 谢谢楼主,昨天我反复看了一个多小时,后来看懂了,谢谢

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

大家留下来的问题,不一定能保证及时回复,但是我一定是会回复的~

from blog.

zjp6049 avatar zjp6049 commented on May 2, 2024

为什么我在vue单页面 import了这个debounce函数之后,在clearTimeout的时候timout变量一直为undefined,在正常页面用就没问题了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zjp6049 我也判断不出这个问题所在……如果可以的话,可以写个 demo 一些讨论下~

from blog.

jasonzhangdong avatar jasonzhangdong commented on May 2, 2024

看了你的博客将近一个多星期了,才知道这个字读冴(ya)

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@jasonzhangdong 是第四声的呦~

from blog.

zjp6049 avatar zjp6049 commented on May 2, 2024
<template>
  <div class="dashboard">
    <p @mousemove="handlerMouseMove" class="wrapper">{{ num }}</p>
  </div>
</template>

<script type="text/ecmascript-6">
function debounce(func, wait) {
    var timeout;
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}
export default {
  data() {
    return {
      num: 0,
    };
  },
  methods: {
    addNum() {
      this.num++
    },
    handlerMouseMove(e) {
      debounce(addNum, 1000)
//    debounce(addNum, 1000)()
    }
  }
};
</script>

<style lang="scss">
.dashboard {
  .wrapper {
    height: 300px;
    background: #ccc;
  }
}
</style>

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zjp6049 这个是 Vue 的示例?

from blog.

hazxy avatar hazxy commented on May 2, 2024

@mqyqingfeng 请问一下,对于第三版里那个var args = arguments,在arguments这里有点弄不懂了,这个arguments是哪个的?那args和getUserAction函数的参数的区别?我都有一点绕了

from blog.

zjp6049 avatar zjp6049 commented on May 2, 2024

@mqyqingfeng 我之前自己混淆的,import进来的函数没有this作用域,还有vue绑定的事件跟原生你写的绑定方式不一样(在触发函数执行的区别:好像原生只绑定了指针,所以debuounce只触发了一次,vue每触发一次事件都重新执行一次新事件,执行了多次)不知道是否理解错了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@hazxy

// 第三版的代码
function debounce(func, wait) {
    var timeout;

    return function () {
        var context = this;
        var args = arguments;

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

arguments 在普通函数中就是指 arguments 所在的函数的参数,在这个例子中就是指 return 的这个函数的参数

如果我们这样使用 debounce:

var func = debounce(getUserAction, 1000);
container.onmousemove = func;

arguments 就是指 func 函数执行时的参数,在这里例子中,onmousemove 传给 func 一个参数,这个参数表示事件对象,一般我们用 event 表示。

args 和 getUserAction 函数的参数没有什么区别,因为我们只是把 onmousemove 传给 func 的参数又传递给 getUserAction 而已,所以两者是一样的。

如果有疑问,欢迎留言哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zjp6049 应该是把 debounce 函数执行后的返回函数传递给 @mousemove,我不会 Vue,不知道是不是这样写的:

<template>
  <div class="dashboard">
    <p @mousemove="handlerMouseMove" class="wrapper">{{ num }}</p>
  </div>
</template>

<script type="text/ecmascript-6">
function debounce(func, wait) {
    var timeout;
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}

var handlerMouseMove = debounce(addNum, 1000)

export default {
  data() {
    return {
      num: 0,
    };
  },
  methods: {
    addNum() {
      this.num++
    },
    handlerMouseMove: handlerMouseMove
  }
};
</script>

<style lang="scss">
.dashboard {
  .wrapper {
    height: 300px;
    background: #ccc;
  }
}
</style>

from blog.

zjp6049 avatar zjp6049 commented on May 2, 2024

@mqyqingfeng 不是的,你这样写拿不到addNum。关键还是要把debounce的执行结果给mousemove绑定,我最后是改成这样了。当然如果要用引入的方式的话,我就用了mixin(注释掉的代码是mixin的)。可能我用vue模仿才这样吧

<template>
  <div class="dashboard">
    <p @mousemove="result" class="wrapper">{{ num }}</p>
  </div>
</template>

<script type="text/ecmascript-6">
function debounce(func, wait) {
    var timeout
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}
//import { debounceMixin } from 'utils'
export default {
// mixins: [debounceMixin],
  data() {
    return {
      num: 0,
    };
  },
  created() {
    this.result = debounce(this.addNum, 1000, true)
//  this.result = this.debounce(this.addNum, 1000, true)
  },
  methods: {
    addNum() {
      this.num++
    }
  }
};
</script>

<style lang="scss">
.dashboard {
  .wrapper {
    height: 300px;
    background: #ccc;
  }
}
</style>

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zjp6049 哈哈,解决就好~

from blog.

hazxy avatar hazxy commented on May 2, 2024

@mqyqingfeng 懂了懂了,看一半一下子就反应过来了,谢谢解惑!

from blog.

SuperRay3 avatar SuperRay3 commented on May 2, 2024
function debounce(func, wait) {
      var timeout;
      return function () {
        var context = this
        var args = arguments

        clearTimeout(timeout)
        timeout = setTimeout(function() {
          func.apply(context, args)
        }, wait)
      }
    }

为什么 debounce 内部不直接写代码,而是要 return function ,这样写的目的是什么,二者在调用的时候有什么不同呢?

from blog.

iiicon avatar iiicon commented on May 2, 2024

good job

from blog.

zhengxhui avatar zhengxhui commented on May 2, 2024

第五版给的例子不生效的呢。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Imlisten 感谢 PR 哈~ 第五版的例子,我在本地测试了一下,没有问题呀,当鼠标移动的时候,打印 111 ,就证明该例子生效了~

from blog.

zhengxhui avatar zhengxhui commented on May 2, 2024

@mqyqingfeng 嗯,是打印了111, 但是同时防抖的效果也没了。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Imlisten 哈哈,确实会这样哈,因为要展示返回的值,所以就无法将 debounce 返回的函数赋值给 onmouseover 函数,例子只是证明可以正确返回值~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@baixiaoji 正是如此~

from blog.

Sphinm avatar Sphinm commented on May 2, 2024

@mqyqingfeng 大神我又来了~ 之前我也有做过类似的防抖措施,是单纯使用定时器实现的,我贴出来代码你看看有哪些问题哈

let container = document.querySelector('#container'),
count = 1,
time,
log = console.log.bind(console);

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

// setTimeout(fn, 0) 表示立即插入队列而不是立即执行
function debounce() {
    count === 1 ? time = setTimeout(getUserAction, 0) : clearTimeout(time); 
    time = setTimeout(getUserAction, 1000)
}

// mousemove 移动完成后触发
container.addEventListener('mousemove', debounce);

from blog.

Sphinm avatar Sphinm commented on May 2, 2024

@mqyqingfeng 哇,感谢大神提供思路,我写代码的时候就很少考虑这么好!再次感谢~

from blog.

TPORL avatar TPORL commented on May 2, 2024

此时注意一点,就是 getUserAction 函数可能是有返回值的,所以我们也要返回函数的执行结果,但是当 immediate 为 false 的时候,因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,最后再 return 的时候,值将会一直是 undefined,所以我们只在 immediate 为 true 的时候返回函数的执行结果。

其实setTimeout里面把返回值赋给result,只是第一次是undefined(假设函数有返回值),之后每次有效触发,都会得到上一次函数func的返回值。
虽然感觉返回值没什么用...

from blog.

Fromzero828 avatar Fromzero828 commented on May 2, 2024

请教下大神,我把第四版改成这样是否可行呢,里面有没有存在错误


function debounce(func, wait) {
          var timeout;       
          var flag;
          return function () {
              var context = this;
              var args = arguments;
              clearTimeout(timeout)  
              if(!flag) {
                  func.apply(context, args)
                  flag = true;
              } else {
                  timeout = setTimeout(function() {
                      func.apply(context, args)
                  }, wait);
              }         
              
          }
      }

from blog.

webjscss avatar webjscss commented on May 2, 2024

大佬写的文章全是干货 太赞了

from blog.

dotequiet avatar dotequiet commented on May 2, 2024

第五版定时器没有起到作用,函数里面的调用每次都会初始化time为undefined是不是应该把定时器放到外面或者将timer放到外面,就会好吧

 var result = debounce(getUserAction, 1000, true);
container.onmousemove = function(){
   result();
    console.log(result)
}

from blog.

webjscss avatar webjscss commented on May 2, 2024

刚开始看的时候理解错了以为可以防抖事件。好吧事件是无法防抖的。。。感谢大佬写的系列文章。学习到很多

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@TPORL 我也没有找到什么场景会用到返回值,但是 underscore 就这样写了,我也只能按照这个方式讲解了……

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Fromzero828 我去尝试了下这段代码,跟第四版的效果差异还是很大的。这段代码的思路是根据 flag 的值来判断第一次的时候是否立刻执行,然后下次就延时执行,这样的话,实际的效果就是是立刻改变值,然后停止触发 N 秒后又会执行一次,而这一次是没有必要的~ 你可以自己写个例子试验一下,相信你很快就会找到原因~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@dotequiet 不是很明白哈~ debounce 是一个高阶函数,执行后会返回一个函数,而事件触发的时候,反复执行的回调函数就是 debounce 返回的这个函数,所以 timeout 只有在执行 debounce(getUserAction, 1000, true) 才会初始化为 undefined~

此外

 var result = debounce(getUserAction, 1000, true);
container.onmousemove = function(){
   result();
    console.log(result)
}

不就相当于:

var result = debounce(getUserAction, 1000, true);
container.onmousemove = result;

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@webjscss 正是如此,“事件是无法防抖的” 😀

from blog.

dotequiet avatar dotequiet commented on May 2, 2024

@mqyqingfeng
第五版的demo,需要有返回值啦。。

/**
 * 添加函数返回值
 */

// 第五版
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(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    }
}

var count = 1;
var container = document.getElementById('container');

function getUserAction() {
    container.innerHTML = count++;
    return '111'
};

container.onmousemove = function(){
    var result = debounce(getUserAction, 1000, true)();
    console.log(result)
}

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@dotequiet 我明白了,确实是这样,感谢指出哈~ o( ̄▽ ̄)d

from blog.

tangtao007 avatar tangtao007 commented on May 2, 2024

@mqyqingfeng 请问当getUserAction方法有参数传递时,应该如何写?

from blog.

tangtao007 avatar tangtao007 commented on May 2, 2024

@mqyqingfeng 谢了

from blog.

izhangzw avatar izhangzw commented on May 2, 2024

看了之后,在思考可不可以用到防止连点操作。
点击后立即执行,但是在返回之前不会再次触发点击。

from blog.

shaolonger avatar shaolonger commented on May 2, 2024

楼主的文章写得很棒,收益很多,感谢!发现一个小问题,第四版声明的result应该是在对调第四、五版后留下的笔误吧,楼主有空可以看看。

from blog.

imaxing avatar imaxing commented on May 2, 2024

大大我又来点赞学习了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@jDragonV 如果一定要等到返回数据的时候再次触发估计就需要一个变量来控制状态了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@goodluck2018 非常感谢呀~ 已经修改了,Good Luck 呀~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@imaxing 欢迎哦,希望能对你有所帮助~

from blog.

lizhongzhen11 avatar lizhongzhen11 commented on May 2, 2024

前两天阿里面试线上答题考到了防抖函数,我没看过这篇文章,遂挂。。。自己的技术积累还是不够啊!

from blog.

lulin1 avatar lulin1 commented on May 2, 2024

underscore上的定义:

debounce_.debounce(function, wait, [immediate])
返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入(多是一些用户操作)停止到达之后执行的行为有帮助。 例如: 渲染一个Markdown格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.

传参 immediate 为 true, debounce会在 wait 时间间隔的开始调用这个函数 。(注:并且在 waite 的时间之内,不会再次调用。)在类似不小心点了提交按钮两下而提交了两次的情况下很有用。

from blog.

qdDog avatar qdDog commented on May 2, 2024
if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            //  这里我可以这么理解吗。第一次开始执行的时候.timeout是undefined。置反之后所以是true。
           //  可以正常执行到callNow的判断里面。立即执行了。
          // timeout 这个定时器,要过一秒之后才会返回。那之后在一秒之类执行还是会执行到callNow里面的判断才对呀。。。
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
           
            if (callNow) func.apply(context, args)
        }

from blog.

AZmake avatar AZmake commented on May 2, 2024

在 vue里面使用的时候 apply 好像把context丢掉了, 因为 func.apply(context, args) 的 args 并没有 context

change: helper.debounce(item => {
            console.log(item);
            this.$emit('cartChange', item);  // $emit is undefined,because this is undefined.
        }, 1000, false)

from blog.

Tvinsh avatar Tvinsh commented on May 2, 2024

@LeeChar 加上这句,如果不是最后一次触发,会出现快速接连两次触发的情况

from blog.

hubvue avatar hubvue commented on May 2, 2024

您好,我想问一下,不知是我自己的问题,还是设计本就是这样,在立即执行这个功能上,第一次触发之后继续触发就没有了后续的执行。是不是因为不同需求的原因,根据不同的需求去选择合适的功能。比如在鼠标划动上可以添加上立即触发功能而放弃最后一次的触发,在搜索框上面则不需要立即触发功能,用最后一次的触发来实现防抖功能???

from blog.

LeeChar avatar LeeChar commented on May 2, 2024

您好,我想问一下,不知是我自己的问题,还是设计本就是这样,在立即执行这个功能上,第一次触发之后继续触发就没有了后续的执行。是不是因为不同需求的原因,根据不同的需求去选择合适的功能。比如在鼠标划动上可以添加上立即触发功能而放弃最后一次的触发,在搜索框上面则不需要立即触发功能,用最后一次的触发来实现防抖功能???

刚看到这个邮箱,有点疑惑。。。我github没玩转,好像是恢复我的;是这样的,我跟着例子写代码的时候,看见动图显示最后一次触发了;可是我照着写的例子却并没有触发最后一次,在那上面加了一个执行语句最后一次就执行了;可能有点误解吧,或者也许我没有看进去。而且。。。。我没看懂你想表达的意思,尴尬额

from blog.

inottn avatar inottn commented on May 2, 2024

@minghu0902
你的这段代码是 throttle 节流。

from blog.

world8023 avatar world8023 commented on May 2, 2024

// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait);你好,请问这 timeout = null;是不是不写也可以,毕竟timeout 只想要一个返回值

from blog.

yin-shu avatar yin-shu commented on May 2, 2024
function debounce(func, wait) {
  let timeout
  return function() {
    let context = arguments
    clearTimeout(timeout)
    timeout = setTimeout(() => {func.apply(this, context)}, wait)
  }
}

为什么这里要把arguments传到func里,我直接这样把e传到func里不行,请指教

function debounce(func, wait) {
  let timeout
  return function(e) {
    clearTimeout(timeout)
    timeout = setTimeout(() => {func.apply(this, e)}, wait)
  }
}

from blog.

SUNNERCMS avatar SUNNERCMS commented on May 2, 2024

我又来学习这块的知识啦,确实很不错,之前看过有点囫囵吞枣,还是应该彻底弄懂理解加以应用方能掌握

from blog.

hncsjxxxx avatar hncsjxxxx commented on May 2, 2024

请问楼主function debounce(func, wait) {
var timeout;
return function () {
clearTimeout(timeout)
timeout = setTimeout(func, wait);
}
}为什么要return function,去掉return function,直接执行clearTimeout和setTimeout可以吗

from blog.

5201314999 avatar 5201314999 commented on May 2, 2024

// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait);你好,请问这 timeout = null;是不是不写也可以,毕竟timeout 只想要一个返回值

不可以 clearTimeout 不会让timeout =null ,时间到了 callNow 不会为true

from blog.

5201314999 avatar 5201314999 commented on May 2, 2024

请问楼主function debounce(func, wait) {
var timeout;
return function () {
clearTimeout(timeout)
timeout = setTimeout(func, wait);
}
}为什么要return function,去掉return function,直接执行clearTimeout和setTimeout可以吗

debounce 是一开始就执行的,每次移动事件促发执行的是function 啊,像你这样写timeout 声明 就只能放debounce 外面了,这样就不算封装了吧,timeout 是所有事件促发共用的变量。

from blog.

18355166248 avatar 18355166248 commented on May 2, 2024

最后一个取消代码 有点问题

  1. 在 第一次进入的时候 immediate 没有设置成false 这样的话 只会执行一次.
  2. 取消方法里面没有将 immediate设置成false 出发取消事件后 并不能立即执行方法.

from blog.

FinnWu avatar FinnWu commented on May 2, 2024
function debounce(func, wait) {
  let timeout
  return function() {
    let context = arguments
    clearTimeout(timeout)
    timeout = setTimeout(() => {func.apply(this, context)}, wait)
  }
}

为什么这里要把arguments传到func里,我直接这样把e传到func里不行,请指教

function debounce(func, wait) {
  let timeout
  return function(e) {
    clearTimeout(timeout)
    timeout = setTimeout(() => {func.apply(this, e)}, wait)
  }
}

@yin-shu 我一开始还蒙了,不过看清楚后,那个settimeout里面的函数是定义的,就是说参数是形式参数,而不是真正运行时候的参数,你把那个形式参数去掉之后又可以打印出来了e,这个时候才是闭包.不知道你听得懂不?

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.