瀏覽器的 resize
、scroll
、keypress
、mousemove
等事件在觸發時,會不斷地調用綁定在事件上的回調函數,極大地浪費資源,下降前端性能。爲了優化體驗,須要對這類事件進行調用次數的限制。前端
做用是在短期內屢次觸發同一個函數,只執行最後一次,或者只在開始時執行。git
以用戶拖拽改變窗口大小,觸發 resize
事件爲例,在這過程當中窗口的大小一直在改變,因此若是咱們在 resize
事件中綁定函數,這個函數將會一直觸發,而這種狀況大多數狀況下是無心義的,還會形成資源的大量浪費。github
這時候可使用函數防抖來優化相關操做:數組
// 普通方案
window.addEventListener('resize', () => {
console.log('trigger');
})複製代碼
優化方案:瀏覽器
// debounce 函數接受一個函數和延遲執行的時間做爲參數
function debounce(fn, delay){
// 維護一個 timer
let timer = null;
return function() {
// 獲取函數的做用域和變量
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay)
}
}複製代碼
function foo() {
console.log('trigger');
}
// 在 debounce 中包裝咱們的函數,過 2 秒觸發一次
window.addEventListener('resize', debounce(foo, 2000));複製代碼
resize
事件上綁定處理函數,這時 debounce
函數會當即調用,實際上綁定的函數時 debounce
函數內部返回的函數。timer
而後從新設置超時調用。delay
時間後執行。咱們也能夠爲 debounce
函數加一個參數,能夠選擇是否當即執行函數markdown
function debounce(func, delay, immediate){
var timer = null;
return function(){
var context = this;
var args = arguments;
if(timer) clearTimeout(timer);
if(immediate){
var doNow = !timer;
timer = setTimeout(function(){
timer = null;
},delay);
if(doNow){
func.apply(context,args);
}
}else{
timer = setTimeout(function(){
func.apply(context,args);
},delay);
}
}
}複製代碼
相似於防抖,節流是在一段時間內只容許函數執行一次。app
應用場景如:輸入框的聯想,能夠限定用戶在輸入時,只在每兩秒鐘響應一次聯想。前端性能
可經過時間戳和定時器來實現。函數
時間戳實現:oop
var throttle = function(func, delay){
var prev = Date.now();
return function(){
var context = this;
var args = arguments;
var now = Date.now();
if(now-prev>=delay){
func.apply(context,args);
prev = Date.now();
}
}
}複製代碼
定時器實現:
var throttle = function(func, delay){
var timer = null;
return function(){
var context = this;
var args = arguments;
if(!timer){
timer = setTimeout(function(){
func.apply(context, args);
timer = null;
},delay);
}
}
}複製代碼
區別在於,使用時間戳實現的節流函數會在第一次觸發事件時當即執行,之後每過 delay 秒以後才執行一次,而且最後一次觸發事件不會被執行;而定時器實現的節流函數在第一次觸發時不會執行,而是在 delay 秒以後才執行,當最後一次中止觸發後,還會再執行一次函數。