一. 背景
在一些场景下,函数有可能会被频繁地调用,而这些函数本身占用的内存或计算较大时,就可能造成大的性能问题。譬如以下场景:
window.onresize事件。window.onresize事件在浏览器窗口大小改变时被触发,而且浏览器每改变1px,该事件就被触发一次,触发的频率非常的高。如果在window.onresize事件中涉及到一些DOM节点相关的操作时,而DOM操作往往是非常耗内存的,这时浏览器有可能出现卡顿甚至崩溃的情况。
mousemove事件。如果给一个div元素上绑定了mousemove事件,当在该div上移动鼠标时,也会频频触发该mousemove事件函数。
window.onscroll事件。window.onscroll事件会在页面滚动时被触发,而且页面每滚动1px都会触发一次,触发的频率也非常高。
change事件。用户在input和textarea元素中每次改变value值都会触发change事件。当用户输入较快时,而change事件函数可能涉及到DOM操作等耗内存的操作时,则可能导致卡顿问题。
… …
而实际上,我们并不需要那么频繁地触发这些事件函数,只要能确保比较流畅的用户体验即可。
譬如拖拽浏览器窗口改变其大小时,window.onresize事件函数可能被触发了100次,而实际上我们只需要3、4次就可以达到比较流畅的用户体验。这就需要我们使用函数节流,按时间段来忽略一些事件请求,这个可以借助setTimeout函数来实现。
所以,函数节流需要做的是降低触发回调的频率。
二. 函数节流的原理
函数节流的原理是,将即将被执行的函数用setTimeout延迟一段时间后才执行。如果该次言辞执行还没有完成,则忽略接下来调用该函数的请求。
三. 函数节流的代码实现
*
* 节流函数
* @param {Function} fn 需要进行节流的函数
* @param {Int} delay 函数执行的时间间隔。默认值为500
*/
var throttle = function(fn, delay) {
var _self = fn, // 保存需要被延迟执行的函数引用
timer = null, // 定时器
firstTime = true; // 是否是第一次调用
return function() {
var args = arguments,
_this = this,
delay = delay || 500;
// 如果是第一次调用,不需要延迟执行
if(firstTime) {
_self.apply(_this, args);
firstTime = false;
return;
}
if(timer) {
return false;
}
// 延迟一段时间执行
timer = setTimeout(function() {
// 清除调原来的定时器
clearTimeout(timer);
timer = null;
_self.apply(_this, args);
}, delay);
};
}
/* =============== 客户端调用 =============== */
window.onresize = throttle(function() {
console.log("Window is resizeing.");
}, 200);