如何解决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创建自己的插件(自定义插件)的方法
Jun 10 Javascript
web前端设计师们常用的jQuery特效插件汇总
Dec 07 Javascript
深入浅析Extjs中store分组功能的使用方法
Apr 20 Javascript
jquery实现下拉框功能效果【实例代码】
May 06 Javascript
JS使用onerror捕获异常示例
Aug 03 Javascript
AngularJs  Using $location详解及示例代码
Sep 02 Javascript
AngularJS路由实现页面跳转实例
Mar 03 Javascript
jQuery插件zTree实现获取一级节点数据的方法
Mar 08 Javascript
JavaScript全屏和退出全屏事件总结(附代码)
Aug 17 Javascript
Vue.js特性Scoped Slots的浅析
Feb 20 Javascript
javascript实现支付宝滑块验证码效果
Jul 24 Javascript
JavaScript对象访问器Getter及Setter原理解析
Dec 08 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中explode的负数limit用法分析
2015/02/27 PHP
jquery获取多个checkbox的值异步提交给php的方法
2015/06/24 PHP
Google Map Api和GOOGLE Search Api整合实现代码
2009/07/18 Javascript
jQuery基本过滤选择器使用介绍
2013/04/18 Javascript
根据选择不同的下拉值出现相对应的文本输入框
2013/08/01 Javascript
div浮层,滚动条移动,位置保持不变的4种方法汇总
2013/12/11 Javascript
jQuery实现的Div窗口震动特效
2014/06/09 Javascript
js中回调函数的学习笔记
2014/07/31 Javascript
JavaScript自学笔记(必看篇)
2016/06/23 Javascript
原生js实现下拉框功能(支持键盘事件)
2017/01/13 Javascript
jQuery实现使用sort方法对json数据排序的方法
2018/04/17 jQuery
解决vue.js 数据渲染成功仍报错的问题
2018/08/25 Javascript
JS实现动态添加外部js、css到head标签的方法
2019/06/05 Javascript
react-router-dom 嵌套路由的实现
2020/05/02 Javascript
[01:50]2014DOTA2西雅图邀请赛 专访欢乐周宝龙
2014/07/08 DOTA
python实现颜色rgb和hex相互转换的函数
2015/03/19 Python
详解Python3中字符串中的数字提取方法
2017/01/14 Python
Python如何生成树形图案
2018/01/03 Python
Python清空文件并替换内容的实例
2018/10/22 Python
python字典一键多值实例代码分享
2019/06/14 Python
python使用socket 先读取长度,在读取报文内容示例
2019/09/26 Python
Pycharm中import torch报错的快速解决方法
2020/03/05 Python
HTML5中的进度条progress元素简介及兼容性处理
2016/06/02 HTML / CSS
全球第二大家装零售商:Lowe’s
2018/01/13 全球购物
迪奥官网:Dior.com
2018/12/04 全球购物
什么是"引用"?申明和使用"引用"要注意哪些问题?
2016/03/03 面试题
HR喜欢的自荐信格式
2013/10/08 职场文书
中式面点餐厅创业计划书
2014/01/29 职场文书
霸气押韵的班级口号
2014/06/09 职场文书
消防安全宣传口号
2014/06/10 职场文书
中华魂放飞梦想演讲稿
2014/08/26 职场文书
党的群众路线教育实践活动组织生活会发言材料
2014/10/17 职场文书
合作协议书范本
2014/10/25 职场文书
2015年个人现实表现材料
2014/12/10 职场文书
Spring boot应用启动后首次访问很慢的解决方案
2021/06/23 Java/Android
Spring-cloud Config Server的3种配置方式
2021/09/25 Java/Android