手写防抖节流

简介: 手写防抖节流

文章目录


手写前端常用技巧-防抖节流

防抖:当持续触发事件时,一定时间内没有再触发事件,才会在一段时间之后触发事件处理函数。


节流:当持续触发事件时,保证一段delay之内,只调用一次函数

防抖

  • 源代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖</title>
</head>
<body>
    <label for='undebounce'>没防抖:</label>
    <input type="text" id='undebounce' onInput='onInput1(event)' >
    <br />
    <label for="debounce">有防抖:</label>
    <input type="text" id='debounce' onInput='debounceInput(event)' >
    <script>
        function debounce(fn, delay){
            let time = null
            return function(...args){
                if(time){
                    clearTimeout(time)
                }
                time = setTimeout(()=>{
                    fn.apply(this, args)
                },delay)
            }
        }
        function onInput(e){
            val = e.target.value
            if(val){
                console.log('有防抖',val)
            }
        }
        const debounceInput = debounce(onInput, 300)
        function onInput1(e){
            val = e.target.value
            if(val){
                console.log('没有防抖',val)
            }
        }
    </script>
</body>
</html>
  • 执行结果

::: tip

  • 结果分析
  1. 没有防抖的输入框持续输入时,每键入一个字母都执行一次处理函数
  2. 有防抖的输入框持续输入时,在键入字母结束之后才执行一次处理函数

:::

节流

1. 首节流

首节流,时间戳的实现,首次触发立即执行,停止触发后,没办法再次执行

  • 源代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>节流throttle</title>
</head>
<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
    </ul>
</body>
<style>
    body{
        display: flex;
        justify-content: center;
    }
    ul, li {
        padding:0;
        margin: 0;
    }
    li{
        width: 250px;
        height:200px;
        list-style: none;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    li:nth-child(2n){
        background:whitesmoke;
    }
    li:nth-child(2n+1){
        background:yellow;
    }
</style>
<script>
    // 首节流,时间戳的实现,停止触发后,没办法再次执行
    function throttle(fn, interval){
        let prev = 0;
        return function(...args){
            const now = Date.now();
            if( now-prev >= interval ){
                prev = now
                fn.apply(this, args)
            }
        }
    }
    function handle(){
        console.log(new Date())
    }
    const throttleHandler = throttle(handle, 3000)
    console.log('tt', throttleHandler)
    window.addEventListener('scroll', throttleHandler)
</script>
</html>

可以看到,只要滑动了,就会触发事件,但是停止滑动之后,不会触发最后一次。

2. 尾节流

定时器实现,不会立即执行,而是在delay之后执行

最后停止触发之后,还会执行一次

  • 源代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>节流throttle</title>
</head>
<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
    </ul>
</body>
<style>
    body{
        display: flex;
        justify-content: center;
    }
    ul, li {
        padding:0;
        margin: 0;
    }
    li{
        width: 250px;
        height:200px;
        list-style: none;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    li:nth-child(2n){
        background:whitesmoke;
    }
    li:nth-child(2n+1){
        background:yellow;
    }
</style>
<script>
    // 尾节流,定时器实现,不会立即执行,而是在delay之后执行
    // 最后停止触发之后,还会执行一次
    function throttle(fn,delay){
        let timer = null
        return function(...args){
            if( !timer ){
                timer = setTimeout(()=>{
                    fn.apply(this, args)
                    timer = null
                }, delay)
            }
        }
    }
    function handle(){
        console.log(new Date())
    }
    const throttleHandler = throttle(handle, 3000)
    console.log('tt', throttleHandler)
    window.addEventListener('scroll', throttleHandler)
</script>
</html>

可以看到我们停止滑动之后还是触发了事件

3. 首尾节流

  • 源代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>节流throttle</title>
</head>
<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
    </ul>
</body>
<style>
    body{
        display: flex;
        justify-content: center;
    }
    ul, li {
        padding:0;
        margin: 0;
    }
    li{
        width: 250px;
        height:200px;
        list-style: none;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    li:nth-child(2n){
        background:whitesmoke;
    }
    li:nth-child(2n+1){
        background:yellow;
    }
</style>
<script>
    // 首尾节流,计时器+时间戳
    function throttle(fn, delay){
        let timer = null
        let startTime = 0
        return function(...args){
            let curTime = Date.now();
            let remaining = delay - (curTime - startTime)
            // 清除旧的计时器
            clearTimeout(timer)
            if(remaining <= 0 ){
                fn.apply(this, args)
                startTime = Date.now()
            }else{
                timer = setTimeout(()=>{
                    fn.apply(this, args)
                    startTime = Date.now()
                }, remaining)
            }
        }
    }
    function handle(){
        console.log(new Date())
    }
    const throttleHandler = throttle(handle, 500)
    console.log('tt', throttleHandler)
    window.addEventListener('scroll', throttleHandler)
</script>
</html>

在这里我们缩短节流时间,方便看效果。

可以看到每次滑动都至少触发两次事件,即首尾触发

总结

虽然防抖和节流都是避免同一时间频繁执行处理函数,但是原理却有一些差别

::: note

应用场景

  • 防抖debounce
  • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • 节流throttle
  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

:::

文章详细信息,请查看个人博客

相关文章
|
11天前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
42 1
|
2月前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
2月前
|
存储 移动开发 前端开发
「offer来了」面试中必考的15个html知识点
该文章汇总了前端面试中常见的15个HTML知识点,涵盖了从HTML文档的规范书写、doctype声明的作用到新兴的HTML5标签应用及移动端viewport设置等内容,旨在帮助求职者更好地准备相关技术面试。
「offer来了」面试中必考的15个html知识点
|
2月前
|
Web App开发 前端开发 JavaScript
「offer来了」1张思维导图,6大知识板块,带你梳理面试中CSS的知识点!
该文章通过一张思维导图和六大知识板块系统梳理了前端面试中涉及的CSS核心知识点,包括CSS框架、基础样式问题、布局技巧、动画处理、浏览器兼容性及性能优化等方面的内容。
|
6月前
|
Android开发 API
顺利通过阿里Android岗面试,已拿offer
顺利通过阿里Android岗面试,已拿offer
顺利通过阿里Android岗面试,已拿offer
|
6月前
|
设计模式 缓存 前端开发
真的强!借助阿里技术博主分享的Android面试笔记,我拿到了字节跳动的offer
真的强!借助阿里技术博主分享的Android面试笔记,我拿到了字节跳动的offer
|
6月前
|
前端开发 Java 物联网
Android开发面试基础,3天拿到网易Android岗offer
Android开发面试基础,3天拿到网易Android岗offer
|
6月前
|
Android开发 Java 容器
顺利收获Offer,字节Android面试必问
顺利收获Offer,字节Android面试必问
|
6月前
|
算法 Java C++
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
|
6月前
|
算法 Java 关系型数据库
在家“闭关”,阿里竟发来视频面试,4面顺利拿下offer
关于个人呢,我是一个普通的双非本科生,在校成绩不错,各方面的表现自我感觉也比较突出,今年大四即将毕业,对自己进入大厂工作是很有信心的,我的方向是Java,也知道现在Java的竞争比较激烈,大厂比较难进,但我丝毫不胆怯。当然,我还是很走“狗屎运”的,没想到闭关在家期间,也能收到阿里发来的视频面,还一路顺利拿下了offer。