如何解决js函数防抖、节流出现的问题


Posted in Javascript onJune 17, 2019

React中使用防抖函数和节流函数

在React事件调用时,React传递给事件处理程序是一个合成事件对象的实例。SyntheticEvent对象是通过合并得到的。 这意味着在事件回调被调用后,SyntheticEvent 对象将被重用并且所有属性都将被取消。 这是出于性能原因。 因此,您无法以异步方式访问该事件。React合成事件官方文档

所以在用防抖或节流函数封装时,异步方式访问事件对象出现问题。解决的方法如下:

方法一:调用合成事件对象的persist()方法 event.persist && event.persist() //保留对事件的引用

方法二:深拷贝事件对象 const event = e && {...e} //深拷贝事件对象

function debounce(func, wait=500) {
let timeout; // 定时器变量
return function(event){
clearTimeout(timeout); // 每次触发时先清除上一次的定时器,然后重新计时
event.persist && event.persist() //保留对事件的引用
//const event = e && {...e} //深拷贝事件对象
timeout = setTimeout(()=>{
func(event)
}, wait); // 指定 xx ms 后触发真正想进行的操作 handler
};
}

防抖debounce

防抖 Debounce 多次触发,只在最后一次触发时,执行目标函数。

函数防抖就是,延迟一段时间再执行函数,如果这段时间内又触发了该函数,则延迟重新计算。

应用场景

(1)通过监听某些事件完成对应的需求,比如:

通过监听 scroll 事件,检测滚动位置,根据滚动位置显示返回顶部按钮

通过监听 resize 事件,对某些自适应页面调整DOM的渲染(通过CSS实现的自适应不再此范围内)

通过监听 keyup 事件,监听文字输入并调用接口进行模糊匹配

(2)其他场景

表单组件输入内容验证

防止多次点击导致表单多次提交

简单实现

function debounce(fn, wait) {
let t
return () => {
let context = this
let args = arguments
if (t) clearTimeout(t)
t= setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}

完整实现

function debounce(func, wait, immediate) {
let time;
let debounced = function() {
let context = this;
if(time) clearTimeout(time);
if(immediate) {
let callNow = !time;
if(callNow) func.apply(context, arguments);
time = setTimeout(
()=>{time = null} //见注解
, wait)
} else {
time = setTimeout(
()=>{func.apply(context, arguments)}
, wait) 
}
};
debounced.cancel = function() {
clearTimeout(time);
time = null
};
return debounced
}
// underscore.js debounce
//
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
// 处理时间
var later = function() {
var last = _.now() - timestamp;
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last); // 10ms 6ms 4ms
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};

react中调用方法

this.handleGetCustomerNameList = debounce(this.handleGetCustomerNameList.bind(this), 500);

节流 throttle

节流:函数间隔一段时间后才能再触发,避免某些函数触发频率过高,比如滚动条滚动事件触发的函数。

### 简单实现
function throttle (fn, wait, mustRun) {
let start = new Date()
let timeout
return () => {
// 在返回的函数内部保留上下文和参数
let context = this
let args = arguments
let current = new Date()
clearTimeout(timeout)
let remaining = current - start
// 达到了指定触发时间,触发该函数
if (remaining > mustRun) {
fn.apply(context, args)
start = current
} else {
// 否则wait时间后触发,闭包保留一个timeout实例
timeout = setTimeout(fn, 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;
}
// underscore.js throttle
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = _.now();
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) {
timeout = setTimeout(later, remaining);
}
return result;
};
};

react中调用方法

