一. 背景

在一些场景下,函数有可能会被频繁地调用,而这些函数本身占用的内存或计算较大时,就可能造成大的性能问题。譬如以下场景:

  • 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);

四. 总结

函数节流适合于需要频繁调用,但又在一定的时间内必须要执行逻辑的场景。

巧用函数节流方式能够显著得提高页面性能以及交互体验,而函数的节流频率还有待在实际项目中多调试才能获得最优值。

本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/js/1166