前端节流和防抖(上)

节流和防抖(上)

节流和防抖,共性都是对短时间会大量触发浏览器事件的优化

防抖

 //防抖debounce
    /* 在用户触发事件的时间间隔小于(规定时间段)时候才触发函数
        简单来说,就是用户只要触发事件时间比设定的时间段短,那函数就不会触发
        个人粗鄙理解(只要我动得够快,你就不会触发)
    */
<input class="inp" type="text" />
//应用场景,我要在用户输入停止1s后,才打印输出用户的输入内容
    // 防抖函数 (第一版,简易版)
    function debounce(fn, delay) {
        let timer = null;
        return function () {
            // 如果timer存在,清除
            if (timer) {
                clearTimeout(timer)
            }
            // 开规定事件的定时器
            timer = setTimeout(() => {
                fn();
            }, delay)
        }
    }
	
	//获取元素
	let input = document.querySelector('.inp');
	//展示
	 function showVal() {
        console.log('input .value', input .value);
    }
	
	//绑定事件
	input.oninput = debounce(showVal, 1000);
但是简易版中,使用this的时候就会有问题
 function showVal() {
        console.log('this.value', this.value); //输出undefined
    }
//原因在于在闭包返回函数后,this已经指向了window

//解决方法
    // 防抖函数 (第二版,修正this版)
    function debounce(fn, delay) {
        let timer = null;
        return function () {
        		
        	//1.在这里保存函数执行的上下文环境
        	const content = this;
        	
            // 如果timer存在,清除
            if (timer) {
                clearTimeout(timer)
            }
            // 开规定事件的定时器
            timer = setTimeout(() => {
            
            //2.在这里改变函数执行的上下文环境
                fn.call(this);
            }, delay)
        }
    }
在第二版中,我们修正了this,但是对于事件参数并没有修正
 function showVal(e) {
        console.log(e.target);
    }

使用第二部的代码执行上面函数时候会报错
在这里插入图片描述

这个时候就需要第三版

    // 防抖函数 (第三版,修正event参数)
    function debounce(fn, delay) {
        let timer = null;
        return function () {
        		
        	
        	const content = this;
        	//1.在这里保存函数的参数列表
        	const args = arguments;
        	
            // 如果timer存在,清除
            if (timer) {
                clearTimeout(timer)
            }
            // 开规定事件的定时器
            timer = setTimeout(() => {
            
            //2.在这里把函数的参数列表作为参数传递进去
                fn.apply(this,args);
            }, delay)
        }
    }
在这里就会有同学问了,为什么在第三版用apply,不用call呢?

我们可以输出arguments看一下=_=
在这里插入图片描述
很明显arguments是一个参数列表

//函数call和apply都是改变函数的指向,但是参数的传递不一样
fn.call(content,param1,param2...)
fn.apply(conent,[param1,param2...])
//两者的第一个参数都是函数执行的上下文环境,但是后面call是一个个的参数传递,apply是一个参数列表的形势传递

//第三部 使用call传递也是可以的
 fn.apply(this,...args); //使用es6 的...语法将参数列表解构出来

在这里插入图片描述

