Uurige JavaScripti sulundeid praktiliste näidete kaudu, mõistes nende toimimist ja reaalseid rakendusi tarkvaraarenduses.
JavaScripti sulundid: Demüstifitseerimine praktiliste näidetega
Sulundid (closures) on JavaScripti põhimõtteline kontseptsioon, mis tekitab sageli segadust igas tasemes arendajates. Sulundite mõistmine on ülioluline tõhusa, hooldatava ja turvalise koodi kirjutamiseks. See põhjalik juhend demüstifitseerib sulundid praktiliste näidete abil ja demonstreerib nende reaalseid rakendusi.
Mis on sulund?
Lihtsamalt öeldes on sulund funktsiooni ja leksikaalse keskkonna kombinatsioon, milles see funktsioon deklareeriti. See tähendab, et sulund võimaldab funktsioonil pääseda juurde muutujatele oma ümbritsevast skoobist, isegi pärast seda, kui väline funktsioon on oma töö lõpetanud. Mõelge sellest kui sisemisest funktsioonist, mis "mäletab" oma keskkonda.
Selle tõeliseks mõistmiseks jaotame lahti põhikomponendid:
- Funktsioon: Sisemine funktsioon, mis on osa sulundist.
- Leksikaalne keskkond: Ümbritsev skoop, kus funktsioon deklareeriti. See hõlmab muutujaid, funktsioone ja muid deklaratsioone.
Maagia toimub seetõttu, et sisemine funktsioon säilitab juurdepääsu oma leksikaalse skoobi muutujatele isegi pärast seda, kui välimine funktsioon on tagastatud. See käitumine on JavaScripti skoobi ja mälu haldamise põhiosa.
Miks on sulundid olulised?
Sulundid ei ole pelgalt teoreetiline kontseptsioon; need on hädavajalikud paljude levinud programmeerimismustrite jaoks JavaScriptis. Need pakuvad järgmisi eeliseid:
- Andmete kapseldamine: Sulundid võimaldavad teil luua privaatseid muutujaid ja meetodeid, kaitstes andmeid välise juurdepääsu ja muutmise eest.
- Olekusäilitus: Sulundid säilitavad muutujate oleku funktsioonikõnede vahel, mis on kasulik loendurite, taimerite ja muude olekupõhiste komponentide loomiseks.
- Kõrgemat järku funktsioonid: Sulundeid kasutatakse sageli koos kõrgemat järku funktsioonidega (funktsioonid, mis võtavad argumentidena teisi funktsioone või tagastavad funktsioone), võimaldades võimsa ja paindliku koodi loomist.
- Asünkroonne JavaScript: Sulunditel on kriitiline roll asünkroonsete operatsioonide, näiteks tagasikutsete (callbacks) ja lubaduste (promises) haldamisel.
JavaScripti sulundite praktilised näited
Sukeldume mõnedesse praktilistesse näidetesse, et illustreerida, kuidas sulundid töötavad ja kuidas neid saab kasutada reaalsetes stsenaariumides.
Näide 1: Lihtne loendur
See näide demonstreerib, kuidas sulundit saab kasutada loenduri loomiseks, mis säilitab oma oleku funktsioonikõnede vahel.
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const increment = createCounter();
increment(); // Väljund: 1
increment(); // Väljund: 2
increment(); // Väljund: 3
Selgitus:
createCounter()
on välimine funktsioon, mis deklareerib muutujacount
.- See tagastab sisemise funktsiooni (antud juhul anonüümse funktsiooni), mis suurendab
count
väärtust ja logib selle konsooli. - Sisemine funktsioon moodustab sulundi muutuja
count
üle. - Isegi pärast seda, kui
createCounter()
on oma töö lõpetanud, säilitab sisemine funktsioon juurdepääsu muutujalecount
. - Iga
increment()
kutse suurendab samacount
muutujat, demonstreerides sulundi võimet olekut säilitada.
Näide 2: Andmete kapseldamine privaatsete muutujatega
Sulundeid saab kasutada privaatsete muutujate loomiseks, kaitstes andmeid otsejuurdepääsu ja muutmise eest väljaspool funktsiooni.
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: function(amount) {
balance += amount;
return balance; //Tagastatakse demonstreerimiseks, võiks olla ka void
},
withdraw: function(amount) {
if (amount <= balance) {
balance -= amount;
return balance; //Tagastatakse demonstreerimiseks, võiks olla ka void
} else {
return "Insufficient funds.";
}
},
getBalance: function() {
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.deposit(500)); // Väljund: 1500
console.log(account.withdraw(200)); // Väljund: 1300
console.log(account.getBalance()); // Väljund: 1300
// Otse saldole ligipääsemine ei toimi
// console.log(account.balance); // Väljund: undefined
Selgitus:
createBankAccount()
loob pangakonto objekti meetoditega raha sissekandmiseks, väljavõtmiseks ja saldo saamiseks.- Muutuja
balance
on deklareeritudcreateBankAccount()
skoobis ja pole väljastpoolt otse ligipääsetav. - Meetodid
deposit
,withdraw
jagetBalance
moodustavad sulundid muutujabalance
üle. - Need meetodid saavad muutujat
balance
lugeda ja muuta, kuid muutuja ise jääb privaatseks.
Näide 3: Sulundite kasutamine setTimeout
'iga tsüklis
Sulundid on hädavajalikud asünkroonsete operatsioonidega töötamisel, nagu setTimeout
, eriti tsüklites. Ilma sulunditeta võite kohata ootamatut käitumist JavaScripti asünkroonse olemuse tõttu.
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log("Value of i: " + j);
}, j * 1000);
})(i);
}
// Väljund:
// Value of i: 1 (pärast 1 sekundit)
// Value of i: 2 (pärast 2 sekundit)
// Value of i: 3 (pärast 3 sekundit)
// Value of i: 4 (pärast 4 sekundit)
// Value of i: 5 (pärast 5 sekundit)
Selgitus:
- Ilma sulundita (kohe väljakutsutav funktsiooniavaldis ehk IIFE) viitaksid kõik
setTimeout
tagasikutsed lõpuks samalei
muutujale, mille lõppväärtus oleks pärast tsükli lõppu 6. - IIFE loob iga tsükli iteratsiooni jaoks uue skoobi, püüdes kinni
i
hetkeväärtuse parameetrisj
. - Iga
setTimeout
tagasikutse moodustab sulundi muutujaj
üle, tagades, et see logib iga iteratsiooni jaoks õigei
väärtuse.
Kasutades tsüklis let
märksõna var
asemel, lahendaks see samuti probleemi, kuna let
loob iga iteratsiooni jaoks plokiskoobi.
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log("Value of i: " + i);
}, i * 1000);
}
// Väljund (sama mis ülal):
// Value of i: 1 (pärast 1 sekundit)
// Value of i: 2 (pärast 2 sekundit)
// Value of i: 3 (pärast 3 sekundit)
// Value of i: 4 (pärast 4 sekundit)
// Value of i: 5 (pärast 5 sekundit)
Näide 4: Karritamine (Currying) ja osaline rakendamine (Partial Application)
Sulundid on karritamise ja osalise rakendamise aluseks. Need on tehnikad, mida kasutatakse mitme argumendiga funktsioonide muutmiseks funktsioonide jadaks, millest igaüks võtab ühe argumendi.
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)); // Väljund: 30 (5 * 2 * 3)
Selgitus:
multiply
on karritatud funktsioon, mis võtab kolm argumenti, ühe korraga.- Iga sisemine funktsioon moodustab sulundi oma välimise skoobi muutujate (
a
,b
) üle. multiplyBy5
on funktsioon, kusa
väärtuseks on juba seatud 5.multiplyBy5And2
on funktsioon, kusa
on 5 jab
on 2.- Viimane kutse
multiplyBy5And2(3)
lõpetab arvutuse ja tagastab tulemuse.
Näide 5: Moodulimuster
Sulundeid kasutatakse laialdaselt moodulimustris, mis aitab organiseerida ja struktureerida JavaScripti koodi, edendades modulaarsust ja vältides nimekonflikte.
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); // Väljund: This is a public property.
myModule.publicMethod(); // Väljund: Hello, world!
// Otse privateVariable või privateMethod ligipääsemine ei toimi
// console.log(myModule.privateVariable); // Väljund: undefined
// myModule.privateMethod(); // Väljund: TypeError: myModule.privateMethod is not a function
Selgitus:
- IIFE loob uue skoobi, kapseldades
privateVariable
japrivateMethod
. - Tagastatud objekt paljastab ainult
publicMethod
japublicProperty
. publicMethod
moodustab sulundiprivateMethod
japrivateVariable
üle, võimaldades neile juurdepääsu ka pärast IIFE täitmist.- See muster loob tõhusalt mooduli privaatsete ja avalike liikmetega.
Sulundid ja mäluhaldus
Kuigi sulundid on võimsad, on oluline olla teadlik nende potentsiaalsest mõjust mäluhaldusele. Kuna sulundid säilitavad juurdepääsu oma ümbritseva skoobi muutujatele, võivad nad takistada nende muutujate prügikoristust, kui neid enam ei vajata. See võib hooletu käsitsemise korral põhjustada mälulekkeid.
Mälulekete vältimiseks veenduge, et katkestate kõik mittevajalikud viited muutujatele sulundites, kui neid enam ei vajata. Seda saab teha, seades muutujate väärtuseks null
või restruktureerides oma koodi, et vältida mittevajalike sulundite loomist.
Levinud vead sulunditega, mida vältida
- Leksikaalse skoobi unustamine: Pidage alati meeles, et sulund hõlmab keskkonda *selle loomise hetkel*. Kui muutujad muutuvad pärast sulundi loomist, peegeldab sulund neid muudatusi.
- Mittevajalike sulundite loomine: Vältige sulundite loomist, kui neid pole vaja, kuna need võivad mõjutada jõudlust ja mälukasutust.
- Muutujate lekitamine: Olge teadlik sulundite poolt hõivatud muutujate elueast ja veenduge, et need vabastatakse, kui neid enam ei vajata, et vältida mälulekkeid.
Kokkuvõte
JavaScripti sulundid on võimas ja hädavajalik kontseptsioon, mida iga JavaScripti arendaja peab mõistma. Need võimaldavad andmete kapseldamist, oleku säilitamist, kõrgemat järku funktsioone ja asünkroonset programmeerimist. Mõistes, kuidas sulundid töötavad ja kuidas neid tõhusalt kasutada, saate kirjutada tõhusamat, hooldatavamat ja turvalisemat koodi.
See juhend on andnud põhjaliku ülevaate sulunditest koos praktiliste näidetega. Nende näidetega harjutades ja katsetades saate süvendada oma arusaama sulunditest ja saada osavamaks JavaScripti arendajaks.
Lisalugemist
- Mozilla Developer Network (MDN): Sulundid - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
- You Don't Know JS: Scope & Closures, autor Kyle Simpson
- Avastage veebipõhiseid kodeerimisplatvorme nagu CodePen ja JSFiddle, et katsetada erinevate sulundite näidetega.