如何解决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引导程序
Oct 26 Javascript
jQuery中判断一个元素是否为另一个元素的子元素(或者其本身)
Mar 21 Javascript
jQuery实现简单网页遮罩层/弹出层效果兼容IE6、IE7
Jun 16 Javascript
jquery操作select方法汇总
Feb 05 Javascript
node.js操作mongodb学习小结
Apr 25 Javascript
JavaScript中用toString()方法返回时间为字符串
Jun 12 Javascript
JS获取一个未知DIV高度的方法
Aug 09 Javascript
JS中作用域和变量提升(hoisting)的深入理解
Oct 31 Javascript
微信小程序登录态控制深入分析
Apr 12 Javascript
使用JQ完成表格隔行换色的简单实例
Aug 25 Javascript
vue计算属性computed的使用方法示例
Mar 13 Javascript
微信小程序登录session的使用
Mar 17 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
Oracle Faq(Oracle的版本)
2006/10/09 PHP
我的论坛源代码(三)
2006/10/09 PHP
php伪静态之APACHE篇
2014/06/02 PHP
php单文件版在线代码编辑器
2015/03/12 PHP
Javascript类库的顶层对象名用户体验分析
2010/10/24 Javascript
jQuery 三击事件实现代码
2013/09/11 Javascript
js中单引号与双引号冲突问题解决方法
2013/10/04 Javascript
js和jquery中循环的退出和继续学习记录
2014/09/06 Javascript
JQuery实现的图文自动轮播效果插件
2015/06/19 Javascript
Bootstrap进度条组件知识详解
2016/05/01 Javascript
jQuery对象的链式操作用法分析
2016/05/10 Javascript
Vue.js学习之计算属性
2017/01/22 Javascript
javascript表单正则应用
2017/02/04 Javascript
js for循环倒序输出数组元素的实例
2017/03/01 Javascript
JS中的作用域链
2017/03/01 Javascript
纯js仿淘宝京东商品放大镜功能
2017/03/02 Javascript
Javascript继承机制详解
2017/05/30 Javascript
jquery动态赋值id与动态取id方法示例
2017/08/21 jQuery
详解vue.js之绑定class和style的示例代码
2017/08/24 Javascript
200行代码实现blockchain 区块链实例详解
2018/03/14 Javascript
详解Vue的组件中data选项为什么必须是函数
2020/08/17 Javascript
Python实现将SQLite中的数据直接输出为CVS的方法示例
2017/07/13 Python
Python cookbook(数据结构与算法)在字典中将键映射到多个值上的方法
2018/02/18 Python
解决seaborn在pycharm中绘图不出图的问题
2018/05/24 Python
Python 列表去重去除空字符的例子
2019/07/20 Python
Pycharm+django2.2+python3.6+MySQL实现简单的考试报名系统
2019/09/05 Python
python处理document文档保留原样式
2019/09/23 Python
6行Python代码实现进度条效果(Progress、tqdm、alive-progress​​​​​​​和PySimpleGUI库)
2020/01/06 Python
常用python爬虫库介绍与简要说明
2020/01/25 Python
如何用python 操作zookeeper
2020/12/28 Python
vivo智能手机官方商城:vivo
2016/09/22 全球购物
MATCHESFASHION.COM法国官网:英国奢侈品零售商
2018/01/04 全球购物
小米官方旗舰店:Xiaomi
2020/08/07 全球购物
幼儿老师求职信
2014/06/30 职场文书
某某店铺的开业庆典主持词范本
2019/11/25 职场文书
TypeScript中条件类型精读与实践记录
2021/10/05 Javascript