你不知道的JavaScript閉包知識
原文合集地址如下,有需要的朋友可以關(guān)注
本文地址
合集地址
什么是閉包
當一個函數(shù)能夠記住并訪問它創(chuàng)建時的詞法作用域,即使該函數(shù)在其詞法作用域之外執(zhí)行,我們稱之為閉包。
閉包就像是一個背包,函數(shù)和它所需要的變量就像是放在背包里的物品。無論函數(shù)在哪里執(zhí)行,它都能夠打開背包并使用背包里的物品。
讓我們來看一個具體的例子:
function?createCounter()?{
??let?count?=?0;
??return?function?()?{
????count++;
????console.log(count);
??};
}
const?counter?=?createCounter();
counter();?//?輸出:1
counter();?//?輸出:2
在這個例子中,createCounter
函數(shù)返回了一個內(nèi)部函數(shù)。每次調(diào)用 createCounter
函數(shù)時,都會創(chuàng)建一個新的詞法環(huán)境,其中包含一個變量 count
。返回的內(nèi)部函數(shù)可以訪問并修改這個 count
變量。
當我們執(zhí)行 createCounter
并將返回的函數(shù)賦值給 counter
變量時,實際上我們創(chuàng)建了一個閉包。無論何時調(diào)用 counter
函數(shù),它都能夠訪問和修改 count
變量的值,因為它記住了 createCounter
函數(shù)創(chuàng)建時的詞法作用域。
換句話說,閉包允許內(nèi)部函數(shù)訪問外部函數(shù)的變量,即使外部函數(shù)已經(jīng)執(zhí)行完畢。
通俗地說,閉包就像是一個記憶力超強的函數(shù),它能夠記住它所需的東西,即使周圍的環(huán)境發(fā)生了變化。
特點
內(nèi)部函數(shù)可以訪問外部函數(shù)的變量和參數(shù)。 外部函數(shù)的變量和參數(shù)在內(nèi)部函數(shù)執(zhí)行時依然可用,即使外部函數(shù)已經(jīng)執(zhí)行完畢。 每次調(diào)用外部函數(shù)都會創(chuàng)建一個新的閉包,每個閉包都有獨立的詞法環(huán)境。 閉包在 JavaScript 中有許多應(yīng)用場景,例如:
創(chuàng)建私有變量:通過使用閉包,可以模擬私有變量的概念,將一些變量隱藏在函數(shù)作用域內(nèi),只能通過內(nèi)部函數(shù)來訪問。 封裝:通過閉包,可以創(chuàng)建一些具有封裝性質(zhì)的函數(shù),將一些數(shù)據(jù)和操作封裝在一個閉包中,提供特定的接口進行訪問和操作。 延遲執(zhí)行:使用閉包可以實現(xiàn)延遲執(zhí)行某個函數(shù),將函數(shù)和其所需的參數(shù)封裝在閉包中,然后在需要的時候執(zhí)行。
例子
當然!以下是一些其他常見的閉包示例:
1.?計數(shù)器函數(shù):
```javascript
function?createCounter()?{
??let?count?=?0;
??return?function()?{
????count++;
????console.log(count);
??};
}
const?counter1?=?createCounter();
counter1();?//?輸出:1
counter1();?//?輸出:2
const?counter2?=?createCounter();
counter2();?//?輸出:1
在這個例子中,createCounter
函數(shù)返回了一個閉包函數(shù),它記住了 count
變量的值。每次調(diào)用返回的閉包函數(shù)時,都會增加 count
的值并輸出結(jié)果。
私有變量和方法:
function?createPerson(name)?{
??let?age?=?0;
??function?getAge()?{
????return?age;
??}
??function?setAge(newAge)?{
????age?=?newAge;
??}
??return?{
????getName()?{
??????return?name;
????},
????getAge,
????setAge
??};
}
const?person?=?createPerson('John');
console.log(person.getName());?//?輸出:John
console.log(person.getAge());?//?輸出:0
person.setAge(30);
console.log(person.getAge());?//?輸出:30
在這個例子中,createPerson
函數(shù)返回了一個包含私有變量和方法的對象。只有通過返回的對象才能訪問和修改私有變量 age
,并且可以通過公共方法 getAge
和 setAge
來操作它。
事件處理函數(shù):
function?createButton()?{
??let?count?=?0;
??const?button?=?document.createElement('button');
??button.textContent?=?'Click?me';
??
??button.addEventListener('click',?function()?{
????count++;
????console.log(`Button?clicked?${count}?times`);
??});
??return?button;
}
const?button?=?createButton();
document.body.appendChild(button);
在這個例子中,createButton
函數(shù)創(chuàng)建一個按鈕元素,并添加了一個點擊事件處理函數(shù)。該事件處理函數(shù)是一個閉包,它可以訪問并修改 count
變量的值。每次點擊按鈕時,都會增加 count
的值并輸出結(jié)果。
閉包的作用與缺陷
閉包在合適的場景下是非常有用的,但過度或不正確地使用閉包可能會導致一些問題。以下是一些關(guān)于閉包的利弊以及如何避免潛在問題的建議:
優(yōu)點:
數(shù)據(jù)封裝和隱藏:閉包可以用于創(chuàng)建私有變量,將變量封裝在函數(shù)作用域內(nèi),只能通過內(nèi)部函數(shù)進行訪問,從而實現(xiàn)數(shù)據(jù)的封裝和隱藏。
保留狀態(tài):閉包可以使函數(shù)記住其創(chuàng)建時的狀態(tài),即使函數(shù)在其詞法作用域之外執(zhí)行,也能夠保留對變量的引用。
缺點和潛在問題:
內(nèi)存泄漏:閉包中的變量被引用時,即使函數(shù)執(zhí)行完畢,這些變量仍然存在于內(nèi)存中,可能導致內(nèi)存泄漏。要避免內(nèi)存泄漏,確保在不需要閉包時解除對其的引用。
性能影響:閉包會占用額外的內(nèi)存和處理時間,因為它需要記住詞法作用域和變量的引用。在大規(guī)?;蝾l繁使用閉包時,可能會對性能產(chǎn)生一定的影響。
變量混淆和意外的副作用:閉包可以訪問外部函數(shù)的變量,如果不小心修改了外部變量,可能會導致意想不到的副作用。要小心處理閉包中的變量,避免不必要的修改。
避免潛在問題的方法:
明確閉包的作用和范圍:確保閉包的使用是有必要的,仔細考慮閉包的作用和影響范圍。
避免循環(huán)引用:閉包中的變量不應(yīng)該持有對外部對象的引用,否則可能導致循環(huán)引用和內(nèi)存泄漏。確保在閉包中僅引用必要的變量。
及時釋放閉包:在不再需要閉包時,解除對其的引用,以便垃圾回收器可以回收不再使用的內(nèi)存。
總而言之,閉包是一種強大的概念,可以解決許多問題。合理使用閉包,注意內(nèi)存管理和性能方面的考慮,可以充分發(fā)揮其優(yōu)點,并避免潛在的問題。