JS中setTimeout的巧妙用法前端函数节流


Posted in Javascript onMarch 24, 2016

什么是函数节流?

函数节流简单的来说就是不想让该函数在很短的时间内连续被调用,比如我们最常见的是窗口缩放的时候,经常会执行一些其他的操作函数,比如发一个ajax请求等等事情,那么这时候窗口缩放的时候,有可能连续发多个请求,这并不是我们想要的,或者是说我们常见的鼠标移入移出tab切换效果,有时候连续且移动的很快的时候,会有闪烁的效果,这时候我们就可以使用函数节流来操作。大家都知道,DOM的操作会很消耗或影响性能的,如果是说在窗口缩放的时候,为元素绑定大量的dom操作的话,会引发大量的连续计算,比如在IE下,过多的DOM操作会影响浏览器性能,甚至严重的情况下,会引起浏览器崩溃的发生。这个时候我们就可以使用函数节流来优化代码了~

函数节流的基本原理:

使用一个定时器,先延时该函数的执行,比如使用setTomeout()这个函数延迟一段时间后执行函数,如果在该时间段内还触发了其他事件,我们可以使用清除方法 clearTimeout()来清除该定时器,再setTimeout()一个新的定时器延迟一会儿执行。

最近在某团队忙于一个项目,有这么一个页面,采用传统模式开发(吐槽它为什么不用React),它的DOM操作比较多,然后性能是比较差的,尤其当你缩放窗口时,可怕的事情发生了,出现了卡顿,甚至浏览器瘫痪。为什么呢?

由于该页面的DOM操作非常多,故窗口缩放每一帧时都会触发函数的执行,连续的重新DOM操作,这样对浏览器的开销是非常大的。既然在窗口缩放时,会让浏览器重新计算DOM,那么我们为什么不可以让DOM的计算延时呢,让窗口停止缩放后才重新计,这样不就节省了浏览器的开销,达到优化的效果了吗?

知识准备

1. setTimeout(code,millisec) 当然就是本文的主角了。

setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。

code必需。要调用的函数后要执行的 JavaScript 代码串。

millisec必需。在执行代码前需等待的毫秒数。

提示:setTimeout() 只执行 code 一次。如果要多次调用,请使用 setInterval() 或者让 code 自身再次调用 setTimeout()。

广泛应用于定时器,轮播图,动画效果,自动滚动等等。

2. clearTimeout(id_of_setTimeout)

参数 id_of_settimeout由 setTimeout() 返回的 ID 值。该值标识要取消的延迟执行代码块。

3. fun.apply(thisArg[, argsArray])

apply() 方法在指定 this 值和参数(参数以数组或类数组对象的形式存在)的情况下调用某个函数

该函数的语法与call()方法几乎相同,唯一的区别在于,call()方法接受的是一个参数列表,而apply()接受的是一个包含多个参数数组的(或类数组对象)。

参数

thisArg

在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

argsArray

一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

4. fun.call(thisArg[, arg1[, arg2[, ...]]])

该 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法.

参数

thisArg

在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

arg1, arg2, ...

指定的参数列表。

当调用一个函数时,可以赋值一个不同的 this 对象。this 引用当前对象,即 call 方法的第一个参数。通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法。

作用:

使用call方法调用父构造函数

使用call方法调用匿名函数

使用call方法调用匿名函数并且指定上下文的'this'

这里插个题外话:

apply 与 call() 非常相似,不同之处在于提供参数的方式。apply 使用参数数组而不是一组参数列表。apply 可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))。你也可以使用 arguments 对象作为 argsArray 参数。 arguments 是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。

从 ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个 length 属性和[0...length) 范围的整数属性。例如现在可以使用 NodeList 或一个自己定义的类似 {'length': 2, '0': 'eat', '1': 'bananas'} 形式的对象。

call, apply方法区别是,从第二个参数起, call方法参数将依次传递给借用的方法作参数, 而apply 直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是一样的.

应用场景:当参数明确时可用call, 当参数不明确时可用apply给合arguments

现在先给出一个例子

总所皆知,onscolll,onresize等是非常耗性能,窗口缩放时打印数字。

var count = ;
window.onresize = function () {
count++;
console.log(count);
}

在chrome浏览器中伸缩浏览器窗口大小,打印如下

这显然不是我们想要的,那如果我们换成ajax请求的话,那么就会缩放一次窗口会连续触发多次ajax请求,下面我们试着使用函数节流的操作试试一下;当然加个settimeout()的定时器就好了,

第一种封装方法

var count = ;
function oCount() {
count++;
console.log(count);
}
window.onresize = function () {
delayFun(oCount)
};
function delayFun(method, thisArg) {
clearTimeout(method.props);
method.props = setTimeout(function () {
method.call(thisArg)
}, )
}

第二种封装方法

构造一个闭包,使用闭包的方式形成一个私有的作用域来存放定时器timer, timer是通过传参数的形式引入的。

var count = ;
function oCount() {
count++;
console.log(count);
}
var funs= delayFun(oCount,);
window.onresize = function () {
funs()
};
function delayFun(func, wait) {
var timer = null;
return function () {
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
func.apply(context, args);
}, wait)
};
}

对第二种方法优化一下,性能会更好

这里返回一个函数,如果它被不间断地调用,它将不会得到执行。该函数在停止调用 N 毫秒后,再次调用它才会得到执行。如果有传递 ‘immediate' 参数,会马上将函数安排到执行队列中,而不会延迟。

