如何解决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的合并表格中相同文本的相邻单元格的代码
Apr 06 Javascript
js文本框走动跑马灯效果代码分享
Aug 25 Javascript
jquery实现全选、反选、获得所有选中的checkbox
Sep 13 Javascript
Bootstrap弹出框modal上层的输入框不能获得焦点问题的解决方法
Dec 13 Javascript
JS树形菜单组件Bootstrap TreeView使用方法详解
Dec 21 Javascript
jQuery密码强度验证控件使用详解
Jan 05 Javascript
纯js仿淘宝京东商品放大镜功能
Mar 02 Javascript
js脚本编写简单刷票投票系统
Jun 27 Javascript
React从react-router路由上做登陆验证控制的方法
May 10 Javascript
javascript中函数的写法实例代码详解
Oct 28 Javascript
详解koa2学习中使用 async 、await、promise解决异步的问题
Nov 13 Javascript
vue element-ui之怎么封装一个自己的组件的详解
May 20 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
Win2003下APACHE+PHP5+MYSQL4+PHPMYADMIN 的简易安装配置
2006/11/18 PHP
详解WordPress开发中过滤属性以及Sql语句的函数使用
2015/12/25 PHP
利用laravel搭建一个迷你博客实战教程
2017/08/13 PHP
分享5个非常有用的Laravel Blade指令
2018/05/30 PHP
PHP接口类(interface)的定义、特点和应用示例
2020/05/18 PHP
jQuery实现瀑布流布局
2014/12/12 Javascript
jquery事件preventDefault()方法用法实例
2015/01/16 Javascript
基于JavaScript如何制作遮罩层对话框
2016/01/26 Javascript
借助node实战JSONP跨域实例
2017/03/30 Javascript
AngularJS ionic手势事件的使用总结
2017/08/09 Javascript
javascript用rem来做响应式开发
2018/01/13 Javascript
vue2.0 element-ui中el-select选择器无法显示选中的内容(解决方法)
2018/08/24 Javascript
解决vue中使用proxy配置不同端口和ip接口问题
2019/08/14 Javascript
微信小程序可滑动月日历组件使用详解
2019/10/21 Javascript
微信小程序实现上拉加载功能
2019/11/20 Javascript
如何在postman测试用例中实现断言过程解析
2020/07/09 Javascript
解决ant-design-vue中menu菜单无法默认展开的问题
2020/10/31 Javascript
python3抓取中文网页的方法
2015/07/28 Python
Using Django with GAE Python 后台抓取多个网站的页面全文
2016/02/17 Python
python对json的相关操作实例详解
2017/01/04 Python
python替换字符串中的子串图文步骤
2019/06/19 Python
django-rest-framework解析请求参数过程详解
2019/07/18 Python
python 监测内存和cpu的使用率实例
2019/11/28 Python
python str字符串转uuid实例
2020/03/03 Python
通过实例解析python创建进程常用方法
2020/06/19 Python
python 第三方库paramiko的常用方式
2021/02/20 Python
HTML5语音识别标签写法附图
2013/11/18 HTML / CSS
Kidsroom台湾:来自德国的婴儿用品
2017/12/11 全球购物
汽车专业毕业生推荐信
2013/11/12 职场文书
培训心得体会
2013/12/29 职场文书
2014年两会学习心得范例
2014/03/17 职场文书
银行先进个人事迹材料
2014/05/11 职场文书
服务标语口号
2014/07/01 职场文书
户籍证明书标准模板
2014/09/10 职场文书
教师考核表个人总结
2015/02/12 职场文书
入党积极分子培养人意见
2015/06/02 职场文书