详谈js防抖和节流

本文深入探讨了防抖和节流技术,旨在解决高频率事件触发导致的性能问题。防抖确保事件触发n秒后执行,适用于输入框查询和窗口调整等场景;节流则在规定时间内限制事件触发次数,适合鼠标点击和滚动加载。文章提供了具体实例和代码实现。
本文由小芭乐发表

0. 引入

首先举一个例子:

模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>没有防抖</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = function () {
            //模拟ajax请求
            function ajax(content) {
                console.log('ajax request ' + content)
            }
            let inputNormal = document.getElementById('normal');

            inputNormal.addEventListener('keyup', function (e) {
                ajax(e.target.value)
            })
        }
    </script>
</head>

<body>
    <div>
        1.没有防抖的输入:
        <input type="text" name="normal" id="normal">
    </div>
</body>

</html>

效果:在输入框里输入一个,就会触发一次“ajax请求”(此处是console)。

![](https://img2018.cnblogs.com/blog/1557854/201812/1557854-20181212193030330-1117758259.png)

没有防抖和节流

缺点:浪费请求资源,可以加入防抖和节流来优化一下。

本文会分别介绍什么是防抖和节流,它们的应用场景,和实现方式。防抖和节流都是为了解决短时间内大量触发某函数而导致的性能问题,比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。但二者应对的业务需求不一样,所以实现的原理也不一样,下面具体来看看吧。

1. 防抖(debounce)

1.1 什么是防抖

在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。

1.2 应用场景

(1) 用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次的查询ajax请求,这样可以有效减少请求次数,节约请求资源;

(2) window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖让其只触发一次;

1.3 实现

还是上述列子,这里加入防抖来优化一下,完整代码如下:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>加入防抖</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = function () {
            //模拟ajax请求
            function ajax(content) {
                console.log('ajax request ' + content)
            }
            function debounce(fun, delay) {
                return function (args) {
                    //获取函数的作用域和变量
                    let that = this
                    let _args = args
                    //每次事件被触发,都会清除当前的timeer,然后重写设置超时调用
                    clearTimeout(fun.id)
                    fun.id = setTimeout(function () {
                        fun.call(that, _args)
                    }, delay)
                }
            }
            let inputDebounce = document.getElementById('debounce')
            let debounceAjax = debounce(ajax, 500)
            inputDebounce.addEventListener('keyup', function (e) {
                debounceAjax(e.target.value)
            })
        }
    </script>
</head>

<body>
    <div>
        2.加入防抖后的输入:
        <input type="text" name="debounce" id="debounce">
    </div>
</body>

</html>

代码说明:

1.每一次事件被触发,都会清除当前的 timer 然后重新设置超时调用,即重新计时。 这就会导致每一次高频事件都会取消前一次的超时调用,导致事件处理程序不能被触发;

2.只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行;

效果:

加入防抖后,当持续在输入框里输入时,并不会发送请求,只有当在指定时间间隔内没有再输入时,才会发送请求。如果先停止输入,但是在指定间隔内又输入,会重新触发计时。

![](https://img2018.cnblogs.com/blog/1557854/201812/1557854-20181212193047164-175003873.png)

加入防抖

2.节流(throttle)

2.1 什么是节流

规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

2.2 应用场景

(1)鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;

(2)在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据;

(3)监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断;

2.3 实现

还是上述列子,这里加入节流来优化一下,完整代码如下:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>加入节流</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = function () {
            //模拟ajax请求
            function ajax(content) {
                console.log('ajax request ' + content)
            }

            function throttle(fun, delay) {
                let last, deferTimer
                return function (args) {
                    let that = this;
                    let _args = arguments;

                    let now = +new Date();
                    if (last && now < last + delay) {
                        clearTimeout(deferTimer);
                        deferTimer = setTimeout(function () {
                            last = now;
                            fun.apply(that, _args);
                        }, delay)
                    } else {
                        last = now;
                        fun.apply(that, _args);
                    }
                }
            }
            let throttleAjax = throttle(ajax, 1000)
            let inputThrottle = document.getElementById('throttle')
            inputThrottle.addEventListener('keyup', function (e) {
                throttleAjax(e.target.value)
            })
        }
    </script>
</head>

<body>
    <div>
        3.加入节流后的输入:
        <input type="text" name="throttle" id="throttle">
    </div>
</body>

</html>

效果:实验可发现在持续输入时,会安装代码中的设定,每1秒执行一次ajax请求

![](https://img2018.cnblogs.com/blog/1557854/201812/1557854-20181212193122217-45133779.png)

加入节流

3. 小结

总结下防抖和节流的区别:

-- 效果:

函数防抖是某一段时间内只执行一次;而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

-- 原理:

防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。

节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。

如有问题,欢迎指正。

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

<think>嗯,用户想了解标准IO文件IO的区别及详细使用方法。我需要先回顾一下相关知识,确保自己理解正确。根据提供的引用内容,标准IO是C库函数,比如fopen、fclose这些,而文件IO是系统调用,比如open、read、write。区别主要在于缓冲机制应用层与内核层的交互。 首先,标准IO有缓冲区,数据先存到应用层的缓冲区,等满了或者满足条件时才调用系统调用写入内核缓冲区。这样可以减少系统调用的次数,提高效率。而文件IO每次操作都是直接系统调用,没有缓冲,适合需要实时读写的场景,比如设备文件或者网络套接字。 接下来,用户可能想知道具体的使用方法,比如函数列表例子。需要分别列出标准IO文件IO常用的函数,比如fopen对应open,fread对应read等。同时,要说明它们的参数用法差异,比如标准IO用FILE指针,文件IO用文件描述符。 另外,用户可能关心它们的适用场景。比如标准IO适合普通文件操作,尤其是需要频繁读写的情况,而文件IO适合底层操作或者特殊文件类型。需要结合引用中的例子,比如标准IO的缓冲区大小如何计算,以及文件IO如何直接操作设备文件。 还要注意用户可能的误区,比如标准IO文件IO是否可以混用。需要提醒他们避免混用,因为缓冲机制可能导致数据不一致。此外,错误处理的方式也不同,标准IO用ferror,文件IO用errno。 最后,生成相关问题时要覆盖性能分析、缓冲区设置、实际应用例子、错误处理适用场景比较。这样用户可以根据问题进一步深入学习。</think>### 标准IO与文件IO的区别及详细使用方法 #### 一、核心区别 1. **层级与封装关系** - **标准IO**:应用层C库函数(如$fopen$, $fread$),对文件IO进行封装,自带缓冲机制[^1][^2] - **文件IO**:系统级调用(如$open$, $read$),直接操作内核接口,无缓冲[^1][^2] 2. **缓冲机制** - **标准IO**: - 全缓冲:默认模式,缓冲区满或调用$fflush$时触发系统调用 - 行缓冲:终端设备专用(如$stdout$),遇换行符或缓冲区满时触发 - 无缓冲:$stderr$等特殊场景[^3] - **文件IO**:无缓冲,每次调用$write$立即写入内核缓冲区 3. **性能对比** - 标准IO通过减少系统调用次数提升效率(例如连续写入10次数据仅触发1次$write$)[^1] - 文件IO适合实时性要求高的场景(如设备驱动开发) #### 二、具体使用对比 | 操作类型 | 标准IO函数 | 文件IO函数 | 参数差异 | |----------|-------------------------|----------------|------------------------| | 打开文件 | `FILE *fopen()` | `int open()` | 权限模式、返回值类型不同 | | 读取数据 | `size_t fread()` | `ssize_t read()` | 需指定FILE指针 vs 文件描述符 | | 写入数据 | `size_t fwrite()` | `ssize_t write()` | 标准IO含缓冲,文件IO直接操作 | | 关闭文件 | `int fclose()` | `int close()` | 释放资源逻辑不同 | **标准IO示例:** ```c FILE *fp = fopen("test.txt", "w"); if(fp) { fwrite("Hello", 1, 5, fp); // 数据暂存应用层缓冲区 fclose(fp); // 触发实际写入 } ``` **文件IO示例:** ```c int fd = open("test.txt", O_WRONLY|O_CREAT, 0644); if(fd != -1) { write(fd, "Hello", 5); // 立即写入内核缓冲区 close(fd); } ``` #### 三、选型指南 1. **优先标准IO的场景** - 普通文件读写(文本/二进制) - 需要格式输入输出(如`fprintf`) - 频繁小数据量操作 2. **必须用文件IO的场景** - 设备文件操作(如`/dev`目录下设备) - 需要文件锁定(`fcntl`) - 非阻塞IO或异步IO操作 #### 四、注意事项 1. **避免混用**:同一文件不应同时使用两种IO方式,可能导致缓冲数据丢失 2. **错误处理**: - 标准IO通过`ferror()`检测错误 - 文件IO通过`errno`全局变量获取错误码 3. **缓冲区控制**:可通过`setvbuf()`修改标准IO缓冲策略[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值