<think>嗯,用户现在想了解前端开发中节流防抖的原理、实现及应用。首先,我需要回顾之前提供的引用内容,确保准确理解这两个概念。引用[1]提到防抖用于延迟执行,节流用于限制频率,两者都能减少性能开销。引用[2]详细说明了防抖是延迟执行并重新计时,而节流是固定时间只执行一次。引用[3][4]则强调它们在处理高频事件中的作用,比如输入、滚动等场景。引用[5]补充了它们解决频繁事件导致的性能问题。 接下来,用户的需求是原理、实现应用。需要分三部分来组织回答。先讲防抖的原理:事件触发后延迟执行,期间重复触发则重置计时。引用[2]中的描述需要准确转述。然后是节流的原理,固定时间间隔内只执行一次,引用[2][4]的内容支持这点。 实现部分,需要提供代码示例。防抖的实现可以用setTimeoutclearTimeout,关键点在于闭包保存timer。节流则可以用时间戳或定时器,比如时间戳方式记录上次执行时间,比较当前时间差。引用中的例子可能不够详细,需要自己补充完整代码,并注释说明。 应用场景方面,防抖适合搜索建议、窗口调整,节流适合滚动事件、按钮点击。引用[1][4]提到输入框滚动,引用[5]也提到了滚动输入。可能需要举例说明,比如搜索框输入停止后才请求API,滚动时每100ms更新位置。 组合使用的情况是否需要提及?比如同时需要防抖节流的情况,但用户的问题可能更关注基础,可以先放在扩展问题里。 注意事项部分,引用中提到优化性能,但具体注意事项如清除定时器、this绑定可能用户需要知道。比如防抖中最后一次是否执行,节流的立即执行选项。 扩展问题方面,用户可能想知道区别、实际案例、组合使用、参数调整、框架集成等。根据引用[1]的组合使用建议,可以提出相关问题。 </think>好的,我们来详细解析前端开发中的防抖(Debounce)节流(Throttle)技术,涵盖其原理、实现方式典型应用场景。这些技术对于优化高频事件处理、提升性能至关重要。 **一、 基本原理** 1. **防抖 (Debounce)** * **原理:** 当一个事件被连续频繁触发时,防抖函数会设定一个**等待时间(延迟)**。只有在事件**停止触发**且**超过这个等待时间后**,目标函数才会被执行一次。如果在等待时间内事件又被触发了,则**重新开始计时等待**。 * **核心思想:** “等你折腾够了(停止触发)并且安静一会儿(等待时间结束)后,我再动手做一次”。它关注的是**事件触发序列的结束**。 * **类比:** 电梯关门。当有人不断进出电梯(连续触发事件),电梯门不会关闭(不会执行函数)。只有当最后一个人进入并间隔几秒没人再进出(等待时间结束),电梯门才会关闭(执行函数)。如果在这几秒内又有人要进(重新触发事件),电梯门会重新打开并重新等待(重新计时)[^2][^4]。 2. **节流 (Throttle)** * **原理:** 当一个事件被连续频繁触发时,节流函数会设定一个**固定的时间间隔**。在这个时间间隔内,无论事件被触发了多少次,目标函数**最多只被执行一次**。执行后,在下一个时间间隔到来之前,后续触发将被忽略。 * **核心思想:** “不管你触发得多疯狂,我只会按照固定的节奏干活”。它关注的是**限制函数执行的最大速率**。 * **类比:** 水龙头滴水。即使你一直拧着水龙头(连续触发事件),水(函数执行)只会以固定的、间隔性的水滴(固定时间间隔)落下。在两次水滴之间,拧水龙头的动作(触发事件)不会产生新的水滴(执行函数)[^2][^4]。 **二、 实现方式 (JavaScript 示例)** 1. **防抖实现 (基础版)** ```javascript function debounce(func, delay) { let timeoutId; // 保存定时器ID return function(...args) { // 返回的函数是被实际绑定到事件上的处理函数 const context = this; // 保存当前this上下文 // 如果之前有定时器(意味着等待期内又被触发),清除它,重新计时 clearTimeout(timeoutId); // 设置新的定时器,在delay时间后执行目标函数 timeoutId = setTimeout(() => { func.apply(context, args); // 使用保存的this参数执行目标函数 }, delay); }; } ``` 2. **节流实现 (定时器版)** ```javascript function throttle(func, interval) { let lastExecTime = 0; // 记录上次执行的时间戳 let timeoutId; // 保存定时器ID (用于尾调用) return function(...args) { const context = this; const now = Date.now(); // 当前时间戳 // 计算距离上次执行已经过去的时间 const elapsed = now - lastExecTime; // 如果距离上次执行时间小于间隔,并且没有设定延迟执行(尾调用)的定时器 if (elapsed < interval && !timeoutId) { // 如果之前没有定时器,设置一个定时器,在剩余间隔时间后执行 timeoutId = setTimeout(() => { lastExecTime = Date.now(); // 更新执行时间 timeoutId = null; // 清除定时器ID func.apply(context, args); }, interval - elapsed); return; // 本次触发不执行,等待定时器 } // 清除可能存在的定时器(可能是上次设置的尾调用) clearTimeout(timeoutId); timeoutId = null; // 如果距离上次执行时间已经大于等于间隔,立即执行 lastExecTime = now; func.apply(context, args); }; } ``` * **说明:** 这个实现结合了**时间戳检查(立即执行)** **定时器(尾调用)**。确保在间隔开始时能立即执行一次(如果距上次执行足够久),并且在间隔结束时触发最后一次调用(如果期间有触发)。这是比较完善的实现。也可以使用纯时间戳版(只保证立即执行,不保证尾调用)或纯定时器版(只保证尾调用,不保证立即执行)。 **三、 应用场景对比** | 技术 | 典型应用场景 | 目的 | 引用依据 | | :----- | :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------- | :------------------------------------------------------------------------------ | | **防抖** | **1. 搜索框输入建议**:用户停止输入一段时间(如300ms)后才发送请求获取建议,避免每按一个键都请求。<br>**2. 窗口大小调整 (resize)**:用户停止调整窗口大小后再计算布局/重绘,避免调整过程中频繁计算。<br>**3. 提交按钮防重复点击**:点击后禁用按钮或使用防抖,防止用户快速多次提交。 | **延迟执行**,确保只在连续操作结束后执行一次,节省资源(如网络请求、DOM操作)。 | [^1]: "防抖适用于延迟执行的场景"<br>[^4]: "输入框输入"<br>[^5]: "用户输入" | | **节流** | **1. 页面滚动 (scroll)**:滚动过程中,定期(如每100ms)更新位置信息或执行懒加载/无限滚动检查,避免滚动时像素级触发。<br>**2. 鼠标移动 (mousemove)**:如拖拽元素时,限制元素位置更新的频率,使其更平滑。<br>**3. 高频点击事件**:防止用户疯狂点击按钮导致逻辑混乱或过多请求。<br>**4. Canvas 绘图**:限制鼠标移动绘图的密度。 | **限制频率**,确保函数以可接受的、可控的速率执行,避免卡顿。 | [^1]: "节流适用于限制执行频率的场景"<br>[^4]: "窗口滚动、鼠标移动"<br>[^5]: "窗口滚动" | **四、 关键区别总结** * **执行时机:** * 防抖:连续触发停止后,延迟一段时间才执行。 * 节流:按照固定的时间间隔执行(可能在连续触发开始时就执行一次,也可能在中间或结束时执行,取决于实现)。 * **关注点:** * 防抖:关注**“连续触发结束”**。 * 节流:关注**“执行频率”**。 * **效果:** * 防抖:合并多次触发为一次最终执行。 * 节流:稀释触发的频率,确保有规律地执行。 **五、 选择与注意事项** * **选择依据:** 仔细分析需求。需要的是“最终结果”(如搜索关键词)用**防抖**;需要的是“过程控制”或“限制速率”(如滚动更新)用**节流**。 * **参数调整:** `delay` (防抖) `interval` (节流) 是关键参数,需要根据具体场景调整。太短起不到优化作用,太长影响响应性。 * **this 参数:** 实现时需确保目标函数执行时的 `this` 指向正确,并能接收事件参数 (`...args`)。 * **立即执行选项:** 防抖有时需要支持首次触发立即执行(例如搜索框希望用户一开始输入就有建议),节流通常需要支持首次立即执行(如滚动位置初始化)。上面的基础防抖是延迟执行的,可以扩展实现立即执行版。 * **尾调用:** 节流的实现需要决定是否保证在触发结束后的最后一个触发也能被执行(上面的实现保证了这一点)。 * **框架集成:** 现代前端框架(React, Vue)通常有内置指令(如 Vue 的 `v-debounce`, `v-throttle`)或社区库(如 Lodash 的 `_.debounce`, `_.throttle`)提供更完善的实现。 **总结:** 防抖节流前端性能优化的利器,它们通过不同的策略(延迟执行 vs 频率限制)有效管理高频事件,减少不必要的函数调用、DOM操作网络请求,从而提升用户体验系统性能[^1][^2][^3][^4][^5]。理解其原理、熟练掌握实现细节、并准确应用于合适的场景,是前端开发者必备的技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值