如何解决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 相关文章推荐
jsp+javascript打造级连菜单的实例代码
Jun 14 Javascript
jQuery判断当前点击的是第几个li的代码
Sep 26 Javascript
jQuery中before()方法用法实例
Dec 25 Javascript
JavaScript控制两个列表框listbox左右交换数据的方法
Mar 18 Javascript
谈谈AngularJs中的隐藏和显示
Dec 09 Javascript
js实现移动端轮播图效果
Dec 09 Javascript
JavaScript代码判断输入的字符串是否含有特殊字符和表情代码实例
Aug 17 Javascript
Node.js自定义实现文件路由功能
Sep 22 Javascript
vue-cli的eslint相关用法
Sep 29 Javascript
node中的密码安全(加密)
Sep 17 Javascript
JavaScript实现与使用发布/订阅模式详解
Jan 19 Javascript
vue防止花括号{{}}闪烁v-text和v-html、v-cloak用法示例
Mar 13 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中Session的概念
2006/10/09 PHP
PHP之变量、常量学习笔记
2008/03/27 PHP
关于JSON以及JSON在PHP中的应用技巧
2013/11/27 PHP
解决file_get_contents无法请求https连接的方法
2013/12/17 PHP
PHP Imagick完美实现图片裁切、生成缩略图、添加水印
2016/02/22 PHP
验证token、回复图文\文本、推送消息的实用微信类php代码
2016/06/28 PHP
完美解决thinkphp唯一索引重复时出错的问题
2017/03/31 PHP
JavaScript词法作用域与调用对象深入理解
2012/11/29 Javascript
网站接入QQ登录的两种方法
2014/07/22 Javascript
EasyUi中的Combogrid 实现分页和动态搜索远程数据
2016/04/01 Javascript
Vuejs第六篇之Vuejs与form元素实例解析
2016/09/05 Javascript
js+html5生成自动排列对话框实例
2017/10/09 Javascript
微信小程序实战篇之购物车的实现代码示例
2017/11/30 Javascript
Vue组件通信之Bus的具体使用
2017/12/28 Javascript
如何利用@angular/cli V6.0直接开发PWA应用详解
2018/05/06 Javascript
Vue实现textarea固定输入行数与添加下划线样式的思路详解
2018/06/28 Javascript
原生js实现Flappy Bird小游戏
2018/12/24 Javascript
微信小程序开发实现的IP地址查询功能示例
2019/03/28 Javascript
jQuery pager.js 插件动态分页功能实例分析
2019/08/02 jQuery
windows下python安装小白入门教程
2018/09/18 Python
python在OpenCV里实现投影变换效果
2019/08/30 Python
利用python计算时间差(返回天数)
2019/09/07 Python
TensorFlow2.0:张量的合并与分割实例
2020/01/19 Python
pycharm不能运行.py文件的解决方法
2020/02/12 Python
Pytorch中的自动求梯度机制和Variable类实例
2020/02/29 Python
Python如何用filter函数筛选数据
2020/03/05 Python
浅析Python中字符串的intern机制
2020/10/03 Python
amazeui页面分析之登录页面的示例代码
2020/08/25 HTML / CSS
不假外出检讨书
2014/01/27 职场文书
酒店大堂副理的职责范文
2014/02/13 职场文书
文秘专业应届生求职信
2014/05/26 职场文书
治庸问责心得体会
2014/09/12 职场文书
工地食品安全责任书
2015/05/09 职场文书
2019开业庆典剪彩仪式主持词!
2019/07/22 职场文书
游戏开发中如何使用CocosCreator进行音效处理
2021/04/14 Javascript
Promise面试题详解之控制并发
2021/05/14 面试题