资讯专栏INFORMATION COLUMN

JS函数节流和函数防抖

doodlewind / 1244人阅读

摘要:为什么需要函数防抖和函数节流在浏览器中某些计算和处理要比其他的昂贵很多。函数防抖如果一个事件被频繁触发多次,并且触发的时间间隔过短,则防抖函数可以使得对应的事件处理函数只执行最后触发的一次。函数防抖可以把多个顺序的调用合并成一次。

1.为什么需要函数防抖和函数节流?

在浏览器中某些计算和处理要比其他的昂贵很多。例如DOM操作比起非DOM交互需要更多的内存和CPU占用时间。连续尝试进行过多的DOM操作可能会导致浏览器挂起,甚至崩溃;

例如当调整浏览器大小的时候,resize事件会连续触发;如果在resize事件处理程序内部尝试进行DOM操作,其高频率的更改可能会让浏览器崩溃;

为了绕开上面的问题,需要对该类函数进行节流;

2.什么是函数防抖和函数节流
防抖(debounce)和节流(throttle)都是用来控制某个函数在一定时间内执行多少次的技巧,两者相似而又不同。
背后的基本思想是某些代码不可以在没有间断的情况下连续重复执行。
2.1 函数防抖 (debounce)
如果一个事件被频繁触发多次,并且触发的时间间隔过短,则防抖函数可以使得对应的事件处理函数只执行最后触发的一次。
函数防抖可以把多个顺序的调用合并成一次。
2.2 函数节流 (throttle)
如果一个事件被频繁触发多次,节流函数可以按照固定频率去执行对应的事件处理方法。
函数节流保证一个事件一定时间内只执行一次。
3.应用场景
类型 场景
函数防抖 1. 手机号、邮箱输入检测
2. 搜索框搜索输入(只需最后一次输入完后,再放松Ajax请求)
3. 窗口大小resize(只需窗口调整完成后,计算窗口大小,防止重复渲染)
4. 滚动事件scroll(只需执行触发的最后一次滚动事件的处理程序)
5. 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,(停止输入后)验证一次就好)
函数节流 1. DOM元素的拖拽功能实现(mousemove
2. 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
3. 计算鼠标移动的距离(mousemove
4. 搜索联想(keyup
5. 滚动事件scroll,(只要页面滚动就会间隔一段时间判断一次)
4.如何实现 4.1 函数防抖实现
function debounce(fn, delay, scope) {
    let timer = null;
    // 返回函数对debounce作用域形成闭包
    return function () {
        // setTimeout()中用到函数环境总是window,故需要当前环境的副本;
        let context = scope || this, args = arguments;
        // 如果事件被触发,清除timer并重新开始计时
        clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(context, args);
        }, delay);
    }
}

代码解读

第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码;

当第二次调用该函数时,它会清除前一次的定时器并设置另一个;

如果前一个定时器已经执行过了,这个操作就没有任何意义;

然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器;

目的是只有在执行函数的请求停止了delay时间之后才执行

4.2 函数节流实现 4.2.1 利用时间戳简单实现
function throttle(fn, threshold, scope) {
    let timer;
    let prev = Date.now();
    return function () {
        let context = scope || this, args = arguments;
        let now = Date.now();
        if (now - prev > threshold) {
            prev = now;
            fn.apply(context, args);
        }
    }
}
4.2.2 利用定时器简单实现
function throttle2(fn, threshold, scope) {
    let timer;
    return function () {
        let context = scope || this, args = arguments;
        if (!timer) {
            timer = setTimeout(function () {
                fn.apply(context, args);
                timer = null;
            }, threshold);
        }
    }
}
5 举例(scroll事件) CSS代码
    .wrap {
       width: 200px;
        height: 330px;
        margin: 50px;
        margin-top: 200px;
        position: relative;
        float: left;
        background-color: yellow;
    }
    .header{
        width: 100%;
        height: 30px;
        background-color: #a8d4f4;
        text-align: center;
        line-height: 30px;
    }
    .container {
        background-color: pink;
        box-sizing: content-box;
        width: 200px;
        height: 300px;
        overflow: scroll;
        position: relative;
    }
    .content {
        width: 140px;
        height: 800px;
        margin: auto;
        background-color: #14ffb2;
    }
HTML代码
    
滚动事件:普通
滚动事件:加了函数防抖
滚动事件:加了函数节流
JS代码
    let els = document.getElementsByClassName("container");
    let count1 = 0,count2 = 0,count3 = 0;
    const THRESHOLD = 200;

    els[0].addEventListener("scroll", function handle() {
        console.log("普通滚动事件!count1=", ++count1);
    });
    els[1].addEventListener("scroll", debounce(function handle() {
        console.log("执行滚动事件!(函数防抖) count2=", ++count2);
    }, THRESHOLD));
    els[2].addEventListener("scroll", throttle(function handle() {
        console.log(Date.now(),", 执行滚动事件!(函数节流) count3=", ++count3);
    }, THRESHOLD));
// 函数防抖
function debounce(fn, delay, scope) {
    let timer = null;
    let count = 1;
    return function () {
        let context = scope || this,
            args = arguments;
        clearTimeout(timer);
        console.log(Date.now(), ", 触发第", count++, "次滚动事件!");
        timer = setTimeout(function () {
            fn.apply(context, args);
            console.log(Date.now(), ", 可见只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行!");
        }, delay);
    }
}
// 函数节流
function throttle(fn, threshold, scope) {
    let timer;
    let prev = Date.now();
    return function () {
        let context = scope || this, args = arguments;
        let now = Date.now();
        if (now - prev > threshold) {
            prev = now;
            fn.apply(context, args);
        }
    }
}
效果截图




6.总结

debouncethrottle均是通过减少高频触发事件的实际事件处理程序的执行来提高事件处理函数运行性能的手段,并没有实质上减少事件的触发次数。

debounce可以把多个顺序的调用合并成一次

throttle保证一个事件一定时间内只执行一次。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99591.html

相关文章

  • 彻底弄懂函数防抖函数节流

    摘要:若时间差大于间隔时间,则立刻执行一次函数。不同点函数防抖,在一段连续操作结束后,处理回调,利用和实现。函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。 原博客地址,欢迎star 函数防抖和节流 函数防抖和函数节流:优化高频率执行js代码的一种手段,js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover...

    Mr_houzi 评论0 收藏0
  • JS进阶篇3---函数节流” VS “防抖

    摘要:目的都是为了降低回调函数执行频率,节省计算机资源,优化性能,提升用户体验。函数防抖事件频繁触发的情况下,只有经过足够的空闲时间,才执行代码一次。 函数节流和函数防抖的对比分析 一、前言 前端开发中,函数节流(throttle) 和 函数防抖(debounce) 作为常用的性能优化方法,两者都是用于优化高频率执行 js 代码的手段,那具体它们有什么异同点呢?有对这两个概念不太了解的小伙伴...

    hlcc 评论0 收藏0
  • 前端优化 —— 函数节流防抖

    摘要:文件为函数要传入的参数返回事件处理函数添加事件监听节流函数一般用于事件的情况较多,因为这些事件的触发是连续性的,需要在一个时间间隔内只触发一次。 showImg(https://segmentfault.com/img/remote/1460000018998747); 阅读原文 前言 在前端开发当中我们经常会绑定一些事件触发的某些程序执行,有时这些事件会连续触发,如浏览器窗口 s...

    ralap 评论0 收藏0
  • 详谈js防抖节流

    摘要:本文会分别介绍什么是防抖和节流,它们的应用场景,和实现方式。防抖和节流都是为了解决短时间内大量触发某函数而导致的性能问题,比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。 本文由小芭乐发表 0. 引入 首先举一个例子: 模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码: 没有防抖 ...

    shevy 评论0 收藏0
  • js 防抖 节流 JavaScript

    摘要:此时需要采用防抖和节流的方式来减少调用频率,同时不影响原来效果。函数防抖当持续触发事件时,一段时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前就触发了事件,延时重新开始。 js 防抖 节流 JavaScript 实际工作中,通过监听某些事件,如scroll事件检测滚动位置,根据滚动位置显示返回顶部按钮;如resize事件,对某些自适应页面调整DOM的渲染;如ke...

    int64 评论0 收藏0
  • 小菊花课堂之JS防抖节流

    摘要:文章来源详谈防抖和节流轻松理解函数节流和函数防抖函数防抖和节流好啦,今天的小菊花课堂之的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。 前言 陆游有一首《冬夜读书示子聿》——古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。 详解 在前端开发中,我们经常会碰到一些会持...

    leoperfect 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<