如何解决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 相关文章推荐
鼠标滚轮改变图片大小的示例代码
Nov 20 Javascript
jquery对象和DOM对象的任意相互转换
Feb 21 Javascript
基于jquery实现即时检查格式是否正确的表单
May 06 Javascript
JSON与XML的区别对比及案例应用
Nov 11 Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
Feb 07 Javascript
js禁止浏览器的回退事件
Apr 20 Javascript
ES6扩展运算符的用途实例详解
Aug 20 Javascript
使用async、enterproxy控制并发数量的方法详解
Jan 02 Javascript
Vue实现动态创建和删除数据的方法
Mar 17 Javascript
vue动态路由:路由参数改变,视图不更新问题的解决
Nov 05 Javascript
jQuery利用cookie 实现本地收藏功能(不重复无需多次命名)
Nov 07 jQuery
JavaScript仿京东秒杀倒计时
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
apache+mysql+php+ssl服务器之完全安装攻略
2006/09/05 PHP
建立文件交换功能的脚本(一)
2006/10/09 PHP
PHP批量生成缩略图的代码
2008/07/19 PHP
Php 构造函数construct的前下划线是双的_
2009/12/08 PHP
PHP中图片等比缩放的实例
2013/03/24 PHP
解析thinkphp中的导入文件标签
2013/06/20 PHP
阿里对象存储OSS在laravel框架中的使用方法
2019/10/13 PHP
js 跨域和ajax 跨域问题小结
2009/07/01 Javascript
移动手机APP手指滑动切换图片特效附源码下载
2015/11/30 Javascript
详解vue-router 2.0 常用基础知识点之导航钩子
2017/05/10 Javascript
JS实现微信里判断页面是否被分享成功的方法
2017/06/06 Javascript
解决Jstree 选中父节点时被禁用的子节点也会选中的问题
2017/12/27 Javascript
vue里input根据value改变背景色的实例
2018/09/29 Javascript
Electron autoUpdater实现Windows安装包自动更新的方法
2018/12/24 Javascript
vue点击按钮实现简单页面的切换
2020/09/08 Javascript
[01:07:47]Secret vs Optic Supermajor 胜者组 BO3 第一场 6.4
2018/06/05 DOTA
[01:07:13]TNC vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
[02:21]2018完美盛典章节片——初心
2018/12/17 DOTA
python实现给字典添加条目的方法
2014/09/25 Python
flask框架实现连接sqlite3数据库的方法分析
2018/07/16 Python
vscode 配置 python3开发环境的方法
2019/09/19 Python
Django 实现外键去除自动添加的后缀‘_id’
2019/11/15 Python
tensorflow 获取所有variable或tensor的name示例
2020/01/04 Python
FOREO官方网站:LUNA露娜洁面仪
2016/11/28 全球购物
美国在线咖啡、茶和餐厅供应商:LollicupStore
2018/05/04 全球购物
GoDaddy英国:全球排名第一的域名注册商
2018/06/08 全球购物
Ted Baker美国官网:英国时尚品牌
2018/10/29 全球购物
意大利时尚奢侈品店:D’Aniello Boutique
2021/01/19 全球购物
意大利顶级奢侈品电商:LUISAVIAROMA(支持中文)
2020/05/26 全球购物
导购员的岗位职责
2014/02/08 职场文书
委托公证书范本
2014/04/03 职场文书
缓刑期间思想汇报范文
2014/10/10 职场文书
2014党的群众路线教育实践活动学习心得体会
2014/10/31 职场文书
学校党风廉政建设调研报告
2015/01/01 职场文书
2015年教师业务工作总结
2015/05/26 职场文书
php解析非标准json、非规范json的方式实例
2022/05/10 PHP