如何解决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 相关文章推荐
如何在Mozilla Gecko 用Javascript加载XSL
Jan 09 Javascript
用JQuery实现表格隔行变色和突出显示当前行的代码
Feb 10 Javascript
密码框显示提示文字jquery示例
Aug 29 Javascript
处理文本部分内容的TextRange对象应用实例
Jul 29 Javascript
Bootstrap4一次重大更新 几乎涉及每行代码
May 16 Javascript
JQuery点击事件回到页面顶部效果的实现代码
May 24 Javascript
详解如何使用webpack+es6开发angular1.x
Aug 16 Javascript
实现jquery放大镜的两种方法
Feb 22 jQuery
Vue 使用formData方式向后台发送数据的实现
Apr 14 Javascript
JSON是什么?有哪些优点?JSON和XML的区别?
Apr 29 Javascript
在Koa.js中实现文件上传的接口功能
Oct 08 Javascript
ssm+vue前后端分离框架整合实现(附源码)
Jul 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代码
2010/08/08 PHP
PHP 5.5 创建和验证哈希最简单的方法详解
2013/11/07 PHP
php+xml结合Ajax实现点赞功能完整实例
2015/01/30 PHP
使用PHP uniqid函数生成唯一ID
2015/11/18 PHP
解析arp病毒背后利用的Javascript技术附解密方法
2007/08/06 Javascript
javascript getElementsByClassName 和js取地址栏参数
2010/01/02 Javascript
JavaScript将字符串转换成字符编码列表的方法
2015/03/19 Javascript
用JS实现轮播图效果(二)
2016/06/26 Javascript
Angularjs中ng-repeat-start与ng-repeat-end的用法实例介绍
2016/12/31 Javascript
JS switch判断 三目运算 while 及 属性操作代码
2017/09/03 Javascript
JavaScript数组的5种迭代方法
2017/09/29 Javascript
Vue实现 点击显示再点击隐藏效果(点击页面空白区域也隐藏效果)
2020/01/16 Javascript
vue实现路由不变的情况下,刷新页面操作示例
2020/02/02 Javascript
Vuex的API文档说明详解
2020/02/05 Javascript
JS 5种遍历对象的方式
2020/06/16 Javascript
[48:00]EG vs LGD 2018国际邀请赛淘汰赛BO3 第二场 8.26
2018/08/29 DOTA
[47:04]EG vs RNG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
python爬虫租房信息在地图上显示的方法
2019/05/13 Python
Python csv模块使用方法代码实例
2019/08/29 Python
Python爬虫实现“盗取”微信好友信息的方法分析
2019/09/16 Python
Python变量、数据类型、数据类型转换相关函数用法实例详解
2020/01/09 Python
浅谈python中频繁的print到底能浪费多长时间
2020/02/21 Python
在keras下实现多个模型的融合方式
2020/05/23 Python
openCV提取图像中的矩形区域
2020/07/21 Python
开发人员所需要知道的HTML5性能分析面面观
2012/07/05 HTML / CSS
兰蔻美国官网:Lancome美国
2017/04/25 全球购物
红色康乃馨酒店:Red Carnation Hotels
2017/06/22 全球购物
领先的荷兰线上超市:荷兰之家Holland at Home(支持中文)
2021/01/21 全球购物
说出你对remoting 和webservice的理解和应用
2014/06/08 面试题
幼儿园小班评语
2014/04/18 职场文书
协议书范文
2015/01/27 职场文书
小学生安全教育主题班会
2015/08/12 职场文书
WordPress多语言翻译插件 - WPML使用教程
2021/04/01 PHP
MySQL 分页查询的优化技巧
2021/05/12 MySQL
Vue Element-ui表单校验规则实现
2021/07/09 Vue.js
Github 使用python对copilot做些简单使用测试
2022/04/14 Python