this.handleGetCustomerNameList = throttle (this.handleGetCustomerNameList.bind(this), 500);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript中创建对象的三种常用方法
Dec 30 Javascript
用js实现trim()的解决办法
Apr 16 Javascript
基于pthread_create,readlink,getpid等函数的学习与总结
Jul 17 Javascript
Javascript中的几种继承方式对比分析
Mar 22 Javascript
浅析JS动态创建元素【两种方法】
Apr 20 Javascript
基于jQuery和Bootstrap框架实现仿知乎前端动态列表效果
Nov 09 Javascript
js 单引号替换成双引号,双引号替换成单引号的实现方法
Feb 16 Javascript
详解webpack3如何正确引用并使用jQuery库
Aug 26 jQuery
JavaScript设计模式之调停者模式实例详解
Feb 03 Javascript
vue.js仿hover效果的实现方法示例
Jan 28 Javascript
微信小程序把百度地图坐标转换成腾讯地图坐标过程详解
Jul 10 Javascript
如何使用JavaScript实现无缝滚动自动播放轮播图效果
Aug 20 Javascript
使用imba.io框架得到比 vue 快50倍的性能基准
Jun 17 #Javascript
通过js给网页加上水印背景实例
Jun 17 #Javascript
JavaScript 扩展运算符用法实例小结【基于ES6】
Jun 17 #Javascript
通过实例解析js简易模块加载器
Jun 17 #Javascript
如何从头实现一个node.js的koa框架
Jun 17 #Javascript
javascript定时器的简单应用示例【控制方块移动】
Jun 17 #Javascript
深入解析koa之异步回调处理
Jun 17 #Javascript
You might like
php中HTTP_REFERER函数用法实例
2014/11/21 PHP
PHP中strpos、strstr和stripos、stristr函数分析
2016/06/11 PHP
Laravel如何同时连接多个数据库详解
2019/08/13 PHP
js querySelector和getElementById通过id获取元素的区别
2012/04/20 Javascript
jQuery根据纬度经度查看地图处理程序
2013/05/08 Javascript
详谈JavaScript 匿名函数及闭包
2014/11/14 Javascript
jQuery实现点击按钮文字变成input框点击保存变成文字
2016/05/09 Javascript
IONIC自定义subheader的最佳解决方案
2016/09/22 Javascript
详解Vue-cli代理解决跨域问题
2017/09/27 Javascript
AngularJS基于MVC的复杂操作实例讲解
2017/12/31 Javascript
vue.js通过路由实现经典的三栏布局实例代码
2018/07/08 Javascript
JavaScript数据结构与算法之二叉树实现查找最小值、最大值、给定值算法示例
2019/03/01 Javascript
在Vue中使用icon 字体图标的方法
2019/06/14 Javascript
微信小程序点击图片实现长按预览、保存、识别带参数二维码、转发等功能
2019/07/20 Javascript
layui复选框限制选择个数的方法
2019/09/18 Javascript
javascript 设计模式之组合模式原理与应用详解
2020/04/08 Javascript
js this 绑定机制深入详解
2020/04/30 Javascript
Python去除、替换字符串空格的处理方法
2018/04/01 Python
对Python捕获控制台输出流的方法详解
2019/01/07 Python
对IPython交互模式下的退出方法详解
2019/02/16 Python
对Python3 * 和 ** 运算符详解
2019/02/16 Python
python中的colorlog库使用详解
2019/07/05 Python
Python数据结构dict常用操作代码实例
2020/03/12 Python
完美解决jupyter由于无法import新包的问题
2020/05/26 Python
Keras:Unet网络实现多类语义分割方式
2020/06/11 Python
windows+vscode安装paddleOCR运行环境的步骤
2020/11/11 Python
使用gunicorn部署django项目的问题
2020/12/30 Python
HTML5触摸事件(touchstart、touchmove和touchend)的实现
2020/05/08 HTML / CSS
Jacadi Paris美国官方网站:法国童装品牌
2017/10/15 全球购物
心理健康心得体会
2014/01/02 职场文书
大学同学聚会邀请函
2014/01/19 职场文书
大学新生军训感言
2014/02/25 职场文书
圣诞节开幕词
2015/01/29 职场文书
建国大业观后感600字
2015/06/01 职场文书
pytorch中的numel函数用法说明
2021/05/13 Python
如何Python使用re模块实现okenizer
2022/04/30 Python