如何解决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 相关文章推荐
jQuery 定时局部刷新(setInterval)
Nov 19 Javascript
javascript变量作用域使用中常见错误总结
Mar 26 Javascript
JS.elementGetStyle(element, style)应用示例
Sep 24 Javascript
readonly和disabled属性的区别
Jul 26 Javascript
jq checkbox 的全选并ajax传参的实例
Apr 01 Javascript
bootstrap suggest下拉框使用详解
Apr 10 Javascript
vue组件实现进度条效果
Jun 06 Javascript
深入理解Vue 组件之间传值
Aug 16 Javascript
JavaScript函数的4种调用方法实例分析
Mar 05 Javascript
详解小程序如何避免多次点击,重复触发事件
Apr 08 Javascript
用云开发Cloudbase实现小程序多图片内容安全监测的代码详解
Jun 07 Javascript
在vue中使用eslint,配合vscode的操作
Nov 09 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(5) 类和对象
2010/02/16 PHP
PHP实现检测客户端是否使用代理服务器及其匿名级别
2015/01/07 PHP
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
2009/04/10 Javascript
js实现GridView单选效果自动设置交替行、选中行、鼠标移动行背景色
2010/05/27 Javascript
jsPDF生成pdf后在网页展示实例
2014/01/16 Javascript
AngularJS中isolate scope的用法分析
2016/11/22 Javascript
bootstrap switch开关组件使用方法详解
2017/08/22 Javascript
JavaScript代码实现txt文件的上传预览功能
2018/03/27 Javascript
vue.js 实现点击展开收起动画效果
2018/07/07 Javascript
vue 调用 RESTful风格接口操作
2020/08/11 Javascript
vue使用openlayers实现移动点动画
2020/09/24 Javascript
vue 中使用print.js导出pdf操作
2020/11/13 Javascript
[03:10]2014DOTA2 TI马来劲旅Titan首战告捷目标只是8强
2014/07/10 DOTA
归纳整理Python中的控制流语句的知识点
2015/04/14 Python
python 接口_从协议到抽象基类详解
2017/08/24 Python
python机器学习理论与实战(四)逻辑回归
2018/01/19 Python
将python图片转为二进制文本的实例
2019/01/24 Python
python自动化测试之DDT数据驱动的实现代码
2019/07/23 Python
Python Django框架防御CSRF攻击的方法分析
2019/10/18 Python
python如果快速判断数字奇数偶数
2019/11/13 Python
Python遍历字典方式就实例详解
2019/12/28 Python
django xadmin 管理器常用显示设置方式
2020/03/11 Python
pycharm通过anaconda安装pyqt5的教程
2020/03/24 Python
python中matplotlib实现随鼠标滑动自动标注代码
2020/04/23 Python
Python爬虫之Selenium警告框(弹窗)处理
2020/12/04 Python
websocket+sockjs+stompjs详解及实例代码
2018/11/30 HTML / CSS
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
英国知名化妆品网站:Revolution Beauty(原TAM Beauty)
2018/02/28 全球购物
草莓网官网:StrawberryNET
2019/08/21 全球购物
Lentiamo丹麦:购买便宜的隐形眼镜
2021/01/13 全球购物
怎样有效的进行自我评价
2013/10/06 职场文书
市场营销毕业生自荐信
2013/11/23 职场文书
小学运动会广播稿200字(十二篇)
2014/01/14 职场文书
幼儿园消防演练方案
2014/02/13 职场文书
社区公民道德宣传日活动总结
2015/03/23 职场文书
2015年终个人政治思想工作总结
2015/11/24 职场文书