Comments (45)
本题我重点从节流的作用、概念应用场景、手写节流函数方面回答。
一、节流(throttle)的概念
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,列入水管的水滴在一定的间隔时间滴一滴水。节流是高频触发函数时,但在n秒内也就是约定时间内只会执行一次,节流会稀释函数的执行频率。比如点击发送验证码按钮时,按钮会变成禁用,当到了约定时间比如60秒后按钮才能变成可点击状态,才能再次发送验证码,调用函数。每次触发事件时,如果当前有等待执行的延时函数,则直接return。
二、节流的作用
节流就是在一定时间间隔内触发一次事件。在不影响功能的前提下,减少触发事件的频率,提升性能,减少浏览器或者服务器的压力。
三、应用场景
1、滚动页面
2、商品预览图的放大镜效果时,不必每次鼠标移动都计算位置
3、API的调用
4、按钮点击事件/input事件,防止用户多次重复提交
四、手写节流
- 实现思路
调用函数时,通过上一次pre和现在now两个变量,记录调用时间的频率,prev-now 如果大于约定的时间,才调用函数。调用函数结束后,把pre设置为现在的时间。
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
五、参考文章
结语
我的回答如有不对的地方,麻烦务必指出来,我及时改正,以免误导别人,让我们共同进步吧!
from step-by-step.
1、节流(throttle)函数的作用是什么?有哪些应用场景,请实现一个节流函数
节流函数:在事件被触发n秒后再执行的回调,如果在这n秒内又重新被触发,则重新开始计时
作用:避免函数频繁调用
区别在于,当事件持续被触发,如果触发时间间隔短于规定的等待时间(n秒),那么
函数防抖的情况下,函数将一直推迟执行,造成不会被执行的效果;
函数节流的情况下,函数将每个 n 秒执行一次
应用场景:
1. window对象的resize、scroll事件
2. 拖拽时的mousemove事件
3. 射击游戏中的mousedown、keydown事件
4. 文字输入、自动完成的keyup事件
实现方式:时间戳或定时器
时间戳
只要触发,就用 Date 获取现在的时间,与上一次的时间比较。
如果时间差大于了规定的等待时间,就可以执行一次;
目标函数执行以后,就更新 previous 值,确保它是“上一次”的时间。
否则就等下一次触发时继续比较
function throttle(func, wait) {
let previous = 0;
return function() {
let now = +new Date();
let context = this;
if (now - previous >= wait) {
func.apply(context, arguments);
previous = now; // 执行后更新 previous 值
}
}
}
eg:
container.onmousemove = throttle(doSomething, 1000);
定时器
用定时器实现时间间隔。
当定时器不存在,说明可以执行函数,于是定义一个定时器来向任务队列注册目标函数
目标函数执行后设置保存定时器ID变量为空
当定时器已经被定义,说明已经在等待过程中。则等待下次触发事件时再进行查看。
若要防抖,就马上取消它,重新定义计时器以重新计时
function throttle(func, wait) {
let time, context
return function(){
context = this
if(!time){
time = setTimeout(function(){
func.apply(context, arguments)
time = null
}, wait)
}
}
}
效果差异
一个周期内:
时间戳实现的:先执行目标函数,后等待规定的时间段;
计时器实现的:先等待够规定时间,再执行。
即停止触发后,若定时器已经在任务队列里注册了目标函数,它也会执行最后一次。
优化:两者结合
参考
function throttle(func, wait, options) {
let time, context, args, result;
let previous = 0;
if (!options) options = {};
let later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
time = null;
func.apply(context, args);
if (!time) context = args = null;
};
let throttled = function() {
let now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (time) {
clearTimeout(time);
time = null;
}
previous = now;
func.apply(context, args);
if (!time) context = args = null;
} else if (!time && options.trailing !== false) {
time = setTimeout(later, remaining);
}
};
return throttled;
}
在 函数防抖(debounce)中,实现了一个 cancel 方法。同理:
throttled.cancel = function() {
clearTimeout(time);
time = null;
previous = 0;
}
参考
mqyqingfeng/Blog#26
https://blog.csdn.net/beijiyang999/article/details/79836463
emmmm.....手写函数什么的还是需要多练习,只能参考别人的。。
from step-by-step.
函数节流以及其使用场景
节流: 节流就是流体在管道内流动,突然遇到截面变窄,而使压力下降的现象。节制流入或流出,尤指用节流阀调节。
出处:语出《荀子·富国》:“百姓时和、事业得叙者,货之源也; 等赋府库者,货之流也。故明主必谨养其和,节其流,开其源,而时斟酌焉,潢然使天下必有馀而上不忧不足。”后以“开源节流”指开辟财源,节约开支。
其实作用跟定义就是上面的意思,在日常开发中,我们经常会有频繁调用或执行某一函数的需求,其实按实际用户情况,可能 100ms 甚至 1s 调用一次就OK,所以需要用到函数节流,在一定的时间间隔之后再执行函数。
场景很多,常见的有:
-
屏幕尺寸变化时页面内容的变动,执行相应逻辑;
-
监听鼠标滚动时间,执行相应逻辑;
-
监听重复点击时的时间,执行相应逻辑
下面依然是 lodash
的代码实现:
function isObject(value) {
const type = typeof value
return value != null && (type == 'object' || type == 'function')
}
function throttle(func, wait, options) {
let leading = true
let trailing = true
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading
trailing = 'trailing' in options ? !!options.trailing : trailing
}
return debounce(func, wait, {
leading,
trailing,
'maxWait': wait,
})
}
使用方法如下:
var 打印一些东西啦 = function (event) {
console.log(event);
};
document.querySelector('html').addEventListener('click', throttle(打印一些东西啦, 1000));
from step-by-step.
什么是节流
节流用大白话说就是,在一定的时间周期内这件事儿就只做一次。
以昨天“包治百病”为例,说十天为周期给女朋友买包,如果在这个十天的周期内如果多次要包,怎么着也是不能给买的,要求其严格遵守十天这个期限。如果十天期限一过,女朋友要求买包,这个是肯定可以的。
实际应用
在实际的开发中会有这样一个需求,用户输入查询关键词,点击搜索按钮后提交搜索关键词从服务器获取搜索数据,讲搜索结果展示到页面当中。在这个过程中会有一系列的操作,如数据的提交,关键词匹配,数据查询,数据传输等,这些相对都会占据一定的资源,如果用户的网络不稳定,则会增加查询过程中的等待时间。如果用户等不到自己想要的查询结果就会多次点击i提交按钮,这个时候就需要做节流处理,当用户点击搜索按钮的时候判断数据是否在获取过程中,如果是则不进行搜索提交,如果否则提交关键词,进行搜索。
实现节流
实现节流就需要使用时间戳进行控制,具体实现代码如下:(当然这里也可以参照昨天的防抖设置立即执行和延时执行,这里偷懒了。。。[罪过])
function throttle(fn, ...arg) {
let run = false;
return function () {
if (run) return;
run = true;
setTimeout(function () {
fn.applly(this, arg);
run = false;
}, 1000);
}
}
from step-by-step.
函数节流: 指定时间间隔内只会执行一次任务;
以判断页面是否滚动到底部为例,普通的做法就是监听 window 对象的 scroll 事件,然后再函数体中写入判断是否滚动到底部的逻辑:
$(window).on('scroll', function() {
// 判断是否滚动到底部的逻辑
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height(),
thresold = pageHeight - scrollTop - winHeight;
if (thresold > -100 && thresold <= 20) {
console.log('end');
}
});
这样做的一个缺点就是比较消耗性能,因为当在滚动的时候,浏览器会无时不刻地在计算判断是否滚动到底部的逻辑,而在实际的场景中是不需要这么做的,在实际场景中可能是这样的:在滚动过程中,每隔一段时间在去计算这个判断逻辑。而函数节流所做的工作就是每隔一段时间去执行一次原本需要无时不刻地在执行的函数,所以在滚动事件中引入函数的节流是一个非常好的实践:
// 添加节流函数
function throttle(fn, interval = 300) {
let canRun = true;
return function() {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval);
};
}
$(window).on(
'scroll',
throttle(function() {
// 判断是否滚动到底部的逻辑
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height(),
thresold = pageHeight - scrollTop - winHeight;
if (thresold > -100 && thresold <= 20) {
console.log('end');
}
})
);
加上函数节流之后,当页面再滚动的时候,每隔 300ms
才会去执行一次判断逻辑。
简单来说,函数的节流就是通过闭包保存一个标记(canRun = true
),在函数的开头判断这个标记是否为 true
,如果为 true
的话就继续执行函数,否则则 return 掉,判断完标记后立即把这个标记设为 false
,然后把外部传入的函数的执行包在一个 setTimeout
中,最后在 setTimeout
执行完毕后再把标记设置为 true
(这里很关键),表示可以执行下一次的循环了。当 setTimeout
还未执行的时候,canRun
这个标记始终为 false
,在开头的判断中被 return 掉。
防抖和节流都是为了避免事件的频繁触发,有些事件无论是防抖还是节流都做到性能上的优化,要根据业务需求选择合适的方案。
今日打卡完毕,首次沙发 嘻嘻嘻
from step-by-step.
函数节流: 指定时间间隔内只会执行一次任务;
场景的运用 => 滚动条滚动到底部
function throttle(fn, interval) {
let flag= true;
return function() {
if (!flag) return;
flag= false;
setTimeout(() => {
fn.apply(this, arguments);
flag= true;
}, interval);
};
}
from step-by-step.
函数节流,是指如果一个事件不停地触发,那么就每隔一定的时间才执行回调函数,回调函数的执行会被稀释。
应用场景:
函数节流典型的应用场景就是图片懒加载,用户下啦页面,需要监听wheel事件,然后判断是否应该展示图片。这个判断就可以用函数节流来做,没必要一直判断。
实现:
- 时间戳方式
function throttle(fn, delay) {
let pre = 0;
return function() {
let now = Date.now();
if ((now - pre) >= delay) {
fn.apply(this, arguments);
pre = now;
}
}
}
- 定时器版
function throttle(fn, delay) {
return function() {
if (!throttle.timer) {
fn.apply(this, arguments);
}
throttle.timer = setTimeout(function() {
throttle.timer = true;
}, delay);
}
}
from step-by-step.
节流函数的作用:降低函数的调用频率,间隔一段时间执行,防止某一时间频繁触发,减少不必要的计算,不浪费资源;
应用场景:1、元素拖拽功能实现;2、滚动事件;3、鼠标不断点击,单位时间内只触发一次;
/**
- fn:延时调用函数
- delay:延迟多长时间
- mustRun:至少多长时间触发一次
*/
function throttle(fn, delay, mustRun){
var timer = null, prev = null
return function(){
var now = +new Date(), context = this, args = arguments;
if(!prev) prev = now;
var wait = now - prev;
if(mustRun && wait >= mustRun){
fn.apply(context, args);
prev = now
}else{
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
}
}
}
window.onmousedown = throttle(function(){
console.log(+new Date())
}, 2000, 2000)
from step-by-step.
看到一个总结挺好的,分享下
防抖和节流的区别:函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。
from step-by-step.
节流函数作用
节流函数和防抖函数一样,都是用于处理高频事件的,优化性能。
节流函数是在规定时间段内只执行一次。
节流函数应用场景
1、鼠标点击事件
节流函数实现
/**
* 节流函数
* @param fn 延迟执行函数
* @param delay 延迟时间
* @param atleast 规定多长时间触发一次
*/
function throttle(fn, delay, atleast) {
let timer = null;
let previous = null;
return function() {
let context = this, args = arguments;
let now = +new Date();
if (!previous) previous = now;
if (now - previous < atleast) {
fn.apply(context, args);
previous = now;
} else {
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args)
}, delay)
}
}
}
from step-by-step.
节流
节流 就是指定时间间隔内 只执行一次
作用
提升性能 避免必要的资源浪费 提高用户体验
场景
重复点击 分页滚动底部的监听比较常见
实现过程
使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳,
如果大于设置的时间周期,就执行函数,然后把时间戳更新为当前的时间戳,如果小于,就不执行
jieliu(fn, delay) {
let _arg;
let previous = 0;
return function(arg) {
that = this;
_arg = arg;
let now = +new Date();
if (now - previous > delay) {
fn.call(this, _arg);
previous = now;
}
};
}
from step-by-step.
背景
事件的触发权很多时候都属于用户,有些情况下会产生问题:
- 向后台发送数据,用户频繁触发,对服务器造成压力
- 一些浏览器事件:window.onresize、window.mousemove等,触发的频率非常高,会造成浏览器性能问题如果你碰到这些问题,那就需要用到函数节流和防抖了。
函数节流 (throttle)
- 一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
有个需要频繁触发函数,出于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效。
应用场景
需要间隔一定时间触发回调来控制函数调用频率:
- DOM 元素的拖拽功能实现(mousemove)
- 搜索联想(keyup)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
节流函数
function throttle(fn, delay) {
let pre = 0;
return function() {
let now = Date.now();
if ((now - pre) >= delay) {
fn.apply(this, arguments);
pre = now;
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
from step-by-step.
概念:函数的节流就是预定一个函数只有在大于等于执行周期时才会执行,周期内调用不会执行。好像一滴水只有积攒到一定重量才会落下一样。
场景:窗口调整(resize)、页面滚动(scroll)、抢购疯狂点击(movedown)
function scrollFn(){
console.log(1)
}
function throttle(method,delay,duration){
var timer=null;
var begin=new Date();
return function(){
var context=this, args=arguments;
var current=new Date();
clearTimeout(timer);
if(current-begin>=duration){
method.apply(context,args);
begin=current;
}else{
timer=setTimeout(function(){
method.apply(context,args);
},delay);
}
}
}
window.onscroll=throttle(scrollFn,100,500)
from step-by-step.
节流: 指的是高频事件在规定时间内只执行一次,执行一次后,只有大于设定的执行周期后才会执行第二次。
应用场景:
实现dom元素的拖拽功能。
计算鼠标移动的距离。
监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次。
`
var processor = {
timeoutId: null,
//实际进行处理的方法
performProcessing: function(){
//实际执行的代码
},
//初始处理调用的方法
process: function(){
clearTimeout(this.timeoutId);
var that = this;
this.timeoutId = setTimeout(function(){
that.performProcessing();
}, 100);
}
};
//尝试开始执行
processor.process();
`
from step-by-step.
概念方面先跟前面的防抖区分开。
函数防抖是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。
函数节流是指一定时间内js方法只跑一次。大于设定的时间再跑第二次。
同样的改变窗口大小的效果,我们看防抖和节流出现的效果有什么不一样。
//防抖
var windowResize = (function (){
var a = 0;
return function(){
a++;
console.log(a);
}
})()
$(window).on("resize", _.debounce(windowResize, 300, {
'leading': false,
'trailing': true
}))
效果是我在停止拖动浏览器窗口的时候才会触发windowResize 方法。
// 节流
var windowResize = (function (){
var a = 0;
return function(){
a++;
console.log(a);
}
})()
$(window).on("resize", _.throttle(windowResize, 300, {
'leading': false,
'trailing': true
}))
节流展示的效果更像一个轮询,每隔300ms就会执行一次。在拉动窗口变化执行函数的性能优化上,防抖优于节流
from step-by-step.
节流就是在规定的时间间隔内触发一次事件。在不影响功能的前提下,避免一定时间内多次执行某个事件,提升性能,减少浏览器或者服务器的压力。
还是以“包治百病”为例: 节流就是女朋友耍无赖,还特别馋,我不管这10天有没有吃过零食,每隔10天,你就要给我买一次包(败家)。防抖呢:只有连续的10天一次没有吃过零食,才能买一次包,否则每吃一次零食,就从头开始就算天数,哈哈..(尽量找这样的女朋).
应用场景:
实现dom元素的拖拽功能
在页面滚动时判断是否滚动的页面底部,来加载更多
Canvas画笔功能
function throttle(fn, delay = 500) {
var prev = Date.now();
return function () {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
fn.apply(context, args);
prev = Date.now();
}
}
}
window.addEventListener("scroll", function () {
console.log("对比一下节流")
})
window.addEventListener("scroll", throttle(function () {
console.log("节流:滚动条是否滚动到页面底部")
}))
from step-by-step.
节流函数就是固定时间段内只触发一次,比如轮播图的点击,发送短信验证码等
from step-by-step.
函数节流
含义: 当持续触发事件时,保证一定时间段只调用一次事件处理函数
代码实现
function throttle (func, delay) {
let prev = new Date().getTime()
return function (args) {
let now = new Date().getTime()
const that = this
if (math.floor((now - pre) / 1000) ->delay) {
func.apply(that, args)
pre = new Date().getTime()
}
}
}
函数节流保证,一定时间只执行一次函数,当有一些事情需要每隔一段时间就执行的话,节流就可以派上用场了,现实工作中遇到的防抖比较多,节流的反而会少点,这方面体会会比较少
from step-by-step.
函数节流,背后基本**是指:某些代码不可以没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数请求停止了一段时间之后才执行。
function throttle(fn, context){
clearTimeout(fn.tId);
fn.tId = setTimeout(function(){
fn.apply(context, arguments)
},200)
}
节流在resize事件中是最常用的,如下案例,设置一个div的高度适中和宽度保持一致
案例:
function resizeDiv(){
var div = document.getElementById('#myDiv');
div.style.height = div.offsetWidth + 'px';
}
window.onresize = function(){
throttle(resizeDiv);
}
from step-by-step.
函数节流:限制函数执行频率,保证函数执行频率不超过设置的值,在保证计算速率要求的情况下节省更多的计算资源。
应用场景:适用于 计算量大,触发时机多 的函数,例如 触发时机为:滚动或者resize,引发重绘 的一类函数。
节流实现:
function throttle(method, timeout) {
// 函数执行计时器
let time;
// 函数执行计数器
let count = 0;
let countTime;
return function () {
// 未到时间则直接返回
if (time) return;
// 第一次立即执行
if (count === 0) {
count++ && method();
return;
} else {
time = setTimeout(() => {
method();
clearTimeout(time);
time = undefined;
// 一个间隔过后清空执行次数
countTime && clearTimeout(countTime);
countTime = setTimeout(() => { count = 0 }, timeout);
}, timeout);
}
}
};
和防抖的区别:防抖函数有可能一直都不执行,但是节流更加高级一点,除了可以保证多长时间内不会重复执行,还可以保证一定时间内至少可以会执行一次。
from step-by-step.
节流函数的作用
节流函数的作用是规定一个单位时间,在这个单位时间内只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。
举例说明:小明的妈妈和小明约定好,如果小明在周考中取得满分,那么当月可以带他去游乐场玩,但是一个月最多只能去一次。
我们来分析一下:
1.如果小明在一个月4次周考中都取得了满分,但是根据约定,他只能去一次游乐场。
2.如果小明一次满分都没有取得,他没有去游乐场的机会。
3.如果小明一月取得3次满分,那么一月份他可以去一次游乐场,但是二月一次满分都没有,二月他不能去游乐场。
这其实就是一个节流的例子,在一个月的时间内,去游乐场最多只能触发一次。即使这个时间周期内,小明取得多次满分。
节流应用场景
按钮点击事件
拖拽事件
onScoll
计算鼠标移动的距离(mousemove)
节流函数实现
function throttle (func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function () {
previous = options.leading === false ? 0 : Date.now() || new Date().getTime();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function () {
var now = Date.now() || new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
}
from step-by-step.
节流函数的作用:
在事件频繁触发的情况下,每隔一段时间,只执行一次回调函数,避免产生性能问题。
应用场景:
- 滚动加载,加载更多或滚到底部监听
- 谷歌搜索框,搜索联想功能
- 高频点击提交,表单重复提交
实现:
function throttle(fn, delay) {
var timer;
return function () {
var _this = this;
var args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null;
}, delay)
}
}
from step-by-step.
节流指的是每隔一段时间,执行一次事件。在时间范围内,不管我们怎么去触发这个事件,实际上只会触发一次。
应用场景常见的有懒加载
实现节流的方式有两种,一是时间戳,二是定时器。
1,使用时间戳的方式:
`function throttle(func, wait) {
var context, args;
var previous = 0;
return function() {
var now = +new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}`
2,使用定时器的方式
`function throttle(func, wait) {
var timeout;
var previous = 0;
return function() {
context = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(function(){
timeout = null;
func.apply(context, args)
}, wait)
}
}
}`
from step-by-step.
- 作用:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,可以提升性能,减少浏览器或者服务器的压力。
/**
* @param fn {Function} 实际要执行的函数
* @param interval {Number} 延迟时间,默认 300 毫秒
*
* @return {Function}
*/
function throttle(fn, interval = 3000) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval)
}
}
from step-by-step.
节流就是不管事件连续触发多少次,我只在固定的时间过后,执行后面的方法。
还是下面这篇文章,后续还是要再看看。还是要今日事今日毕。
缺太多,补起来好累啊。
https://segmentfault.com/a/1190000018205319?utm_source=tag-newest
from step-by-step.
在这里我再写写防抖和节流的概念
防抖
防抖就是规定的时间内执行一次,分为立即执行和非立即执行,如果规定的时间内再次触发,那么事件是不会被触发的
节流
节流就很理解,就是每隔一定的时间执行一次。 有时间戳和定时器两种方法
时间戳
thorttle(func,delay){
let prev = Date.now();
return fucntion(){
let context = this;
let args = arguments;
let now = Date.bow();
if(now-prev>=delay){
func.apply(context,args);
prev = Date.now()
}
}
}
定时器
function thorttle(func,delay){
let timer = null;
return function(){
let context = this;
let args= arguments;
if(!timer){
timer= setTimeout(()=>{
func.apply(context,args);
timer = null;
},delay)
}
}
}
虽然参考了别人的代码,但是是自己手敲的,印象就多了一分
from step-by-step.
在JS中,mousemove 和滚动是会不间断的触发的
函数节流的基本**是设置一个定时器,在指定时间间隔内运行代码时清除上一次的定时器,并设置另一个定时器,直到函数请求停止并超过时间间隔才会执行。
在javascript高级程序设计 例子如下:
function scrollFn(){
console.log(1)
}
function throttle(method,delay,duration){
var timer=null;
var begin=new Date();
return function(){
var context=this, args=arguments;
var current=new Date();
clearTimeout(timer);
if(current-begin>=duration){
method.apply(context,args);
begin=current;
}else{
timer=setTimeout(function(){
method.apply(context,args);
},delay);
}
}
}
window.onscroll=throttle(scrollFn,100,500)
from step-by-step.
节流、是指函数在指定的时间间隔内只执行一次,执行期间再次调用会被无视。等本次调用完等待下一次调用。
from step-by-step.
节流是为了防止事件的频繁书法的一种方式,常用的事件包括
- 滚动加载,加载更多或滚到底部监听
- resize事件,鼠标事件,onmouseover,按钮点击事件
- 一般有两种方式:时间戳,定时器方式实现。
function throttle(func,delay){
var arg = argument;
var context = this;
var timer = null;
if(!timer){
timer = settimeout(()=>{
func.apply(arg,context);
timer = null;
},delay)
}}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
from step-by-step.
1.节流函数的作用:
预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
2.应用场景:
(1)DOM元素的拖拽功能实现(mousemove)
(2)射击游戏的mousedown/keydown事件(单位时间只能发射一颗子弹)
(3)计算鼠标移动的距离(mousemove)
(4)Canvas模拟画板功能(mousemove)
(5)搜索联想(keyup)
(6)监听滚动事件判断是否到页面底部自动加载更多:给scroll加了debounce后,只有用户停止滚动后,才会判断是否到了页面底部;如果是throttle的花,只要页面滚动就会间隔一段时间判断一次
(7)click事件(不停快速点击按钮,减少触发频次)
(8)scroll事件(返回顶部按钮出现\隐藏事件触发)
(9)keyup事件(输入框文字与显示栏内容复制同步)
(10)减少发送ajax请求,降低请求频率
3.实现节流函数
function scrollFn(){
console.log(1)
}
function throttle(method,delay,duration){
var timer=null;
var begin=new Date();
return function(){
var context=this, args=arguments;
var current=new Date();
clearTimeout(timer);
if(current-begin>=duration){
method.apply(context,args);
begin=current;
}else{
timer=setTimeout(function(){
method.apply(context,args);
},delay);
}
}
}
window.onscroll=throttle(scrollFn,100,500)
(微信名:RUN)
from step-by-step.
节流与防抖都是针对于高频率触发函数进行限制的手段。
节流的功效就类似于放技能的冷却器,只要你一直按那个技能。冷却过了才会释放技能。
节流:只要你持续触发事件,每隔一段时间才会触发一次函数。
举个栗子!
下图是未经过节流处理的浏览器滚动条监听事件:
下图是经过节流处理:
比较喜欢时间戳的方式,
贴一下
function throttle(fn,gapTime){
if(gapTime == null || gapTime == undefined) {
gapTime = 500
}
let _lastTime = 0;
return function(){
var _nowTime = +new Date();
if(_nowTime - _lastTime > gapTime || !_lastTime) {
fn.apply(this,arguments)
_lastTime = _nowTime
}
}
}
from step-by-step.
throttle-函数节流:一个水龙头在滴水,可能一次性会滴很多滴,但是我们只希望它每隔 500ms 滴一滴水,保持这个频率。即我们希望函数在以一个可以接受的频率重复调用。
应用场景:(同防抖)
DOM元素的拖拽功能实现(mousemove)
(1)射击游戏的mousedown/keydown事件(单位时间只能发射一颗子弹)
(2)计算鼠标移动的距离(mousemove)
(3)搜索联想(keyup)
(4)监听滚动事件判断是否到页面底部自动加载更多:给scroll加了debounce后,只有用户停止滚动后,才会判断是否到了页面底部;如果是throttle的花,只要页面滚动就会间隔一段时间判断一次
(5)click事件(不停快速点击按钮,减少触发频次)
(6)scroll事件(返回顶部按钮出现\隐藏事件触发)
基本模式
function throttle(fn, interval) {
var _self = fn
var firstTime = true
var timer return function() {
var args = arguments
var _me = this
if (firstTime) {
_self.call(_me, args)
} if (timer) { return false
}
timer = setTimeout(function() {
clearTimeout(timer)
timer = null
_self.call(_me, args)
}, interval || 500)
}
}
from step-by-step.
节流函数也是为了限制用户的频繁操作,比如在点击按钮的时候需要执行数值+1的操作,如果没有设置节流函数我们就可以使用js脚本的方式,来一次性执行多次。
用大白话来讲节流和防抖就如同坐电梯,电梯一段时间内只能上一个人,需要最后一个人上了之后电梯才嫩启动。
function throttle(fn, wait){
let pre = 0
return function(){
let cur = Date.now()
if(cur - pre > 0) {
fn.apply(this)
pre = cur
}
}
}
from step-by-step.
- 节流
// 节流:降低函数的执行频率。在规定时间内,连续触发事件函数只执行一次。
/**
* 定时器版
* @param {Function} fn 需要执行的函数
* @param {Number} delay 触发时间间隔
*/
function throttle (fn, wait) {
let timeout = null
return function () {
let context = this,
args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
fn.call(context, args)
}, wait)
}
}
}
/**
*
* @param {Function} fn 需要执行的函数
* @param {Number} wait 触发时间间隔
*/
function throttle (fn, wait) {
let pre = 0
return function () {
let now = Date.now(),
context = this,
args = arguments
if (now - pre > wait) {
fn.call(context, args)
pre = now
}
}
}
from step-by-step.
作用
控制函数每隔多长时间执行一次,防止用户高频率的触发事件,刚好这个事件又需要处理大量的计算和渲染而带来的性能问题。
应用场景
- onscroll
- onresize
- mousemove
- touchmove
- ...
这边我们以onscroll
来写个例子,加深理解。
比如有这样一个场景:我们需要判断浏览器滚动条滚动到底部的时候去动态加载一些数据,可能我们直接就会写上以下的代码:
let obj = document.querySelector('.throttle-test');
let _count = 0;
obj.onscroll = function () {
// 假设已经滚动到底部了,我们给_count加1。
_count++;
console.log('执行次数:' + _count);
}
我们来看下浏览器的打印结果,看以下gif图:
可以看到,函数执行了20次,很显然这并不是我们想要的,因为onscroll
事件并不会等你滚动到底部了再去触发事件,而是会不间断的触发,这就很容易引发一些性能问题,这时候就需要用到节流了。
我们把代码做下修改:
let obj = document.querySelector('.throttle-test');
let _count = 0;
let _canRun = true;
obj.onscroll = function () {
if (!_canRun) {
return false;
}
_canRun = false;
setTimeout(function () {
// 假设已经滚动到底部了,我们给_count加1。
_count++;
console.log('执行次数:'+_count);
_canRun = true;
}, 500);
}
通过以下gif图,我们可以看到,函数最终只执行了2次。
通过一个定时器,我们控制函数每隔500毫秒再执行一次,大大降低了执行频率,从而提升性能。
节流概念
节流跟防抖,它们既有相似之处但又有所不同,很容易混淆。这里通过两个比喻来加深理解,先来说说节流。
节流的概念可以想象一下水坝,你建了水坝在河道中,不能让水流动不了,你只能让水流慢些。换言之,你不能让用户的方法都不执行。(个人比较喜欢这个比喻,因为它很形象的说出了跟防抖的区别。)
复习下防抖
再来说防抖:可以想象一下做电梯,当有人进入电梯(触发事件),那电梯将在10秒后出发(执行事件),这时如果又有人进入电梯了(在10秒内再次触发了事件),我们又得重新等10秒才能触发(重新计时)。
来看个防抖的例子:
有个文本框让用户填写用户名,当用户输入字符时,我们需要实时发请求到后台去验证用户名是否有重复的。实际上,在加入防抖机制前,用户输入helloworld
后,我们已经发送了10次请求了,很显然是不可取的。
看代码:
let obj = document.querySelector('#testInput'); // 获取文本输入框
let _count = 0;
obj.onkeyup = function () {
_count++;
console.log('执行次数:' + _count);
}
控制台打印效果gif图:
我们给代码加入防抖机制:
我们只能假设用户在停顿n秒内没有再触发事件,我们就判定用户已经输入完成了,这时再发送请求。
看代码:
let obj = document.querySelector('#testInput');
let timer = null;
let _count = 0;
obj.onkeyup = function () {
clearTimeout(timer); // 清除定时器,重新计时
timer = setTimeout(function () {
_count++;
console.log('执行次数:'+_count);
}, 800);
}
控制台打印效果gif图:
通过动图,可以看到,当我一直输入的时候,事件是不会被触发的,直到我停止输入才会触发一次。
实现思路:我们把目标代码放入到一个定时器里,如果事件被频繁的触发,目标代码将不会被执行。为什么不执行呢,因为我们前面加了clearTimeout
。相当于中途不断的有人进入电梯,电梯又得重新倒计时10秒才会启动一样,直到用户没再输入了(没人再进入电梯了),这时候目标代码才会按照我们设定的时间再去执行一次(电梯才会启动)。
看到这里,你应该能知道节流
和防抖
的区别了:
- 节流:目标代码会按照我们设定的时间间隔即每隔n秒就执行一次
- 防抖:在用户不触发事件时,才去执行目标代码,并且抑制了本来在事件中要执行的动作;当事件被一直触发的情况下,目标代码有可能不会被执行
- 函数节流会用在比
input
,keyup
更频繁触发的事件中,如resize
,touchmove
,mousemove
,scroll
。节流
会强制函数以固定的速率执行。因此这个方法比较适合应用于动画相关的场景。
最后
感谢您的阅读,希望对你有所帮助。文中所演示的代码仅用来测试使用,并不适合用在实际开发中,实际开发可以使用Lodash
库中的节流
和防抖
方法,这里就不贴代码了,毕竟考虑的比较全面哈。
参考:https://zhuanlan.zhihu.com/p/38313717
from step-by-step.
节流(throttle)
高频事件每隔n秒就会执行一次
节流代码实现
function throttle(func, delay){
let last, deferTime;
return function(args){
let this = _this, _args = args;
let now = +new Date();
if(last && now < last+delay) {
clearTimeout(deferTime);
deferTime = setTimeout(function() {
func.call(_this, _args)
}, delay);
}else {
last = now();
func.call(_this, _args);
}
}
}
节流:
1.DOM 元素的拖拽功能实现(mousemove)
2.射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
3.计算鼠标移动的距离(mousemove)
4.Canvas 模拟画板功能(mousemove)
5.搜索联想(keyup)
6.监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
from step-by-step.
- 节流函数的作用: 减少实际逻辑处理过程的执行来提高事件处理性能,在规定的周期时间里出发一次,防止高频率触发函数,影响浏览器性能!
- 最常用的就是手机验证码验证,一般是60s一个周期,为了防止用户无限发验证码
3.function throttle(fn, delay=1000, atleast){
let timer = null
let pervious = null;
return function(){
let self = this
let args = arguments
let nowTime = +new Date();
if(!pervious) {
previous = nowTime;
}
if(nowTime - pervious > atleast){
fn.apply(self,args);
previous = nowTime;
} else {
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(self,args);
},delay);
}
}
}
from step-by-step.
1. 函数节流概念:
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
2. 个人理解:
函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。
3.举例:
//模拟一段ajax请求
function ajax(content) {
console.log('ajax request ' + content)
}
let inputa = document.getElementById('unDebounce')
inputa.addEventListener('keyup', function (e) {
ajax(e.target.value)
})
//函数节流
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 inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
})
4.使用场景
- 鼠标不断点击触发,mousedown(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
from step-by-step.
函数节流,是指如果一个事件不停地触发,那么就每隔一定的时间才执行回调函数,回调函数的执行会被稀释。
from step-by-step.
防抖:在n秒后进行事件回调,如果n秒内又被触发,则重新计时。
节流:在规定时间内,触发的多次时间只生效一次。
function throttle (fn, gapTime) {
let lastTime = null;
return function {
let nowTime = new Date();
if (nowTime - lastTime > gapTime || !lastTime) {
fn();
lastTime = nowTime;
}
}
}
let fn = () => {
console.log('hello');
}
setInterval(throttle(fn, 1000), 10);
from step-by-step.
节流
我的理解为,在规定时间内 ,只执行一次事件,如果想第二次触发 ,必须得等到规定时间结束 同样在第二个周期内也是如此。
实现方式
第一种
function throttle(fn,wait){
let pre =0;
return function(){
let now = Date.now();
let args = arguments;
let context = this;
if ( now- pre > wait ) {
fn.apply(context,this);
pre =now;
}
}
}
第二种
function trottle(fn, ...arg){
let run = false
return function(){
if run return
run =true
setTimeout(function(){
fn.apply(this,arg);
run = false;
},300)
}
}
from step-by-step.
节流就是在一定时间内一定会去执行
function throttle(fn,delay){
let valid = true;
if(!valid){
return true
}
valid = false;
setTimeout(()=>{
fn();
valid = true
},delay)
}
from step-by-step.
节流就是限制你在一定时间内触发时间的频率,通过设置定时器在规定时间内触发会被忽略,知道定时器结束才能被触发继续执行并开启新的定时器。
日常应用场景我是用在限制用户过度频繁调用接口。
俺手敲的一个实现
/**
*
* @param func {Function} 实际要执行的函数
* @param wait {Number} 执行间隔,单位是毫秒(ms),默认100ms
*
* @return {Function} 返回一个“节流”函数
*/
export const throttle = function(func: Function, delay: number): Function {
let timer = null;
let previous = 0;
return (...args) => {
const context = this;
const now = +new Date();
if (previous && now < previous + delay) {
//超过限定时间可执行
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
} else {
previous = now;
func.apply(context, args);
}
};
};
from step-by-step.
什么是防抖
当持续触发事件时,一定范围时间内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发事件,就重新开始延时
举例说明:当你乘电梯时,按下关电梯按钮,现在开始计时,如果5s内没有人点击开电梯按钮,你就会正常的乘着电梯上楼,如果5s内有人点击开电梯按钮,那么关门事件会重新开始延时5s再触发
什么是节流
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
举例说明:身为一个妹子,特别喜欢买化妆品。为了控制自己,节约钱,规定自己一个月买一次化妆品。
作用
处理高频事件,避免函数频繁调用,优化性能,减少浏览器或者服务器的压力。
防抖函数的应用场景
规定时间多次触发,以最后一次结果为准
- 输入框格式验证
- 输入框模糊搜索。输入框输入数据后向服务端请求数据拉一个列表。
节流函数的应用场景
规定时间只触发一次,不以结果为准
- 屏幕尺寸变化时,执行相应逻辑
- 监听鼠标滚动事件,执行相应逻辑
- 监听重复点击事件,执行相应逻辑
防抖函数的实现
function denounce(func,delay) {
let timerId = null
return function(){
window.clearTimeout(timerId)
let context = this
let args = arguments
timerId = setTimeout(()=>{
func.call(context,args)
},time)
}
}
节流函数的实现
function throttle(func, time) {
let preTime = 0
return function() {
let currentTime = Date.now()
let context = this
let args = arguments
if(currentTime-preTime>time){
func.call(context,args)
preTime = Date.now()
}
}
}
参考资料
from step-by-step.
对于持续触发的事件,规定一个间隔时间(n秒),每隔一段只能执行一次。
const throttle = function (func, wait) {
let lastTime;
return function () {
const curTime = Date.now();
if (!lastTime || curTime - lastTime >= wait) {
lastTime = curTime;
return func();
}
}
}
from step-by-step.
Related Issues (20)
- 寄生组合式继承的基本**是什么?有哪些优缺点? HOT 11
- 实现一个 JSON.stringify HOT 9
- 实现一个 JSON.parse HOT 9
- 实现一个观察者模式 HOT 10
- 使用CSS让一个元素水平垂直居中 HOT 11
- ES6模块和CommonJS模块有哪些差异? HOT 8
- 如何使用Proxy实现简单MVVM HOT 2
- 以下代码的输出的结果为: HOT 7
- 列举常见的JS和CSS兼容性问题
- 介绍下 Set、Map、WeakSet 和 WeakMap 的区别? HOT 1
- Vue组件间是怎么进行参数传递的? HOT 4
- 如果浏览器主线程一直被占据,那么setTimeout会什么时候执行
- rem、em、vh、vw 有什么区别
- flex-grow 与 flex-shrink 的计算规则
- while循环卡死和微任务卡死有什么区别
- sourcemap原理是什么
- 闭包会导致内存泄漏为什么还要使用?
- 前端怎么显示1G的图片甚至1T的图片
- 手写lodash的get方法
- setTimeout回调和fetch回调哪个先执行
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from step-by-step.