Izpētiet JavaScript closure, izmantojot praktiskus piemērus, izprotot, kā tie darbojas un kādi ir to reālās pasaules pielietojumi programmatūras izstrādē.
JavaScript Closure: Demistifikācija ar praktiskiem piemēriem
Closure ir būtisks jēdziens JavaScript, kas bieži rada apjukumu visu līmeņu izstrādātājiem. Closure izpratne ir ļoti svarīga, lai rakstītu efektīvu, uzturējamu un drošu kodu. Šī visaptverošā rokasgrāmata demistificēs closure ar praktiskiem piemēriem un parādīs to reālās pasaules pielietojumus.
Kas ir Closure?
Vienkāršiem vārdiem sakot, closure ir funkcijas un leksiskās vides kombinācija, kurā šī funkcija tika deklarēta. Tas nozīmē, ka closure ļauj funkcijai piekļūt mainīgajiem no tās apkārtējās darbības jomas pat pēc tam, kad ārējā funkcija ir beigusi darboties. Domājiet par to kā par iekšējo funkciju, kas "atceras" savu vidi.
Lai patiesi to saprastu, sadalīsim galvenās sastāvdaļas:
- Funkcija: Iekšējā funkcija, kas veido closure daļu.
- Leksiskā vide: Apkārtējā darbības joma, kurā funkcija tika deklarēta. Tas ietver mainīgos, funkcijas un citas deklarācijas.
Maģija notiek tāpēc, ka iekšējā funkcija saglabā piekļuvi mainīgajiem savā leksiskajā darbības jomā pat pēc tam, kad ārējā funkcija ir atgriezusies. Šī uzvedība ir būtiska daļa no tā, kā JavaScript apstrādā darbības jomu un atmiņas pārvaldību.
Kāpēc Closure ir Svarīgi?
Closure nav tikai teorētisks jēdziens; tie ir būtiski daudziem izplatītiem programmēšanas modeļiem JavaScript. Tie nodrošina šādus ieguvumus:
- Datu iekapsulēšana: Closure ļauj jums izveidot privātus mainīgos un metodes, aizsargājot datus no ārējas piekļuves un modifikācijas.
- Stāvokļa saglabāšana: Closure saglabā mainīgo stāvokli starp funkciju izsaukumiem, kas ir noderīgi, lai izveidotu skaitītājus, taimerus un citus stāvokli saturošus komponentus.
- Augstākas kārtas funkcijas: Closure bieži tiek izmantoti kopā ar augstākas kārtas funkcijām (funkcijām, kas pieņem citas funkcijas kā argumentus vai atgriež funkcijas), nodrošinot jaudīgu un elastīgu kodu.
- Asinhronais JavaScript: Closure spēlē būtisku lomu asinhrono operāciju pārvaldībā, piemēram, atzvanīšanas funkcijās un solījumos.
Praktiski JavaScript Closure Piemēri
Iedziļināsimies dažos praktiskos piemēros, lai ilustrētu, kā closure darbojas un kā tos var izmantot reālās pasaules scenārijos.
1. piemērs: Vienkāršs skaitītājs
Šis piemērs parāda, kā closure var izmantot, lai izveidotu skaitītāju, kas saglabā savu stāvokli starp funkciju izsaukumiem.
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const increment = createCounter();
increment(); // Output: 1
increment(); // Output: 2
increment(); // Output: 3
Paskaidrojums:
createCounter()
ir ārējā funkcija, kas deklarē mainīgocount
.- Tā atgriež iekšējo funkciju (šajā gadījumā anonīmu funkciju), kas palielina
count
un reģistrē tās vērtību. - Iekšējā funkcija veido closure pār
count
mainīgo. - Pat pēc tam, kad
createCounter()
ir beigusi darboties, iekšējā funkcija saglabā piekļuvicount
mainīgajam. - Katrs izsaukums
increment()
palielina to pašucount
mainīgo, parādot closure spēju saglabāt stāvokli.
2. piemērs: Datu iekapsulēšana ar privātiem mainīgajiem
Closure var izmantot, lai izveidotu privātus mainīgos, aizsargājot datus no tiešas piekļuves un modifikācijas no funkcijas ārpuses.
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: function(amount) {
balance += amount;
return balance; //Returning for demonstration, could be void
},
withdraw: function(amount) {
if (amount <= balance) {
balance -= amount;
return balance; //Returning for demonstration, could be void
} else {
return "Insufficient funds.";
}
},
getBalance: function() {
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.deposit(500)); // Output: 1500
console.log(account.withdraw(200)); // Output: 1300
console.log(account.getBalance()); // Output: 1300
// Trying to access balance directly will not work
// console.log(account.balance); // Output: undefined
Paskaidrojums:
createBankAccount()
izveido bankas konta objektu ar metodēm iemaksai, izņemšanai un bilances saņemšanai.balance
mainīgais ir deklarētscreateBankAccount()
darbības jomā un nav tieši pieejams no ārpuses.- Metodes
deposit
,withdraw
ungetBalance
veido closure pārbalance
mainīgo. - Šīs metodes var piekļūt un modificēt
balance
mainīgo, bet pats mainīgais paliek privāts.
3. piemērs: Closure lietošana ar `setTimeout` ciklā
Closure ir būtiski, strādājot ar asinhronām operācijām, piemēram, setTimeout
, īpaši ciklos. Bez closure var rasties neparedzēta uzvedība JavaScript asinhronās būtības dēļ.
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log("Value of i: " + j);
}, j * 1000);
})(i);
}
// Output:
// Value of i: 1 (after 1 second)
// Value of i: 2 (after 2 seconds)
// Value of i: 3 (after 3 seconds)
// Value of i: 4 (after 4 seconds)
// Value of i: 5 (after 5 seconds)
Paskaidrojums:
- Bez closure (nekavējoties izsauktās funkcijas izteiksmes jeb IIFE) visas
setTimeout
atzvanīšanas funkcijas galu galā atsauktos uz to pašui
mainīgo, kuram pēc cikla pabeigšanas būtu galīgā vērtība 6. - IIFE izveido jaunu darbības jomu katrai cikla iterācijai, uztverot pašreizējo
i
vērtībuj
parametrā. - Katra
setTimeout
atzvanīšanas funkcija veido closure pārj
mainīgo, nodrošinot, ka tā reģistrē pareizoi
vērtību katrai iterācijai.
Izmantojot let
tā vietā, lai var
ciklā, arī atrisinātu šo problēmu, jo let
izveido bloka darbības jomu katrai iterācijai.
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log("Value of i: " + i);
}, i * 1000);
}
// Output (same as above):
// Value of i: 1 (after 1 second)
// Value of i: 2 (after 2 seconds)
// Value of i: 3 (after 3 seconds)
// Value of i: 4 (after 4 seconds)
// Value of i: 5 (after 5 seconds)
4. piemērs: Currying un daļēja pielietošana
Closure ir fundamentāli currying un daļējai pielietošanai, paņēmieniem, ko izmanto, lai pārveidotu funkcijas ar vairākiem argumentiem funkciju virknēs, kuras katra pieņem vienu argumentu.
function multiply(a) {
return function(b) {
return function(c) {
return a * b * c;
};
};
}
const multiplyBy5 = multiply(5);
const multiplyBy5And2 = multiplyBy5(2);
console.log(multiplyBy5And2(3)); // Output: 30 (5 * 2 * 3)
Paskaidrojums:
multiply
ir curried funkcija, kas pieņem trīs argumentus, pa vienam.- Katra iekšējā funkcija veido closure pār mainīgajiem no tās ārējās darbības jomas (
a
,b
). multiplyBy5
ir funkcija, kurai jau ira
iestatīts uz 5.multiplyBy5And2
ir funkcija, kurai jau ira
iestatīts uz 5 unb
iestatīts uz 2.- Pēdējais izsaukums
multiplyBy5And2(3)
pabeidz aprēķinu un atgriež rezultātu.
5. piemērs: Moduļa modelis
Closure tiek plaši izmantoti moduļa modelī, kas palīdz organizēt un strukturēt JavaScript kodu, veicinot modularitāti un novēršot nosaukumu konfliktus.
const myModule = (function() {
let privateVariable = "Hello, world!";
function privateMethod() {
console.log(privateVariable);
}
return {
publicMethod: function() {
privateMethod();
},
publicProperty: "This is a public property."
};
})();
console.log(myModule.publicProperty); // Output: This is a public property.
myModule.publicMethod(); // Output: Hello, world!
// Trying to access privateVariable or privateMethod directly will not work
// console.log(myModule.privateVariable); // Output: undefined
// myModule.privateMethod(); // Output: TypeError: myModule.privateMethod is not a function
Paskaidrojums:
- IIFE izveido jaunu darbības jomu, iekapsulējot
privateVariable
unprivateMethod
. - Atgrieztais objekts atklāj tikai
publicMethod
unpublicProperty
. publicMethod
veido closure pārprivateMethod
unprivateVariable
, ļaujot tam piekļūt tiem pat pēc tam, kad IIFE ir izpildīts.- Šis modelis efektīvi izveido moduli ar privātiem un publiskiem dalībniekiem.
Closure un atmiņas pārvaldība
Lai gan closure ir jaudīgi, ir svarīgi apzināties to potenciālo ietekmi uz atmiņas pārvaldību. Tā kā closure saglabā piekļuvi mainīgajiem no to apkārtējās darbības jomas, tie var neļaut šiem mainīgajiem tikt savāktiem kā atkritumiem, ja tie vairs nav vajadzīgi. Tas var izraisīt atmiņas noplūdes, ja to neapstrādā uzmanīgi.
Lai izvairītos no atmiņas noplūdēm, pārliecinieties, vai esat pārtraucis visas nevajadzīgās atsauces uz mainīgajiem closure iekšienē, kad tie vairs nav vajadzīgi. To var izdarīt, iestatot mainīgos uz null
vai pārstrukturējot kodu, lai izvairītos no nevajadzīgu closure izveides.
Biežākās closure kļūdas, no kurām jāizvairās
- Aizmirstot leksisko darbības jomu: Vienmēr atcerieties, ka closure uztver vidi *tās izveides brīdī*. Ja mainīgie mainās pēc closure izveides, closure atspoguļos šīs izmaiņas.
- Izveidojot nevajadzīgus closure: Izvairieties no closure izveides, ja tie nav vajadzīgi, jo tie var ietekmēt veiktspēju un atmiņas patēriņu.
- Nopludinot mainīgos: Esiet uzmanīgi ar closure uztverto mainīgo darbības laiku un pārliecinieties, vai tie tiek atbrīvoti, kad tie vairs nav vajadzīgi, lai novērstu atmiņas noplūdes.
Secinājums
JavaScript closure ir jaudīgs un būtisks jēdziens, kas jāizprot ikvienam JavaScript izstrādātājam. Tie nodrošina datu iekapsulēšanu, stāvokļa saglabāšanu, augstākas kārtas funkcijas un asinhrono programmēšanu. Izprotot, kā closure darbojas un kā tos efektīvi izmantot, jūs varat rakstīt efektīvāku, uzturējamāku un drošāku kodu.
Šī rokasgrāmata ir sniegusi visaptverošu pārskatu par closure ar praktiskiem piemēriem. Praktizējot un eksperimentējot ar šiem piemēriem, jūs varat padziļināt savu izpratni par closure un kļūt par prasmīgāku JavaScript izstrādātāju.
Papildu mācības
- Mozilla Developer Network (MDN): Closure - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
- You Don't Know JS: Scope & Closures, autors Kyle Simpson
- Izpētiet tiešsaistes kodēšanas platformas, piemēram, CodePen un JSFiddle, lai eksperimentētu ar dažādiem closure piemēriem.