如何解决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之Getters和Setters 平台支持等详细介绍
Dec 07 Javascript
js获取通过ajax返回的map型的JSONArray的方法
Jan 09 Javascript
jQuery的css()方法用法实例
Dec 24 Javascript
swtich/if...else的替代语句
Aug 16 Javascript
win7下安装配置node.js+express开发环境
Dec 06 Javascript
JavaScript获取当前url根目录(路径)
Jun 17 Javascript
JS中正则表达式全局匹配模式 /g用法详解
Apr 01 Javascript
JS基于递归实现网页版计算器的方法分析
Dec 20 Javascript
vue.js在标签属性中插入变量参数的方法
Mar 06 Javascript
angular 实现同步验证器跨字段验证的方法
Apr 11 Javascript
javascript实现函数柯里化与反柯里化过程解析
Oct 08 Javascript
如何使用 JavaScript 操作浏览器历史记录 API
Nov 24 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生成网页快照 不用COM不用扩展.
2010/02/11 PHP
linux下为php添加curl扩展的方法
2011/07/29 PHP
javascript Firefox与IE 替换节点的方法
2010/02/24 Javascript
在IE6下发生Internet Explorer cannot open the Internet site错误
2010/06/21 Javascript
Jquery Ajax请求代码(2)
2011/01/07 Javascript
基于jQuery的让非HTML5浏览器支持placeholder属性的代码
2011/05/24 Javascript
Jquery 表格合并的问题分享
2011/09/17 Javascript
JavaScript各类型的关系图解
2015/10/16 Javascript
js实现仿微博滚动显示信息的效果
2015/12/21 Javascript
JS只能输入正整数的简单实例
2016/10/07 Javascript
基于JQuery及AJAX实现名人名言随机生成器
2017/02/10 Javascript
NodeJS、NPM安装配置步骤(windows版本) 以及环境变量详解
2017/05/13 NodeJs
详解nodejs的express如何自动生成项目框架
2017/07/12 NodeJs
解决vue build打包之后首页白屏的问题
2018/03/06 Javascript
WebSocket的通信过程与实现方法详解
2018/04/29 Javascript
一个手写的vue放大镜效果
2019/08/09 Javascript
JavaScript文档加载模式以及元素获取
2020/07/28 Javascript
[03:32]2014DOTA2西雅图邀请赛 CIS外卡赛赛前black专访
2014/07/09 DOTA
使用python BeautifulSoup库抓取58手机维修信息
2013/11/21 Python
Python Sleep休眠函数使用简单实例
2015/02/02 Python
python实现对图片进行旋转,放缩,裁剪的功能
2019/08/07 Python
基于pytorch的保存和加载模型参数的方法
2019/08/17 Python
Python多进程编程常用方法解析
2020/03/26 Python
Python虚拟环境venv用法详解
2020/05/25 Python
Python 解析xml文件的示例
2020/09/29 Python
selenium自动化测试入门实战
2020/12/21 Python
HTML5新增属性data-*和js/jquery之间的交互及注意事项
2017/08/08 HTML / CSS
匡威英国官网:Converse英国
2018/12/02 全球购物
六道php面试题附答案
2014/06/05 面试题
生态学毕业生自荐信
2013/10/27 职场文书
医学专业五年以上个人求职信
2013/12/03 职场文书
大专生的学习自我评价
2013/12/04 职场文书
小区文明倡议书
2014/05/16 职场文书
高中学生自我评价范文
2014/09/23 职场文书
材料员岗位职责
2015/02/10 职场文书
小学语文教师研修感悟
2015/11/18 职场文书