如果只需要代码,就直接拿取最底部的一段代码即可
当我们使用window.resize mousemove等事件的时候,会非常频繁的触发,对于的函数,如果还要操作dom元素的,将会十分的消耗性能的,这时候就需要节流,防抖来解决。
函数防抖(debounce)
这里用一个例子,来进行讲解
假设我在一个输入框里输入内容,他会自动的根据输入框的内容给我显示搜索的结果。 但是这里的要求是在输入完成后,等待一段时间再触发搜索的功能,并且在这段时间还没到达的时候,如果在次进行输入,那么就要重新开始计时。
不进行处理的显示
js
复制代码
//html <div class="container"> <input id="input" type="text"> <div id="content"> </div> </div> //js //假设这是请求函数 function getData(value) { console.log("runs"); return `搜索${value}的内容`; } const input = document.getElementById("input"); const content = document.getElementById("content"); input.oninput = function (e) { content.innerText = getData(input.value); };
显示结果。这里可以看到每进行一次输入就会触发,请求函数,如果是真实的api接口,就过度浪费
基础版防抖函数
这里是通过全局变量来存储setTimeout的id,再每次触发函数的时候,就请求之前的setTimeout(也可以用if来判断是否要清除),然后再开启一个新的setTimeout来执行函数
js
复制代码
let debounce_timer; /** * 防抖函数 * @param {*} callback 需要运行的函数 * @param {*} duration 需要等待的时间 */ function debounce(callback, duration) { clearTimeout(debounce_timer); debounce_timer = setTimeout(function () { callback(); debounce_timer = null; }, duration); } //请求函数 function getData(value) { console.log("runs"); return `搜索${value}的内容`; } const input = document.getElementById("input"); const content = document.getElementById("content"); input.oninput = function (e) { debounce(function () { content.innerText = getData(input.value); }, 1000); };
最终结果,这里已经基本符合我们的要求了,但是采用这种方法也会带来一个问题就是全部变量的污染,所以接下来我们继续优化节流函数
进阶版防抖函数
首先,讲解一下高阶函数的定义,就是当一个函数的返回值是一个函数的话,那么它就是高阶函数
之前的节流函数的问题是会污染全局变量,于是我们就直接将,debounce_time变量,放在debounce函数内部,将功能函数给return出去。这里要注意被return出去的函数,再被调用的时候,依旧访问的是他debounce函数作用域里面的debounce_time变量。(这里需要学习一下,闭包与作用域的知识,我也会逐步更新的),所以每次清空的依旧是同一个debounce_time变量。采用了一个很巧妙的方法来解决的。
js
复制代码
/** * 防抖函数 * @param {*} callback 需要运行的函数 * @param {*} duration 需要等待的时间 * @returns 返回一个函数 */ function debounce(callback, duration) { var debounce_timer; return function () { clearTimeout(debounce_timer); debounce_timer = setTimeout(function () { callback(); debounce_timer = null; }, duration); }; } //请求函数 function getData(value) { console.log("runs"); return `搜索${value}的内容`; } const input = document.getElementById("input"); const content = document.getElementById("content"); // 这里不能放在input事件里面,否则返回的函数,就不是同一个作用域里面的了 let handleSearch = debounce(function () { content.innerText = getData(input.value); }, 1000); input.oninput = function (e) { handleSearch(); };
来看执行结果,依旧没有问题
但是这里还有最后一个问题,就是传参
再这段代码中,我们不处理debounce函数,假设我需要一些参数,来完善处理函数,直接传递是拿不到的
js
复制代码
let handleSearch = debounce(function (res) { console.log("我需要参数来处理,该函数,参数是" + res); content.innerText = getData(input.value); }, 1000); input.oninput = function (e) { handleSearch("参数1", "参数2"); };
我们继续完善debounce函数,拿不到参数是因为,给handleSearch传递的参数,最终是debounce函数中,return 的那个函数拿到的,而res的形参,需要debounce函数中的callback函数,传参才可以的
所以这里我们首先通过 debounce函数的 return 函数,的argument拿到传递的参数,(用arguments获取的原因,如果传递多个参数,可以直接用arguments获取到,而不用写多个形参),再讲arguments的值传递给callback函数,这里用apply的原因,apply的第二个参数是传递实参,并且是以数组的格式。
js
复制代码
/** * 防抖函数 * @param {*} callback 需要运行的函数 * @param {*} duration 需要等待的时间 * @returns 返回一个函数 */ function debounce(callback, duration) { var debounce_timer; return function () { let args = arguments; clearTimeout(debounce_timer); debounce_timer = setTimeout(function () { callback.apply(null, args); debounce_timer = null; }, duration); }; } //请求函数 function getData(value) { console.log("runs"); return `搜索${value}的内容`; } const input = document.getElementById("input"); const content = document.getElementById("content"); // 这里不能放在input事件里面,否则返回的函数,就不是同一个作用域里面的了 let handleSearch = debounce(function (res) { console.log("我需要参数来处理,该函数,参数是" + res); content.innerText = getData(input.value); }, 1000); input.oninput = function (e) { handleSearch("参数1", "参数2"); };
这里看结果,最终拿到参数了的,这里是用的arguments来拿取,也可以通过解构的方法实现,更为简单
js
复制代码
function debounce(callback, duration) { var debounce_timer; return function (...args) { if (debounce_timer) { clearTimeout(debounce_timer); } debounce_timer = setTimeout(function () { callback(...args); }, duration); }; }
函数节流
函数节流是在固定的时间里执行一次
下面是一个输入框,我会不断地进行输入,但是我需要每过1s,才打印输入的内容
用时间戳实现函数节流
js
复制代码
// 首先获取到input,并且给他绑定键盘点击事件,然后,打印输入的内容 const input = document.getElementById("input"); let res = throttle(() => { console.log(input.value); }, 2000); input.onkeydown = function () { res(); }; //节流函数 function throttle(callback, duration) { let defaultTime = 0; //定义最开始默认的时间 return function (...args) { var nowTime = new Date(); //获取当前时间戳 if (nowTime - defaultTime > duration) { // 如果当前时间戳,前去之前的时间大于我们传递的等待时间,就可以执行代码 callback(...args); defaultTime = nowTime; } }; }
计时器实现
js
复制代码
function throttle(callback, duration) { let timer; //用于记录计时器 return function (...args) { if (!timer) { //如果计时器为空,代表duration时间到了,可以执行 callback(...args); timer = setTimeout(() => { timer = null;//当duration时间到达,清空timer }, duration); } }; }
作者:superyyy
链接:https://juejin.cn/post/7247058712194842680
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。