温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

JavaScript节流与防抖实例分析

发布时间:2022-04-06 11:01:22 来源:亿速云 阅读:140 作者:iii 栏目:开发技术

本篇内容主要讲解“JavaScript节流与防抖实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript节流与防抖实例分析”吧!

    一、js防抖和节流

    在进行窗口的resize、scroll、输出框内容校验等操纵的时候,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常之差。那么为了前端性能的优化也为了用户更好的体验,就可以采用防抖(debounce)和节流(throttle)的方式来到达这种效果,减少调用的频率。

    二、为什么滚动scroll、窗口resize等事件需要优化

    滚动事件的应用很频繁:图片懒加载、下滑自动加载数据、侧边浮动导航栏等。

    在绑定scroll、resize事件时,但它发生的时候,它被触发的频率非常高,间隔很近。在日常开发的页面中,事件中涉及到的大量的位置计算、DOM操作、元素重绘等等这些都无法在下一个scroll事件出发前完成的情况下,浏览器会卡帧。

    三、滚动和页面渲染前端性能优化的关系

    为什么滚动需要优化?前面提到了事件中涉及到大量的位置计算、DOM操作、元素重绘等等,这些又与页面的渲染有关系,页面渲染又与前端的性能优化有关系?套娃一样,一层套着一层,每一层都联系紧密,每一层都如此平凡且重要。

    review一下前端的渲染和优化

    网页生成的时候,至少会渲染(Layout+Paint)一次。用户访问的过程中,还会不断重新的重排(reflow)和重绘(repaint),用户scroll行为和resize行为会导致页面不断的进行重新渲染,而且间隔频繁,容易造成浏览器卡帧。

    四、防抖Debounce

    1 防抖Debounce情景

    ①有些场景事件触发的频率过高(mousemove onkeydown onkeyup onscroll)

    ②回调函数执行的频率过高也会有卡顿现象。 可以一段时间过后进行触发去除无用操作

    2 防抖原理

    一定在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,以新的事件的时间为准,n 秒后才执行,等触发事件 n 秒内不再触发事件才执行。

    官方解释:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

    3 防抖函数简单实现

        //简单的防抖函数
        function debounce(func, wait, immediate) {
            //定时器变量
            var timeout;
            return function () {
                //每次触发scrolle,先清除定时器
                clearTimeout(timeout);
                //指定多少秒后触发事件操作handler
                timeout = setTimeout(func, wait);
            };
        };
        //绑定在scrolle事件上的handler
        function handlerFunc() {
            console.log('Success');
        }
        //没采用防抖动
        window.addEventListener('scroll', handlerFunc);
        //采用防抖动
        window.addEventListener('scrolle', debounce(handlerFunc, 1000));

    4 防抖函数的演化过程

    ①this event绑定问题
        //以闭包的形式返回一个函数,内部解决了this指向的问题,event对象传递的问题
        function debounce(func, wait) {
            var timeout;
            return function () {
                var context = this;
                var args = arguments;
                clearTimeout(timeout)
                timeout = setTimeout(function () {
                    func.apply(context, args)
                }, wait);
            };
        };
    ②立即触发问题
        //首次触发执行,再次触发以后开始执行防抖函数
        //使用的时候不用重复执行这个函数,本身返回的函数才具有防抖功能
    function debounce(func, wait, immediate) {
        var timeout;
        return function () {
            var context = this;
            var args = arguments;
            
            if(timeout) clearTimeout(timeout);
            // 是否在某一批事件中首次执行
            if (immediate) {
                var callNow = !timeout;
                timeout = setTimeout(function() {
                    timeout = null;
                    func.apply(context, args)
                    immediate = true;
                }, wait);
                if (callNow) {
                    func.apply(context, args)
                }
                immediate = false;
            } else {
                timeout = setTimeout(function() {
                    func.apply(context, args);
                    immediate = true;
                }, wait);
            }
        }
    }
    ③返回值问题
    function debounce(func, wait, immediate) {
        var timeout, result;
        return function () {
            var context = this, args = arguments;
            if (timeout)  clearTimeout(timeout);
            if (immediate) {
                var callNow = !timeout;
                timeout = setTimeout(function() {
                    result = func.apply(context, args)
                }, wait);
                if (callNow) result = func.apply(context, args);
            } else {
                timeout = setTimeout(function() {
                    result = func.apply(context, args)
                }, wait);
            }
            return result;
        }
    }
    ④取消防抖,添加cancel方法
    function debounce(func, wait, immediate) {
        var timeout, result;
        function debounced () {
            var context = this, args = arguments;
            if (timeout)  clearTimeout(timeout);
            if (immediate) {
                var callNow = !timeout;
                timeout = setTimeout(function() {
                    result = func.apply(context, args)
                }, wait);
                if (callNow) result = func.apply(context, args);
            } else {
                timeout = setTimeout(function() {
                    result = func.apply(context, args)
                }, wait);
            }
            return result;
        }
        debounced.cancel = function(){
            cleatTimeout(timeout);
            timeout = null;
        }
        return debounced;
    }

    五、节流Throttle

    1 节流Throttle情景

    ①图片懒加载

    ②ajax数据请求加载

    2 节流原理

    如果持续触发事件,每隔一段时间只执行一次函数。

    官方解释:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

    3 节流实现—时间戳和定时器

    时间戳

        var throttle = function (func, delay) {
            var prev = Date.now()
            return function () {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - prev >= delay) {
                    func.apply(context, args);
                    prev = Date.now();
                }
            }
        }
    
        function handle() {
            console.log(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000));

    定时器

        var throttle = function (func, delay) {
            var timer = null;
            return function () {
                var context = this;
                var args = arguments;
                if (!timer) {
                    timer = setTimeout(function () {
                        func.apply(context, args);
                        timer = null;
                    }, delay);
                }
            }
        }
    
        function handle() {
            console.log(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000))

    4 节流函数的演化过程

    ①时间戳触发
    //在开始触发时,会立即执行一次; 如果最后一次没有超过 wait 值,则不触发
    function throttle(func, wait) {
        var context, args;
        var previous = 0; // 初始的时间点,也是关键的时间点
    
        return function() {
            var now = +new Date();
            context = this;
            args = arguments;
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }
    }
    ②定时器触发
    //首次不会立即执行,最后一次会执行,和用时间戳的写法互补。
    function throttle(func, wait){
        var context, args, timeout;
        return function() {
            context = this;
            args = arguments;
            if(!timeout) {
                timeout = setTimeout(function(){
                    func.apply(context, args);
                    timeout = null;
                }, wait);
            }
        }
    }
    ③结合时间戳和定时器
    function throttle(func, wait) {
    
        var timer = null;
        var startTime = Date.now();  
    
        return function(){
            var curTime = Date.now();
            var remaining = wait-(curTime-startTime); 
            var context = this;
            var args = arguments;
    
            clearTimeout(timer);
    
            if(remaining<=0){ 
                func.apply(context, args);
    
                startTime = Date.now();
    
            }else{
                timer = setTimeout(fun, remaining);  // 如果小于wait 保证在差值时间后执行
            }
        }
    }

    到此,相信大家对“JavaScript节流与防抖实例分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI