请实现一个节流函数
什么是节流函数?
简单讲就是让一个函数无法在短时间内连续调用,只有当上一次函数执行后过了规定的时间间隔,才能进行下一次该函数的调用。或者说你在操作的时候不会马上执行该函数,而是等你不操作的时候才会执行。
函数节流的原理:
可通过使用定时器,在操作的时候让函数延时执行,如果在这个时间内还在操作,则清除原来的定时器,再创建一个新的定时器执行。
函数节流非常适用于函数被频繁调用的场景,例如:window.onresize() 事件、mousemove 事件、上传进度等情况。使用 throttle API 很简单,那应该如何实现 throttle 这个函数呢?
实现的方案有以下两种:
- 第一种是用时间戳来判断是否已到执行时间,记录上次执行的时间戳,然后每次触发事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经达到时间差(Xms) ,如果是则执行,并更新上次执行的时间戳,如此循环。
- 第二种方法是使用定时器,比如当 scroll 事件刚触发时,打印一个 hello world,然后设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。
这里我们采用第一种方案来实现,通过闭包保存一个 previous 变量,每次触发 throttle 函数时判断当前时间和 previous 的时间差,如果这段时间差小于等待时间,那就忽略本次事件触发。如果大于等待时间就把 previous 设置为当前时间并执行函数 fn。
我们来一步步实现,首先实现用闭包保存 previous 变量。
const throttle = (fn, wait) => { // 上一次执行该函数的时间 let previous = 0 return function(...args) { console.log(previous) ... } }
执行throttle函数后会返回一个新的function,我们命名为betterFn。
const betterFn = function(...args) { console.log(previous) ... }
betterFn 函数中可以获取到 previous 变量值也可以修改,在回调监听或事件触发时就会执行 betterFn,即 betterFn()
,所以在这个新函数内判断当前时间和 previous 的时间差即可。
const betterFn = function(...args) { let now = +new Date(); if (now - previous > wait) { previous = now // 执行 fn 函数 fn.apply(this, args) } }
结合上面两段代码就实现了节流函数,所以完整的实现如下。
// fn 是需要执行的函数 // wait 是时间间隔 const throttle = (fn, wait = 50) => { // 上一次执行 fn 的时间 let previous = 0 // 将 throttle 处理结果当作函数返回 return function(...args) { // console.log(previous) // 获取当前时间,转换成时间戳,单位毫秒 let now = +new Date() // 将当前时间和上一次执行函数的时间进行对比 // 大于等待时间就把 previous 设置为当前时间并执行函数 fn if (now - previous > wait) { previous = now //通过apply还原this指向事件 fn.apply(this, args) } } // DEMO // 执行 throttle 函数返回新函数 const betterFn = throttle(() => console.log('fn 函数执行了'), 1000) console.log(betterFn) // 每 10 毫秒执行一次 betterFn 函数,但是只有时间差大于 1000 时才会执行 fn setInterval(betterFn, 1000) }
转自:https://www.cnblogs.com/zhangycun/p/10949450.html
<body> <button class="demo">按钮</button> <script> function debounce(func, delay) { let timer; // 通过闭包使timer一直存在内存中 return function (...args) { //清除1秒之内重复点击的事件 if (timer) { console.log(timer) clearTimeout(timer); } //重新生成定时器 /*setTimeout() 方法的返回值是一个唯一的数值,这个数值有什么用呢?如果你想要终止 setTimeout() 方法的执行,那就必须使用 clearTimeout() 方法来终止,而使用这个方法的时候,系统必须知道你到底要终止的是哪一个 setTimeout() 方法 (因为你可能同时调用了好几个 setTimeout() 方法),这样 clearTimeout() 方法就需要一个参数,这个参数就是 setTimeout() 方法的返回值 (数值),用这个数值来唯一确定结束哪一个 setTimeout() 方法。 */ timer = setTimeout(() => { func.apply(this, args); //通过apply还原this指向事件 }, delay) console.log(timer) } } // 点击元素1秒后执行,如果1秒内重复点击会清空之前定时,重新生成定时器! document.querySelector('.demo').addEventListener('click',debounce((e)=>{ // 需要执行的代码 console.log(e); },1000),false); </script> </body>