function delayFun (func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// 用法
var myEfficientFn = delayFun (function() {
// 所有繁重的操作
}, );
window.addEventListener('resize', myEfficientFn);

函数不允许回调函数在指定时间内执行多于一次。当为一个会频繁触发的事件分配一个回调函数时,该函数显得尤为重要。

setTimeout这么厉害,那么我们是可以在项目中大量使用吗?

我个人是不建议的,在我们业务中,基本上是禁止在业务逻辑中使用setTimeout的,因为我所看到的很多使用方式都是一些问题好解决,setTimeout作为一个hack的方式。

例如,当一个实例还没有初始化的前,我们就使用这个实例,错误的解决办法是使用实例时加个setTimeout,确保实例先初始化。

为什么错误?这里其实就是使用hack的手段

第一是埋下了坑,打乱模块的生命周期

第二是出现问题时,setTimeout其实是很难调试的。

我认为正确的使用方式是,看看生命周期(可参考《关于软件的生命周期 》),把实例化提到使用前执行。

有关JS中setTimeout的巧妙用法前端函数节流,小编就给大家介绍到这里,希望对大家有所帮助!

Javascript 相关文章推荐
在JavaScript中通过URL传递汉字的方法
Apr 09 Javascript
js 操作select和option常用代码整理
Dec 13 Javascript
侧栏跟随滚动的简单实现代码
Mar 18 Javascript
javascript实现百度地图鼠标滑动事件显示、隐藏
Apr 02 Javascript
jquery事件的ready()方法使用详解
Nov 11 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
Aug 03 Javascript
详解用node搭建简单的静态资源管理器
Aug 09 Javascript
VUE+Element UI实现简单的表格行内编辑效果的示例的代码
Oct 31 Javascript
vue中v-for通过动态绑定class实现触发效果
Dec 06 Javascript
JS实现的合并两个有序链表算法示例
Feb 25 Javascript
JavaScript实现轮播图片完整代码
Mar 07 Javascript
vue同个按钮控制展开和折叠同个事件操作
Jul 29 Javascript
Zero Clipboard实现浏览器复制到剪贴板的方法(多个复制按钮)
Mar 24 #Javascript
不能不知道的10个angularjs英文学习网站
Mar 23 #Javascript
AngularJS 让人爱不释手的八种功能
Mar 23 #Javascript
java中String类型变量的赋值问题介绍
Mar 23 #Javascript
基于javascript实现泡泡大冒险网页版小游戏
Mar 23 #Javascript
基于javascript实现句子翻牌网页版小游戏
Mar 23 #Javascript
基于jquery编写的放大镜插件
Mar 23 #Javascript
You might like
用PHP实现登陆验证码(类似条行码状)
2006/10/09 PHP
在PHP中使用Sockets 从Usenet中获取文件
2008/01/10 PHP
php将mysql数据库整库导出生成sql文件的具体实现
2014/01/08 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
php的debug相关函数用法示例
2016/07/11 PHP
PHP中时间加减函数strtotime用法分析
2017/04/26 PHP
PHP并发查询MySQL的实例代码
2017/08/09 PHP
thinkphp5.1 文件引入路径问题及注意事项
2018/06/13 PHP
WEB页子窗口(showModalDialog和showModelessDialog)使用说明
2009/10/25 Javascript
js使用setTimeout实现定时炸弹的方法
2015/04/10 Javascript
js实现键盘上下左右键选择文字并显示在文本框的方法
2015/05/07 Javascript
jQuery中Ajax全局事件引用方式及各个事件(全局/局部)执行顺序
2016/06/02 Javascript
vue获取DOM元素并设置属性的两种实现方法
2017/09/30 Javascript
JS使用正则表达式找出最长连续子串长度
2017/10/26 Javascript
vue富文本框(插入文本、图片、视频)的使用及问题小结
2018/08/17 Javascript
vue利用v-for嵌套输出多层对象,分别输出到个表的方法
2018/09/07 Javascript
纯js+css实现在线时钟
2020/08/18 Javascript
vue修改Element的el-table样式的4种方法
2020/09/17 Javascript
Python Web框架Flask信号机制(signals)介绍
2015/01/01 Python
浅谈python对象数据的读写权限
2016/09/12 Python
Bottle框架中的装饰器类和描述符应用详解
2017/10/28 Python
Python scikit-learn 做线性回归的示例代码
2017/11/01 Python
python简单图片操作:打开\显示\保存图像方法介绍
2017/11/23 Python
python登录并爬取淘宝信息代码示例
2017/12/09 Python
详解Django-auth-ldap 配置方法
2018/12/10 Python
使用pandas实现连续数据的离散化处理方式(分箱操作)
2019/11/22 Python
Django Haystack 全文检索与关键词高亮的实现
2020/02/17 Python
python线程池如何使用
2020/05/28 Python
桥梁与隧道工程专业本科生求职信
2013/10/08 职场文书
个人充满哲理的自我评价
2014/02/20 职场文书
超市促销活动总结
2014/07/01 职场文书
夫妻分居协议书范文
2014/11/26 职场文书
党员倡议书
2015/01/19 职场文书
python3 实现mysql数据库连接池的示例代码
2021/04/17 Python
win10清理dns缓存
2022/04/19 数码科技
六个好看实用的 HTML + CSS 后台登录入口页面
2022/04/28 HTML / CSS