జావాస్క్రిప్ట్ ఇటరేటర్ ప్రోటోకాల్పై పట్టు సాధించండి. ఏ ఆబ్జెక్ట్నైనా ఇటరేబుల్గా మార్చడం, `for...of` లూప్లను నియంత్రించడం, మరియు వాస్తవ ఉదాహరణలతో కస్టమ్ ఇటరేషన్ లాజిక్ను అమలు చేయడం నేర్చుకోండి.
జావాస్క్రిప్ట్లో కస్టమ్ ఇటరేషన్ను అన్లాక్ చేయడం: ఇటరేటర్ ప్రోటోకాల్పై ఒక లోతైన విశ్లేషణ
ప్రోగ్రామింగ్లో ఇటరేషన్ అత్యంత ప్రాథమిక భావనలలో ఒకటి. జాబితా ఐటమ్లను ప్రాసెస్ చేయడం నుండి డేటా స్ట్రీమ్లను చదవడం వరకు, మనం నిరంతరం సమాచార శ్రేణులతో పని చేస్తూ ఉంటాము. జావాస్క్రిప్ట్లో, మనకు for...of లూప్ మరియు స్ప్రెడ్ సింటాక్స్ (...) వంటి శక్తివంతమైన మరియు సొగసైన టూల్స్ ఉన్నాయి, ఇవి Arrays, Strings, మరియు Maps వంటి అంతర్నిర్మిత రకాలపై ఇటరేట్ చేయడాన్ని సులభతరం చేస్తాయి.
అయితే మీరు ఎప్పుడైనా ఆగి, ఈ ఆబ్జెక్ట్లను అంత ప్రత్యేకంగా మార్చేది ఏమిటని ఆలోచించారా? మీరు for (const char of "hello") అని ఎందుకు వ్రాయగలరు కానీ for (const prop of {a: 1, b: 2}) అని ఎందుకు వ్రాయలేరు? దీనికి సమాధానం ECMAScript స్టాండర్డ్ యొక్క శక్తివంతమైన, కానీ తరచుగా తప్పుగా అర్థం చేసుకోబడిన ఫీచర్లో ఉంది: ఇటరేటర్ ప్రోటోకాల్.
ఈ ప్రోటోకాల్ కేవలం జావాస్క్రిప్ట్ యొక్క అంతర్నిర్మిత ఆబ్జెక్ట్ల కోసం ఒక అంతర్గత యంత్రాంగం మాత్రమే కాదు. ఇది ఒక ఓపెన్ స్టాండర్డ్, ఏ ఆబ్జెక్ట్ అయినా స్వీకరించగల ఒక ఒప్పందం. ఈ ప్రోటోకాల్ను అమలు చేయడం ద్వారా, మీరు మీ స్వంత కస్టమ్ ఆబ్జెక్ట్లపై ఎలా ఇటరేట్ చేయాలో జావాస్క్రిప్ట్కు నేర్పించవచ్చు, వాటిని భాషలో ఫస్ట్-క్లాస్ పౌరులుగా మార్చవచ్చు. మీ కస్టమ్ డేటా స్ట్రక్చర్ల కోసం, అది బైనరీ ట్రీ అయినా, లింక్డ్ లిస్ట్ అయినా, గేమ్ యొక్క టర్న్ సీక్వెన్స్ అయినా, లేదా ఈవెంట్ల టైమ్లైన్ అయినా, మీరు for...of యొక్క అదే వాక్యనిర్మాణ సొగసును అన్లాక్ చేయవచ్చు.
ఈ సమగ్ర గైడ్లో, మనం ఇటరేటర్ ప్రోటోకాల్ను స్పష్టంగా వివరిస్తాము. దాని ప్రధాన భాగాలను విడదీసి, మొదటి నుండి కస్టమ్ ఇటరేటర్లను నిర్మించడం ద్వారా, అనంతమైన శ్రేణుల వంటి అధునాతన వినియోగ సందర్భాలను అన్వేషించి, చివరగా, జెనరేటర్ ఫంక్షన్లను ఉపయోగించి ఆధునిక, సరళీకృత విధానాన్ని కనుగొంటాము. చివరికి, మీరు ఇటరేషన్ ఎలా పనిచేస్తుందో లోతుగా అర్థం చేసుకోవడమే కాకుండా, మరింత వ్యక్తీకరణ, పునర్వినియోగ, మరియు ఇడియోమాటిక్ జావాస్క్రిప్ట్ కోడ్ వ్రాయడానికి కూడా అధికారం పొందుతారు.
ఇటరేషన్ యొక్క మూలం: జావాస్క్రిప్ట్ ఇటరేటర్ ప్రోటోకాల్ అంటే ఏమిటి?
మొదటగా, "ఇటరేటర్ ప్రోటోకాల్" అనేది మీరు ఎక్స్టెండ్ చేసే ఒకే క్లాస్ లేదా మీరు కాల్ చేసే ఒక నిర్దిష్ట ఫంక్షన్ కాదని అర్థం చేసుకోవడం ముఖ్యం. ఇది ఒక ఆబ్జెక్ట్ "ఇటరేబుల్"గా పరిగణించబడటానికి మరియు "ఇటరేటర్"ను ఉత్పత్తి చేయడానికి అనుసరించాల్సిన నియమాలు లేదా సంప్రదాయాల సమితి. దీన్ని ఒక ఒప్పందం (contract)గా భావించడం ఉత్తమం. మీ ఆబ్జెక్ట్ ఈ ఒప్పందంపై సంతకం చేస్తే, జావాస్క్రిప్ట్ ఇంజిన్ దానిపై ఎలా లూప్ చేయాలో తెలుసుకుంటుందని వాగ్దానం చేస్తుంది.
ఈ ఒప్పందం రెండు విభిన్న భాగాలుగా విభజించబడింది:
- ది ఇటరేబుల్ ప్రోటోకాల్: ఇది ఒక ఆబ్జెక్ట్ మొదటి స్థానంలో ఇటరేబుల్ కాదా అని నిర్ధారిస్తుంది.
- ది ఇటరేటర్ ప్రోటోకాల్: ఇది ఆబ్జెక్ట్పై ఒక్కో విలువ చొప్పున ఎలా ఇటరేట్ చేయబడుతుందో నిర్వచిస్తుంది.
ఈ ఒప్పందం యొక్క ప్రతి భాగాన్ని వివరంగా పరిశీలిద్దాం.
ఒప్పందంలో మొదటి సగం: ఇటరేబుల్ ప్రోటోకాల్
ఇటరేబుల్ ప్రోటోకాల్ ఆశ్చర్యకరంగా సులభం. దీనికి కేవలం ఒకే ఒక్క ఆవశ్యకత ఉంది:
ఒక ఆబ్జెక్ట్ ఇటరేబుల్గా పరిగణించబడుతుంది, దానికి ఒక ఇటరేటర్ను తిరిగి పొందే పద్ధతిని అందించే ఒక నిర్దిష్ట, సుప్రసిద్ధ ప్రాపర్టీ ఉంటే. ఈ సుప్రసిద్ధ ప్రాపర్టీని Symbol.iterator ఉపయోగించి యాక్సెస్ చేయవచ్చు.
కాబట్టి, ఒక ఆబ్జెక్ట్ ఇటరేబుల్ కావాలంటే, దానికి [Symbol.iterator] కీ ద్వారా యాక్సెస్ చేయగల ఒక పద్ధతి ఉండాలి. ఈ పద్ధతిని కాల్ చేసినప్పుడు, అది ఒక ఇటరేటర్ ఆబ్జెక్ట్ను తిరిగి ఇవ్వాలి (దీని గురించి మనం తదుపరి విభాగంలో చర్చిస్తాము).
మీరు అడగవచ్చు, "Symbol అంటే ఏమిటి, మరియు 'iterator' వంటి స్ట్రింగ్ పేరును ఎందుకు ఉపయోగించకూడదు?" Symbol అనేది ES6లో పరిచయం చేయబడిన ఒక ప్రత్యేకమైన మరియు మార్పులేని ప్రిమిటివ్ డేటా రకం. దీని ప్రాథమిక ఉద్దేశ్యం ఆబ్జెక్ట్ ప్రాపర్టీల కోసం ఒక ప్రత్యేకమైన కీగా పనిచేయడం, ప్రమాదవశాత్తూ పేర్ల ఘర్షణలను నివారించడం. ప్రోటోకాల్ 'iterator' వంటి సాధారణ స్ట్రింగ్ను ఉపయోగిస్తే, మీ స్వంత కోడ్ వేరే ప్రయోజనం కోసం అదే పేరుతో ఒక ప్రాపర్టీని నిర్వచించవచ్చు, ఇది అనూహ్యమైన బగ్లకు దారితీస్తుంది. Symbol.iterator ఉపయోగించడం ద్వారా, భాషా స్పెసిఫికేషన్ ఇతర కోడ్తో ఘర్షణ పడని ఒక ప్రత్యేకమైన, ప్రామాణిక కీని హామీ ఇస్తుంది.
మనం అంతర్నిర్మిత ఇటరేబుల్స్పై దీన్ని సులభంగా ధృవీకరించవచ్చు:
const anArray = [1, 2, 3];
const aString = "global";
const aMap = new Map();
console.log(typeof anArray[Symbol.iterator]); // "function"
console.log(typeof aString[Symbol.iterator]); // "function"
console.log(typeof aMap[Symbol.iterator]); // "function"
// A plain object is not iterable by default
const anObject = { a: 1, b: 2 };
console.log(typeof anObject[Symbol.iterator]); // "undefined"
ఒప్పందంలో రెండవ సగం: ఇటరేటర్ ప్రోటోకాల్
ఒక ఆబ్జెక్ట్ [Symbol.iterator]() పద్ధతిని అందించడం ద్వారా ఇటరేబుల్ అని నిరూపించుకున్న తర్వాత, ఆ పద్ధతి తిరిగి ఇచ్చే ఆబ్జెక్ట్పై దృష్టి మారుతుంది: ఇటరేటర్. ఇటరేటర్ అసలు పని చేసేది; ఇది ఇటరేషన్ ప్రక్రియను వాస్తవంగా నిర్వహిస్తుంది మరియు విలువల శ్రేణిని ఉత్పత్తి చేస్తుంది.
ఇటరేటర్ ప్రోటోకాల్ కూడా చాలా సూటిగా ఉంటుంది. దీనికి ఒకే ఒక ఆవశ్యకత ఉంది:
ఒక ఆబ్జెక్ట్ ఇటరేటర్ అవుతుంది, దానికి next() అనే పద్ధతి ఉంటే. ఈ next() పద్ధతిని కాల్ చేసినప్పుడు, అది రెండు నిర్దిష్ట ప్రాపర్టీలతో ఒక ఆబ్జెక్ట్ను తిరిగి ఇవ్వాలి:
done(బూలియన్): ఈ ప్రాపర్టీ ఇటరేషన్ యొక్క స్థితిని సూచిస్తుంది. శ్రేణిలో ఇంకా విలువలు రాబోతున్నట్లయితే ఇదిfalseగా ఉంటుంది. ఇటరేషన్ పూర్తయిన తర్వాత ఇదిtrueఅవుతుంది.value(ఏదైనా రకం): ఈ ప్రాపర్టీ శ్రేణిలోని ప్రస్తుత విలువను కలిగి ఉంటుంది.donetrueఅయినప్పుడు,valueప్రాపర్టీ ఐచ్ఛికం మరియు సాధారణంగాundefinedను కలిగి ఉంటుంది.
ఏ ఇటరేబుల్ ఆబ్జెక్ట్తోనూ సంబంధం లేకుండా, చేతితో సృష్టించిన ఒక స్టాండలోన్ ఇటరేటర్ను చూద్దాం. ఈ ఇటరేటర్ కేవలం 1 నుండి 3 వరకు లెక్కిస్తుంది.
const manualCounterIterator = {
count: 1,
next: function() {
if (this.count <= 3) {
return { value: this.count++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
// We call next() repeatedly to get each value
console.log(manualCounterIterator.next()); // { value: 1, done: false }
console.log(manualCounterIterator.next()); // { value: 2, done: false }
console.log(manualCounterIterator.next()); // { value: 3, done: false }
console.log(manualCounterIterator.next()); // { value: undefined, done: true }
console.log(manualCounterIterator.next()); // { value: undefined, done: true } - It stays done
ప్రతి for...of లూప్ను నడిపించే ప్రాథమిక యంత్రాంగం ఇదే. మీరు for (const item of iterable) అని వ్రాసినప్పుడు, జావాస్క్రిప్ట్ ఇంజిన్ తెరవెనుక ఈ క్రింది వాటిని చేస్తుంది:
- ఇది
iterableఆబ్జెక్ట్పై[Symbol.iterator]()పద్ధతిని కాల్ చేసి ఒక ఇటరేటర్ను పొందుతుంది. - ఆ తర్వాత అది ఆ ఇటరేటర్పై
next()పద్ధతిని పదేపదే కాల్ చేస్తుంది. donefalseగా ఉన్న ప్రతి తిరిగి వచ్చిన ఆబ్జెక్ట్ కోసం, అదిvalueను మీ లూప్ వేరియబుల్కు (item) కేటాయించి, లూప్ బాడీని అమలు చేస్తుంది.next()donetrueగా ఉన్న ఒక ఆబ్జెక్ట్ను తిరిగి ఇచ్చినప్పుడు, లూప్ ముగుస్తుంది.
మొదటి నుండి నిర్మించడం: కస్టమ్ ఇటరేషన్కు ఒక ప్రాక్టికల్ గైడ్
ఇప్పుడు మనం సిద్ధాంతాన్ని అర్థం చేసుకున్నాము, దాన్ని ఆచరణలో పెడదాం. మనం Timeline అనే ఒక కస్టమ్ క్లాస్ను సృష్టిస్తాము. ఈ క్లాస్ చారిత్రక సంఘటనల సేకరణను నిర్వహిస్తుంది, మరియు మన లక్ష్యం దాన్ని నేరుగా ఇటరేబుల్గా మార్చడం, సంఘటనలను కాలక్రమానుసారం లూప్ చేయడానికి వీలు కల్పించడం.
వినియోగ సందర్భం: ఒక `Timeline` క్లాస్
మన Timeline క్లాస్ ఈవెంట్లను నిల్వ చేస్తుంది, ప్రతి ఈవెంట్ year మరియు descriptionతో ఒక ఆబ్జెక్ట్గా ఉంటుంది. ఈ ఈవెంట్లను సంవత్సరం వారీగా క్రమబద్ధీకరించి, వాటిపై for...of లూప్ ఉపయోగించగలగాలి అని మనం కోరుకుంటున్నాము.
class Timeline {
constructor() {
this.events = [];
}
addEvent(year, description) {
this.events.push({ year, description });
}
}
const myTimeline = new Timeline();
myTimeline.addEvent(1995, "JavaScript is created");
myTimeline.addEvent(2009, "Node.js is introduced");
myTimeline.addEvent(1997, "ECMAScript standard is first published");
myTimeline.addEvent(2015, "ES6 (ECMAScript 2015) is released");
// Goal: Make the following code work
// for (const event of myTimeline) {
// console.log(`${event.year}: ${event.description}`);
// }
దశల వారీగా అమలు
మన లక్ష్యాన్ని సాధించడానికి, మనం ఇటరేటర్ ప్రోటోకాల్ను అమలు చేయాలి. అంటే మన Timeline క్లాస్కు [Symbol.iterator]() పద్ధతిని జోడించడం.
ఈ పద్ధతి ఒక కొత్త ఆబ్జెక్ట్ను తిరిగి ఇవ్వాలి—ఇటరేటర్ను—ఇది next() పద్ధతిని కలిగి ఉంటుంది మరియు ఇటరేషన్ యొక్క స్థితిని (ఉదా., మనం ప్రస్తుతం ఏ ఈవెంట్పై ఉన్నాము) నిర్వహిస్తుంది. ఇటరేషన్ స్థితి ఇటరేబుల్ ఆబ్జెక్ట్పై కాకుండా, ఇటరేటర్పై ఉండటం ఒక కీలకమైన డిజైన్ సూత్రం. ఇది ఒకే టైమ్లైన్పై ఏకకాలంలో బహుళ, స్వతంత్ర ఇటరేషన్లను అనుమతిస్తుంది.
class Timeline {
constructor() {
this.events = [];
}
addEvent(year, description) {
// We'll add a simple check to ensure data integrity
if (typeof year !== 'number' || typeof description !== 'string') {
throw new Error("Invalid event data");
}
this.events.push({ year, description });
}
// Step 1: Implement the Iterable Protocol
[Symbol.iterator]() {
// Sort the events chronologically for iteration.
// We create a copy to not mutate the original array's order.
const sortedEvents = [...this.events].sort((a, b) => a.year - b.year);
let currentIndex = 0;
// Step 2: Return the iterator object
return {
// Step 3: Implement the Iterator Protocol with the next() method
next: () => { // Using an arrow function to capture `sortedEvents` and `currentIndex`
if (currentIndex < sortedEvents.length) {
// There are more events to iterate over
const currentEvent = sortedEvents[currentIndex];
currentIndex++;
return { value: currentEvent, done: false };
} else {
// We have reached the end of the events
return { value: undefined, done: true };
}
}
};
}
}
మ్యాజిక్ను చూడటం: మన కస్టమ్ ఇటరేబుల్ను ఉపయోగించడం
ప్రోటోకాల్ను సరిగ్గా అమలు చేయడంతో, మన Timeline ఆబ్జెక్ట్ ఇప్పుడు పూర్తిస్థాయి ఇటరేబుల్గా మారింది. ఇది జావాస్క్రిప్ట్ యొక్క ఇటరేషన్-ఆధారిత భాషా ఫీచర్లతో సజావుగా కలిసిపోతుంది. దాన్ని ఆచరణలో చూద్దాం.
const myTimeline = new Timeline();
myTimeline.addEvent(1995, "JavaScript is created");
myTimeline.addEvent(2009, "Node.js is introduced");
myTimeline.addEvent(1997, "ECMAScript standard is first published");
myTimeline.addEvent(2015, "ES6 (ECMAScript 2015) is released");
console.log("--- Using for...of loop ---");
for (const event of myTimeline) {
console.log(`${event.year}: ${event.description}`);
}
// Output:
// 1995: JavaScript is created
// 1997: ECMAScript standard is first published
// 2009: Node.js is introduced
// 2015: ES6 (ECMAScript 2015) is released
console.log("\n--- Using spread syntax ---");
const eventsArray = [...myTimeline];
console.log(eventsArray);
// Output: An array of the event objects, sorted by year
console.log("\n--- Using Array.from() ---");
const eventsFrom = Array.from(myTimeline);
console.log(eventsFrom);
// Output: An array of the event objects, sorted by year
console.log("\n--- Using destructuring assignment ---");
const [firstEvent, secondEvent] = myTimeline;
console.log(firstEvent);
// Output: { year: 1995, description: 'JavaScript is created' }
console.log(secondEvent);
// Output: { year: 1997, description: 'ECMAScript standard is first published' }
ఇదే ప్రోటోకాల్ యొక్క నిజమైన శక్తి. ఒక ప్రామాణిక ఒప్పందానికి కట్టుబడి ఉండటం ద్వారా, మనం మన కస్టమ్ ఆబ్జెక్ట్ను అదనపు పని లేకుండా ఇప్పటికే ఉన్న మరియు భవిష్యత్ జావాస్క్రిప్ట్ ఫీచర్ల యొక్క విస్తృత శ్రేణితో అనుకూలంగా మార్చాము.
మీ ఇటరేషన్ నైపుణ్యాలను మెరుగుపరచుకోవడం
ఇప్పుడు మీరు ప్రాథమికాలను నేర్చుకున్నారు, మీకు మరింత నియంత్రణ మరియు సౌలభ్యాన్ని ఇచ్చే కొన్ని అధునాతన భావనలను అన్వేషిద్దాం.
స్థితి మరియు స్వతంత్ర ఇటరేటర్ల ప్రాముఖ్యత
మన Timeline ఉదాహరణలో, ఇటరేషన్ యొక్క స్థితిని (currentIndex మరియు sortedEvents కాపీ) [Symbol.iterator]() ద్వారా తిరిగి ఇవ్వబడిన ఇటరేటర్ ఆబ్జెక్ట్లో ఉంచడంలో చాలా జాగ్రత్తగా ఉన్నాము. ఇది ఎందుకు అంత ముఖ్యం? ఎందుకంటే మనం ప్రతిసారీ ఒక ఇటరేషన్ను ప్రారంభించినప్పుడు, మనకు ఒక *కొత్త, స్వతంత్ర ఇటరేటర్* లభిస్తుందని ఇది నిర్ధారిస్తుంది.
ఇది బహుళ వినియోగదారులు ఒకరికొకరు అంతరాయం కలిగించకుండా ఒకే ఇటరేబుల్ ఆబ్జెక్ట్పై ఇటరేట్ చేయడానికి అనుమతిస్తుంది. ఒకవేళ currentIndex Timeline ఉదాహరణ యొక్క ప్రాపర్టీ అయితే ఊహించండి—అది గందరగోళంగా ఉంటుంది!
const sharedTimeline = new Timeline();
sharedTimeline.addEvent(1, 'Event A');
sharedTimeline.addEvent(2, 'Event B');
sharedTimeline.addEvent(3, 'Event C');
const iterator1 = sharedTimeline[Symbol.iterator]();
const iterator2 = sharedTimeline[Symbol.iterator]();
console.log(iterator1.next().value); // { year: 1, description: 'Event A' }
console.log(iterator2.next().value); // { year: 1, description: 'Event A' } (Starts its own iteration)
console.log(iterator1.next().value); // { year: 2, description: 'Event B' } (Unaffected by iterator2)
అనంతంగా వెళ్లడం: అంతులేని శ్రేణులను సృష్టించడం
ఇటరేటర్ ప్రోటోకాల్ ఒక ఇటరేషన్ ఎప్పుడూ ముగియాలని కోరదు. done ప్రాపర్టీ కేవలం ఎప్పటికీ falseగానే ఉండవచ్చు. ఇది మనకు అనంతమైన శ్రేణులను మోడల్ చేయడానికి అనుమతిస్తుంది, ఇది ప్రత్యేకమైన IDలను ఉత్పత్తి చేయడం, యాదృచ్ఛిక డేటా స్ట్రీమ్లను సృష్టించడం లేదా గణిత శ్రేణులను మోడల్ చేయడం వంటి పనులకు చాలా ఉపయోగకరంగా ఉంటుంది.
ఫిబొనాక్సీ శ్రేణిని నిరవధికంగా ఉత్పత్తి చేసే ఒక ఇటరేటర్ను సృష్టిద్దాం.
const fibonacciSequence = {
[Symbol.iterator]() {
let a = 0, b = 1;
return {
next() {
[a, b] = [b, a + b];
return { value: a, done: false };
}
};
}
};
// We can't use spread syntax or Array.from() here, as that would create an infinite loop and crash!
// const fibArray = [...fibonacciSequence]; // DANGER: Infinite loop!
// We must consume it carefully, providing our own termination condition.
console.log("First 10 Fibonacci numbers:");
let count = 0;
for (const number of fibonacciSequence) {
console.log(number);
count++;
if (count >= 10) {
break; // It's crucial to break out of the loop!
}
}
ఐచ్ఛిక ఇటరేటర్ పద్ధతులు: `return()`
మరింత అధునాతన దృశ్యాల కోసం, ముఖ్యంగా వనరుల నిర్వహణ (ఫైల్ హ్యాండిల్స్ లేదా నెట్వర్క్ కనెక్షన్ల వంటివి) ఉన్న వాటిలో, ఒక ఇటరేటర్కు ఐచ్ఛికంగా return() పద్ధతి ఉండవచ్చు. ఇటరేషన్ అకాలంగా ఆగిపోయినప్పుడు జావాస్క్రిప్ట్ ఇంజిన్ ఈ పద్ధతిని స్వయంచాలకంగా కాల్ చేస్తుంది. ఇది ఒక break, return, throw స్టేట్మెంట్ ఒక for...of లూప్ను పూర్తికాకముందే నిష్క్రమింపజేసినప్పుడు జరగవచ్చు.
ఇది మీ ఇటరేటర్కు శుభ్రపరిచే పనులను చేయడానికి ఒక అవకాశం ఇస్తుంది.
function createResourceIterator() {
let resourceIsOpen = true;
console.log("Resource opened.");
let i = 0;
return {
next() {
if (i < 3) {
return { value: ++i, done: false };
} else {
console.log("Iterator finished naturally.");
resourceIsOpen = false;
console.log("Resource closed.");
return { done: true };
}
},
return() {
if (resourceIsOpen) {
console.log("Iterator terminated early. Closing resource.");
resourceIsOpen = false;
}
return { done: true }; // Must return a valid iterator result
}
};
}
console.log("--- Early exit scenario ---");
const resourceIterable = { [Symbol.iterator]: createResourceIterator };
for (const value of resourceIterable) {
console.log(`Processing value: ${value}`);
if (value > 1) {
break; // This will trigger the return() method
}
}
గమనిక: ఎర్రర్ ప్రచారం కోసం ఒక throw() పద్ధతి కూడా ఉంది, కానీ ఇది ప్రధానంగా జెనరేటర్ ఫంక్షన్ల సందర్భంలో ఉపయోగించబడుతుంది, దాని గురించి మనం తరువాత చర్చిస్తాము.
ఆధునిక విధానం: జెనరేటర్ ఫంక్షన్లతో సరళీకరించడం
మనం చూసినట్లుగా, ఇటరేటర్ ప్రోటోకాల్ను మాన్యువల్గా అమలు చేయడానికి ఇటరేటర్ ఆబ్జెక్ట్ను సృష్టించడానికి మరియు { value, done } ఆబ్జెక్ట్లను తిరిగి ఇవ్వడానికి జాగ్రత్తగా స్టేట్ మేనేజ్మెంట్ మరియు బాయిలర్ప్లేట్ కోడ్ అవసరం. ఈ ప్రక్రియను అర్థం చేసుకోవడం అవసరం అయినప్పటికీ, ES6 ఒక మరింత సొగసైన పరిష్కారాన్ని పరిచయం చేసింది: జెనరేటర్ ఫంక్షన్లు.
ఒక జెనరేటర్ ఫంక్షన్ అనేది పాజ్ చేసి, తిరిగి కొనసాగించగల ఒక ప్రత్యేక రకమైన ఫంక్షన్, ఇది కాలక్రమేణా విలువల శ్రేణిని ఉత్పత్తి చేయడానికి అనుమతిస్తుంది. ఇది ఇటరేటర్ల సృష్టిని చాలా సులభతరం చేస్తుంది.
ముఖ్య వాక్యనిర్మాణం:
function*: నక్షత్రం గుర్తు ఒక ఫంక్షన్ను జెనరేటర్గా ప్రకటిస్తుంది.yield: ఈ కీవర్డ్ జెనరేటర్ యొక్క అమలును పాజ్ చేసి, ఒక విలువను "yield" (అందించు) చేస్తుంది. ఇటరేటర్ యొక్కnext()పద్ధతిని మళ్లీ కాల్ చేసినప్పుడు, ఫంక్షన్ అది ఆగిపోయిన చోటు నుండి తిరిగి ప్రారంభమవుతుంది.
మీరు ఒక జెనరేటర్ ఫంక్షన్ను కాల్ చేసినప్పుడు, అది వెంటనే దాని బాడీని అమలు చేయదు. బదులుగా, ఇది ప్రోటోకాల్తో పూర్తిగా అనుకూలమైన ఒక ఇటరేటర్ ఆబ్జెక్ట్ను తిరిగి ఇస్తుంది. జావాస్క్రిప్ట్ ఇంజిన్ స్టేట్ మెషీన్, next() పద్ధతి, మరియు { value, done } ఆబ్జెక్ట్ల సృష్టిని మీ కోసం స్వయంచాలకంగా నిర్వహిస్తుంది.
మన `Timeline` ఉదాహరణను రీఫ్యాక్టర్ చేయడం
జెనరేటర్ ఫంక్షన్లు మన Timeline అమలును ఎంత నాటకీయంగా సులభతరం చేస్తాయో చూద్దాం. లాజిక్ అలాగే ఉంటుంది, కానీ కోడ్ చాలా చదవడానికి సులభంగా మరియు తక్కువ దోషరహితంగా మారుతుంది.
class Timeline {
constructor() {
this.events = [];
}
addEvent(year, description) {
this.events.push({ year, description });
}
// Refactored with a generator function!
*[Symbol.iterator]() { // The asterisk makes this a generator method
// Create a sorted copy
const sortedEvents = [...this.events].sort((a, b) => a.year - b.year);
// Loop through the sorted events
for (const event of sortedEvents) {
// yield pauses the function and returns the value
yield event;
}
// When the function finishes, the iterator is automatically marked as 'done'
}
}
// Usage is exactly the same, but the implementation is cleaner!
const myGenTimeline = new Timeline();
myGenTimeline.addEvent(2002, "The Euro currency is introduced");
myGenTimeline.addEvent(1998, "Google is founded");
for (const event of myGenTimeline) {
console.log(`${event.year}: ${event.description}`);
}
తేడా చూడండి! ఇటరేటర్ ఆబ్జెక్ట్ యొక్క సంక్లిష్టమైన మాన్యువల్ సృష్టి పోయింది. స్థితి (మనం ఏ ఈవెంట్పై ఉన్నాము) జెనరేటర్ ఫంక్షన్ యొక్క పాజ్ చేయబడిన స్థితి ద్వారా పరోక్షంగా నిర్వహించబడుతుంది. ఇది ఇటరేటర్ ప్రోటోకాల్ను అమలు చేయడానికి ఆధునిక, ప్రాధాన్యత కలిగిన మార్గం.
`yield*` యొక్క శక్తి
జెనరేటర్ ఫంక్షన్లకు మరో సూపర్ పవర్ ఉంది: yield* (యీల్డ్ స్టార్). ఇది ఒక జెనరేటర్కు ఇటరేషన్ ప్రక్రియను మరొక ఇటరేబుల్ ఆబ్జెక్ట్కు అప్పగించడానికి అనుమతిస్తుంది. ఇది బహుళ మూలాల నుండి ఇటరేటర్లను కంపోజ్ చేయడానికి ఒక అద్భుతమైన శక్తివంతమైన సాధనం.
మనకు బహుళ Timeline ఆబ్జెక్ట్లు (ఉదా., ఒకటి డిజైన్ కోసం, ఒకటి డెవలప్మెంట్ కోసం) ఉన్న ఒక `Project` క్లాస్ ఉందని ఊహించండి. మనం `Project`ను కూడా ఇటరేబుల్గా మార్చవచ్చు, మరియు అది దాని అన్ని టైమ్లైన్ల నుండి అన్ని ఈవెంట్లను క్రమంలో సజావుగా ఇటరేట్ చేస్తుంది.
class Project {
constructor(name) {
this.name = name;
this.designTimeline = new Timeline();
this.devTimeline = new Timeline();
}
*[Symbol.iterator]() {
console.log(`Iterating through events for project: ${this.name}`);
console.log("--- Design Events ---");
yield* this.designTimeline; // Delegate to the design timeline's iterator
console.log("--- Development Events ---");
yield* this.devTimeline; // Then delegate to the dev timeline's iterator
}
}
const websiteProject = new Project("Global Website Relaunch");
websiteProject.designTimeline.addEvent(2023, "Initial wireframes created");
websiteProject.designTimeline.addEvent(2024, "Final brand guide approved");
websiteProject.devTimeline.addEvent(2024, "Backend API developed");
websiteProject.devTimeline.addEvent(2025, "Frontend deployment");
for (const event of websiteProject) {
console.log(` - ${event.year}: ${event.description}`);
}
పెద్ద చిత్రం: ఆధునిక జావాస్క్రిప్ట్లో ఇటరేటర్ ప్రోటోకాల్ ఎందుకు ఒక మూలస్తంభం
ఇటరేటర్ ప్రోటోకాల్ అకడమిక్ ఆసక్తి లేదా లైబ్రరీ రచయితల కోసం ఒక ఫీచర్ కంటే చాలా ఎక్కువ. ఇది ఇంటర్ఆపరేబిలిటీ మరియు సొగసైన కోడ్ను ప్రోత్సహించే ఒక ప్రాథమిక డిజైన్ ప్యాటర్న్. దీన్ని ఒక యూనివర్సల్ అడాప్టర్గా భావించండి. మీ ఆబ్జెక్ట్లను ఈ ప్రమాణానికి అనుగుణంగా మార్చడం ద్వారా, మీరు వాటిని ఏ డేటా శ్రేణితోనైనా పనిచేయడానికి రూపొందించబడిన భాషా ఫీచర్ల యొక్క భారీ పర్యావరణ వ్యవస్థలోకి ప్లగ్ చేస్తున్నారు.
ఇటరేబుల్ ప్రోటోకాల్పై ఆధారపడే ఫీచర్ల జాబితా విస్తృతమైనది మరియు పెరుగుతోంది:
- లూప్లు:
for...of - అర్రే సృష్టి/సంకలనం: స్ప్రెడ్ సింటాక్స్ (
[...iterable]) మరియుArray.from(iterable) - డేటా స్ట్రక్చర్లు:
new Map(iterable),new Set(iterable),new WeakMap(iterable), మరియుnew WeakSet(iterable)కోసం కన్స్ట్రక్టర్లు అన్నీ ఇటరేబుల్స్ను అంగీకరిస్తాయి. - అసింక్రోనస్ ఆపరేషన్లు:
Promise.all(iterable),Promise.race(iterable), మరియుPromise.any(iterable)ప్రామిస్ల ఇటరేబుల్పై పనిచేస్తాయి. - డిస్ట్రక్చరింగ్: మీరు ఏ ఇటరేబుల్తోనైనా డిస్ట్రక్చరింగ్ అసైన్మెంట్ ఉపయోగించవచ్చు:
const [first, second] = myIterable; - కొత్త APIలు: టెక్స్ట్ సెగ్మెంటేషన్ కోసం
Intl.Segmenterవంటి ఆధునిక APIలు కూడా ఇటరేబుల్ ఆబ్జెక్ట్లను తిరిగి ఇస్తాయి.
మీరు మీ కస్టమ్ డేటా స్ట్రక్చర్లను ఇటరేబుల్గా చేసినప్పుడు, మీరు కేవలం ఒక `for...of` లూప్ను ప్రారంభించడం లేదు; మీరు వాటిని ఈ మొత్తం శక్తివంతమైన సాధనాల సూట్తో అనుకూలంగా మారుస్తున్నారు, మీ కోడ్ భవిష్యత్తుకు అనుకూలంగా ఉంటుందని మరియు ఇతర డెవలపర్లు ఉపయోగించడానికి మరియు అర్థం చేసుకోవడానికి సులభంగా ఉంటుందని నిర్ధారిస్తున్నారు.
ముగింపు: ఇటరేషన్లో మీ తదుపరి దశలు
మనం ఇటరేబుల్ మరియు ఇటరేటర్ ప్రోటోకాల్ల ప్రాథమిక నియమాల నుండి మన స్వంత కస్టమ్ ఇటరేటర్లను నిర్మించడం వరకు, మరియు చివరకు జెనరేటర్ ఫంక్షన్ల యొక్క శుభ్రమైన, ఆధునిక వాక్యనిర్మాణం వరకు ప్రయాణించాము. మీరు ఇప్పుడు ఊహించగల ఏ డేటా స్ట్రక్చర్నైనా ఎలా దాటాలో జావాస్క్రిప్ట్కు నేర్పించే జ్ఞానాన్ని కలిగి ఉన్నారు.
ఈ ప్రోటోకాల్పై పట్టు సాధించడం జావాస్క్రిప్ట్ డెవలపర్గా మీ ప్రయాణంలో ఒక ముఖ్యమైన అడుగు. ఇది మిమ్మల్ని భాష యొక్క ఫీచర్ల వినియోగదారు నుండి, మీ నిర్దిష్ట అవసరాలకు సరిపోయేలా భాష యొక్క ప్రధాన సామర్థ్యాలను విస్తరించగల సృష్టికర్తగా మారుస్తుంది.
గ్లోబల్ డెవలపర్ల కోసం క్రియాత్మక అంతర్దృష్టులు
- మీ కోడ్ను ఆడిట్ చేయండి: మీ ప్రస్తుత ప్రాజెక్ట్లలో డేటా శ్రేణిని సూచించే ఆబ్జెక్ట్ల కోసం చూడండి. మీరు వాటిపై
.forEachItem()లేదా.getItems()వంటి కస్టమ్, ప్రామాణికం కాని పద్ధతులతో ఇటరేట్ చేస్తున్నారా? మెరుగైన ఇంటర్ఆపరేబిలిటీ కోసం వాటిని ప్రామాణిక ఇటరేటర్ ప్రోటోకాల్ను అమలు చేయడానికి రీఫ్యాక్టర్ చేయడాన్ని పరిగణించండి. - సోమరితనాన్ని స్వీకరించండి: పెద్ద లేదా అనంతమైన డేటాసెట్లను సూచించడానికి ఇటరేటర్లను, మరియు ముఖ్యంగా జెనరేటర్లను ఉపయోగించండి. ఇది డిమాండ్పై డేటాను ప్రాసెస్ చేయడానికి మిమ్మల్ని అనుమతిస్తుంది, ఇది మెమరీ సామర్థ్యం మరియు పనితీరులో గణనీయమైన మెరుగుదలలకు దారితీస్తుంది. మీకు అవసరమైనప్పుడు, మీకు అవసరమైనది మాత్రమే మీరు గణిస్తారు.
- జెనరేటర్లకు ప్రాధాన్యత ఇవ్వండి: మీరు సృష్టించే ఏ కొత్త ఆబ్జెక్ట్ ఇటరేబుల్గా ఉండాలంటే, జెనరేటర్ ఫంక్షన్లను (
function*) మీ డిఫాల్ట్ ఎంపికగా చేసుకోండి. అవి మాన్యువల్ అమలు కంటే మరింత సంక్షిప్తంగా, స్టేట్ మేనేజ్మెంట్ దోషాలకు తక్కువ అవకాశం కలిగి, మరియు మరింత చదవడానికి సులభంగా ఉంటాయి. - శ్రేణుల రూపంలో ఆలోచించండి: ప్రోగ్రామింగ్ సమస్యలను శ్రేణుల దృష్టికోణం నుండి చూడటం ప్రారంభించండి. ఒక సంక్లిష్ట వ్యాపార ప్రక్రియ, ఒక డేటా ట్రాన్స్ఫర్మేషన్ పైప్లైన్, లేదా ఒక UI స్టేట్ ట్రాన్సిషన్ను దశల శ్రేణిగా మోడల్ చేయవచ్చా? అలా అయితే, ఒక ఇటరేటర్ దానికి సరైన, సొగసైన సాధనం కావచ్చు.
ఇటరేటర్ ప్రోటోకాల్ను మీ డెవలప్మెంట్ టూల్కిట్లో ఏకీకృతం చేయడం ద్వారా, మీరు ప్రపంచంలో ఎక్కడైనా డెవలపర్లు అర్థం చేసుకుని, ప్రశంసించే శుభ్రమైన, మరింత శక్తివంతమైన, మరియు మరింత ఇడియోమాటిక్ జావాస్క్రిప్ట్ వ్రాస్తారు.