前端學習14 閉包
1.閉包
閉包是 JavaScript 中的一個核心概念,它允許函數(shù)訪問其定義時的詞法作用域中的變量。這種特性使得閉包在數(shù)據封裝、回調函數(shù)、模塊模式等場景中有著廣泛的應用。
1.1 閉包的定義:
閉包是指有權訪問另一個函數(shù)作用域的變量的函數(shù)。在JavaScript中,閉包可以通過函數(shù)嵌套和變量應用實現(xiàn)。
function a() {
let x = '我在outer函數(shù)里!';
function b() {
console.log(x);
}
return b;
}
const c = a();
c(); // 輸出: 我在outer函數(shù)里!
在這個例子中,b函數(shù)引用了x變量,因此JavaScript引擎會保留a函數(shù)的作用域鏈,以便b函數(shù)可以訪問x變量。
1.2閉包的實現(xiàn)
閉包是指一個函數(shù)可以訪問它定義時所在的詞法作用域以及全局作用域中的變量。當內部函數(shù)引用外部函數(shù)的變量時,外部函數(shù)的作用域鏈將被保留在內存中,以便內部函數(shù)可以訪問這些變量。
function a() {
var aa = 333;
function b() {
console.log(aa); // 輸出:333
}
return b;
}
var demo = a();
demo(); // 輸出:333
在這個例子中,b函數(shù)引用了a函數(shù)中的變量aa,因此即使a函數(shù)執(zhí)行完畢后,aa變量仍然可以通過b函數(shù)被訪問,形成了閉包。
2.閉包的用途
2.1 封裝私有變量
閉包可以用于封裝私有變量,以防止其被外部訪問和修改。這可以減少全局變量的數(shù)量,降低全局變量被誤用或意外修改的風險。
function addFn() {
let count = 1;
function increment() {
count++;
console.log(count);
}
return increment;
}
const counter = addFn();
counter(); // 輸出:2
counter(); // 輸出:3
在這個例子中,increment函數(shù)通過閉包訪問并修改了count變量,而count變量不會被外部直接訪問。
2.2 做緩存
閉包可以用于緩存計算結果,減少重復計算。
function fn1() {
var type = 'JavaScript';
let tt1 = 1;
const tt2 = 2;
var innerBar = {
getType: function() {
console.log(tt1);
return type;
},
setType: function(newType) {
type = newType;
}
};
return innerBar;
}
var bar = fn1();
console.log(bar.getType()); // 輸出:1 JavaScript
bar.setType('Python');
console.log(bar.getType()); // 輸出:1 Python
在這個例子中,getType和setType方法通過閉包訪問并修改了type變量。
2.3 模塊化編程
閉包可以用于實現(xiàn)模塊化編程,封裝模塊的私有變量和方法。
const moduleFn = (function() {
let privateVar = '我是私有變量!';
function privateMethod() {
console.log(privateVar);
}
return {
publicMethod: function() {
privateMethod();
}
};
})();
moduleFn.publicMethod(); // 輸出:我是私有的!
在這個例子中,privateVar和privateMethod被封裝在模塊內部,只能通過publicMethod訪問。
2.4 防抖
防抖的本質需求是:在多次觸發(fā)事件時,能夠記住上一次定時器(timer),并且在新的觸發(fā)時清除掉舊的定時器。
function antishake(fn, wait) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, wait);
};
}
let an = antishake(function() {
console.log('555');
}, 2000);
document.querySelector('div').onmouseenter = () => {
an();
};
當鼠標進入div時,執(zhí)行an(),也就是調用了內部函數(shù),clearTiimeout(timer),清除之前的setTimeout,然后重新設置一個定時器。
如果鼠標在2秒內反復移動到div上,每次都會清除舊的setTimeout。
閉包讓 timer 變量在多次事件觸發(fā)過程中保持持久存在,能被正確讀取和修改,從而實現(xiàn)防抖。
2.5 節(jié)流
節(jié)流的核心思想為:一段時間內,只允許執(zhí)行一次函數(shù) fn。如果在 wait 時間內多次觸發(fā)事件,都不會觸發(fā)函數(shù)。
function throttle(fn, wait) {
let timer = null;
return function() {
if (timer) return;
timer = setTimeout(() => {
fn();
timer = null;
}, wait);
};
}
let throttle1 = throttle(() => {
console.log('我上車了');
}, 2000);
document.querySelector('div').onclick = () => {
throttle1();
};
節(jié)流需要記錄“上一次觸發(fā)時間”或“是否還在等待中”,否則就無法判斷下一次是否可以執(zhí)行。 首次點擊時 timer 是 null,允許執(zhí)行函數(shù);
設置 timer,2秒后執(zhí)行函數(shù)并將 timer 設為 null;
若用戶在這 2 秒內重復點擊,timer 不為 null,直接 return 不執(zhí)行。
所以閉包保存并共享了 timer 變量狀態(tài),保證了“時間窗”內只執(zhí)行一次。
3.閉包的缺點
閉包不會導致內存泄漏。
內存泄露是指你用不到(訪問不到)的變量,依然占居著內存空間,不能被再次利用起來。
閉包里面的變量明明就是我們需要的變量(lives),憑什么說是內存泄露?。
因為 IE。IE 有 bug,IE 在我們使用完閉包之后,依然回收不了閉包里面引用的變量。
這是 IE 的問題,不是閉包的問題。
4.總結
閉包是JavaScript中一個強大的特性,它允許函數(shù)訪問其創(chuàng)建時的詞法作用域。通過閉包,可以實現(xiàn)數(shù)據封裝、事件處理、延遲執(zhí)行等多種高級功能。理解閉包的概念和工作原理對于深入掌握JavaScript至關重要。
#前端學習#