如何解决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 getElementsByClassName 和js取地址栏参数
Jan 02 Javascript
javascript函数以及基础写法100多条实用整理
Jan 13 Javascript
js读取注册表的键值示例
Sep 25 Javascript
基于jquery扩展漂亮的CheckBox(自己编写)
Nov 19 Javascript
node.js中实现同步操作的3种实现方法
Dec 05 Javascript
浅谈js和css内联外联注意事项
Jun 30 Javascript
Vue.js第一天学习笔记(数据的双向绑定、常用指令)
Dec 01 Javascript
JS实现颜色动态淡化效果
Mar 06 Javascript
JS实现点击下拉菜单把选择的内容同步到input输入框内的实例
Jan 23 Javascript
在微信小程序中使用图表的方法示例
Apr 25 Javascript
vue使用video.js进行视频播放功能
Jul 18 Javascript
vue项目如何打包之项目打包优化(让打包的js文件变小)
Apr 30 Vue.js
使用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中怎样保持SESSION不过期 原理及方案介绍
2013/08/08 PHP
关于php程序报date()警告的处理(date_default_timezone_set)
2013/10/22 PHP
php+jQuery.uploadify实现文件上传教程
2014/12/26 PHP
PHP设置进度条的方法
2015/07/08 PHP
PHP模板引擎Smarty中变量的使用方法示例
2016/04/11 PHP
详解PHP函数 strip_tags 处理字符串缺陷bug
2017/06/11 PHP
php使用str_replace替换多维数组的实现方法分析
2017/06/15 PHP
javascript使用中为什么10..toString()正常而10.toString()出错呢
2013/01/11 Javascript
JavaScript中for..in循环陷阱介绍
2013/11/12 Javascript
javascript日期格式化示例分享
2014/03/05 Javascript
jQuery如何使用自动触发事件trigger
2015/11/29 Javascript
js+ajax实现获取文件大小的方法
2015/12/08 Javascript
js实现内容显示并使用json传输数据
2016/03/16 Javascript
js调用webservice构造SOAP进行身份验证
2016/04/27 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
微信小程序 基础知识css样式media标签
2017/02/15 Javascript
JS和Canvas实现图片的预览压缩和上传功能
2018/03/30 Javascript
vue+axios+mock.js环境搭建的方法步骤
2018/08/28 Javascript
Vue项目中使用better-scroll实现菜单映射功能方法
2019/09/11 Javascript
laydate只显示时分 不显示秒的功能实现方法
2019/09/28 Javascript
小程序外卖订单界面的示例代码
2019/12/30 Javascript
[01:08:48]LGD vs OG 2018国际邀请赛淘汰赛BO3 第三场 8.25
2018/08/29 DOTA
Python3 中文文件读写方法
2018/01/23 Python
基于python 二维数组及画图的实例详解
2018/04/03 Python
在ipython notebook中使用argparse方式
2020/04/20 Python
Python grequests模块使用场景及代码实例
2020/08/10 Python
Python中Yield的基本用法
2020/10/18 Python
美国顶级户外凉鞋品牌:Chacos
2017/03/27 全球购物
Geekbuying波兰:购买中国电子产品
2019/10/20 全球购物
原材料检验岗位职责
2014/03/15 职场文书
2015年技术员工作总结
2015/04/10 职场文书
教师创先争优承诺书
2015/04/27 职场文书
大国崛起观后感
2015/06/02 职场文书
防溺水安全教育主题班会
2015/08/12 职场文书
党风廉洁教育心得体会
2016/01/20 职场文书
python利用pandas分析学生期末成绩实例代码
2021/07/09 Python