函数节流
背景
javascript中的函数大多数情况下都是由用户主动调用触发的, 除非是函数本身的实现不合理, 否则一般不会遇到跟性能相关的问题,但在少数情况下, 函数的触发不是由用户直接控制的. 在这些场景下, 函数可能被非常频繁调用, 而造成大的性能问题.
场景
window.onresize事件
mousemove事件scroll滚动事件共同的特征:高频触发事件函数, 若事件函数里附带DOM相关操作, 会造成非常大的性能消耗.原理
将即将被执行的函数使用setTimeout延迟一段时间执行, 如果该次延迟执行还没有完成, 则忽略接下来调用该函数的请求.
实现
示例1
underscore.js的函数节流定义: _.throttle(fn, wait, [options]);
_.throttle接收三个参数, 第一次执行默认立刻执行一次fn@params fn: 需要进行函数节流的函数;@params wait: 函数执行的时间间隔, 单位是毫秒.@params options: options有两个选项, 分别是: {leading: false}: 第一次调用不执行fn {trailing: false}: 禁止最后一次延迟的调用_.throttle = function(fn, wait, options) { var context, args, result, timeout = null, previous = 0; if(!options) { options = {}; } var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = fn.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 = fn.apply(context, args); if(!timeout) { context = args = null; } else if(!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; } };};// demo:$(window).scroll(_.throttle(function() { //相关处理}, 500));
示例2
《javascript设计模式与开发实战》中对函数节流示例:
throttle函数接收两个参数@params fn: 需要被延迟执行的函数;@params interval: 延迟执行的时间;var throttle = function(fn, interval) { var _self = fn, // 保存需要被延迟执行的函数引用 timer, // 计时器 firstTime = true; // 是否第一次调用 return function() { var args = arguments, _this = this; if(firstTime) { // 如果是第一次调用, 不需要延迟执行 _self.apply(_this, args); return firstTime = false; } if(timer) { // 如果定时器还在, 说明前一次延迟执行还未完成 return false; } timer = setTimeout(function() { clearTimeout(timer); timer = null; _self.apply(_this, args); }, interval || 500); };};// demo:window.onresize = throttle(function() { console.log(1);}, 500);