如何解决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 相关文章推荐
在网页中屏蔽快捷键
Sep 06 Javascript
JavaScript 实现模态对话框 源代码大全
May 02 Javascript
用JQUERY增删元素的代码
Feb 14 Javascript
JavaScript调用堆栈及setTimeout使用方法深入剖析
Feb 16 Javascript
JS实现自动固定顶部的悬浮菜单栏效果
Sep 16 Javascript
jQuery实现图片向左向右切换效果的简单实例
May 18 Javascript
jQuery中设置form表单中action值的实现方法
May 25 Javascript
JavaScript中的冒泡排序法
Aug 03 Javascript
node.js中ws模块创建服务端和客户端,网页WebSocket客户端
Mar 06 Javascript
vue双向绑定数据限制长度的方法
Nov 04 Javascript
JavaScript定时器使用方法详解
Mar 26 Javascript
Nuxt的路由动画效果案例
Nov 06 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实现首页链接查询 友情链接检查的代码
2010/01/05 PHP
PHP curl 并发最佳实践代码分享
2012/09/05 PHP
laravel5.2实现区分前后台用户登录的方法
2017/01/11 PHP
自制PHP框架之路由与控制器
2017/05/07 PHP
php面试实现反射注入的详细方法
2019/09/30 PHP
php输出文字乱码的解决方法
2019/10/04 PHP
JavaScript 应用技巧集合[推荐]
2009/08/30 Javascript
JavaScript 事件记录使用说明
2009/10/20 Javascript
读jQuery之四(优雅的迭代)
2011/06/20 Javascript
提高javascript效率 一次判断,而不要次次判断
2012/03/30 Javascript
Javascript获取CSS伪元素属性的实现代码
2014/09/28 Javascript
简介JavaScript中strike()方法的使用
2015/06/08 Javascript
js计算文本框输入的字符数
2015/10/23 Javascript
JavaScript通过代码调用Flash显示的方法
2016/02/02 Javascript
ajax在兼容模式下失效的快速解决方法
2016/03/22 Javascript
jQuery实现的瀑布流加载效果示例
2016/09/13 Javascript
JS使用setInterval实现的简单计时器功能示例
2018/04/19 Javascript
手把手教你如何使用nodejs编写cli命令行
2018/11/05 NodeJs
JavaScript实现简单的图片切换功能(实例代码)
2020/04/10 Javascript
解决Nuxt使用axios跨域问题
2020/07/06 Javascript
vue实现表格合并功能
2020/12/01 Vue.js
如何在Vue项目中添加接口监听遮罩
2021/01/25 Vue.js
[02:49]DOTA2完美大师赛首日观众采访
2017/11/23 DOTA
[03:35]2018年度DOTA2最佳辅助位选手5号位-完美盛典
2018/12/17 DOTA
EM算法的python实现的方法步骤
2018/01/02 Python
python实现旋转和水平翻转的方法
2018/10/25 Python
Django实现CAS+OAuth2的方法示例
2019/10/30 Python
Python3读取和写入excel表格数据的示例代码
2020/06/09 Python
文员自我评价怎么写
2013/09/19 职场文书
大三学生学年自我鉴定
2014/09/12 职场文书
交通事故委托书范本(2篇)
2014/09/21 职场文书
老人与海读书笔记
2015/06/26 职场文书
运动会通讯稿100字
2015/07/20 职场文书
新郎父母婚礼致辞
2015/07/27 职场文书
二年级数学教学反思
2016/02/16 职场文书
react antd实现动态增减表单
2021/06/03 Javascript