मराठी

जावास्क्रिप्ट इटरेटर प्रोटोकॉल समजून घेण्यासाठी आणि अंमलात आणण्यासाठी एक सर्वसमावेशक मार्गदर्शक, जे तुम्हाला उत्तम डेटा हँडलिंगसाठी कस्टम इटरेटर्स तयार करण्यास सक्षम करते.

जावास्क्रिप्ट इटरेटर प्रोटोकॉल आणि कस्टम इटरेटर्सचे रहस्य उलगडणे

जावास्क्रिप्टचा इटरेटर प्रोटोकॉल डेटा स्ट्रक्चर्समधून जाण्यासाठी (traverse) एक प्रमाणित मार्ग प्रदान करतो. हा प्रोटोकॉल समजून घेतल्याने डेव्हलपर्सना अॅरेज आणि स्ट्रिंग्स सारख्या अंगभूत इटरेबल्ससोबत कार्यक्षमतेने काम करण्यास आणि विशिष्ट डेटा स्ट्रक्चर्स व ऍप्लिकेशनच्या आवश्यकतांनुसार स्वतःचे कस्टम इटरेबल्स तयार करण्यास सक्षम करते. हा मार्गदर्शक इटरेटर प्रोटोकॉल आणि कस्टम इटरेटर्स कसे लागू करावेत याचे सर्वसमावेशक अन्वेषण प्रदान करतो.

इटरेटर प्रोटोकॉल म्हणजे काय?

इटरेटर प्रोटोकॉल हे परिभाषित करतो की एखाद्या ऑब्जेक्टवर कसे पुनरावृत्ती (iterate) करता येईल, म्हणजेच, त्याचे घटक क्रमाने कसे ऍक्सेस केले जाऊ शकतात. यात दोन भाग आहेत: इटरेबल प्रोटोकॉल आणि इटरेटर प्रोटोकॉल.

इटरेबल प्रोटोकॉल

एखाद्या ऑब्जेक्टला इटरेबल मानले जाते जर त्यात Symbol.iterator की असलेली मेथड असेल. या मेथडने इटरेटर प्रोटोकॉलचे पालन करणारा ऑब्जेक्ट परत करणे आवश्यक आहे.

थोडक्यात, इटरेबल ऑब्जेक्टला स्वतःसाठी इटरेटर कसा तयार करायचा हे माहित असते.

इटरेटर प्रोटोकॉल

इटरेटर प्रोटोकॉल एका क्रमातून (sequence) व्हॅल्यूज कशा मिळवायच्या हे परिभाषित करतो. एखाद्या ऑब्जेक्टला इटरेटर मानले जाते जर त्यात next() मेथड असेल जी दोन प्रॉपर्टीज असलेला ऑब्जेक्ट परत करते:

next() मेथड ही इटरेटर प्रोटोकॉलचा मुख्य भाग आहे. next() ला प्रत्येक कॉल केल्यावर इटरेटर पुढे जातो आणि क्रमातील पुढील व्हॅल्यू परत करतो. जेव्हा सर्व व्हॅल्यूज परत केल्या जातात, तेव्हा next() हे done ला true सेट करून ऑब्जेक्ट परत करते.

बिल्ट-इन इटरेबल्स

जावास्क्रिप्टमध्ये अनेक बिल्ट-इन डेटा स्ट्रक्चर्स आहेत जे मूळतः इटरेबल आहेत. यामध्ये यांचा समावेश आहे:

हे इटरेबल्स for...of लूप, स्प्रेड सिंटॅक्स (...), आणि इटरेटर प्रोटोकॉलवर अवलंबून असलेल्या इतर रचनांसोबत थेट वापरले जाऊ शकतात.

अॅरेजचे उदाहरण:


const myArray = ["apple", "banana", "cherry"];

for (const item of myArray) {
  console.log(item); // Output: apple, banana, cherry
}

स्ट्रिंग्सचे उदाहरण:


const myString = "Hello";

for (const char of myString) {
  console.log(char); // Output: H, e, l, l, o
}

for...of लूप

for...of लूप इटरेबल ऑब्जेक्ट्सवर पुनरावृत्ती करण्यासाठी एक शक्तिशाली रचना आहे. हे इटरेटर प्रोटोकॉलची गुंतागुंत आपोआप हाताळते, ज्यामुळे क्रमातील व्हॅल्यूज ऍक्सेस करणे सोपे होते.

for...of लूपचा सिंटॅक्स आहे:


for (const element of iterable) {
  // प्रत्येक घटकासाठी कार्यान्वित केला जाणारा कोड
}

for...of लूप इटरेबल ऑब्जेक्टमधून इटरेटर मिळवतो (Symbol.iterator वापरून), आणि इटरेटरच्या next() मेथडला done हे true होईपर्यंत वारंवार कॉल करतो. प्रत्येक पुनरावृत्तीसाठी, element व्हेरिएबलला next() ने परत केलेल्या value प्रॉपर्टीचे मूल्य दिले जाते.

कस्टम इटरेटर्स तयार करणे

जावास्क्रिप्टमध्ये बिल्ट-इन इटरेबल्स असले तरी, इटरेटर प्रोटोकॉलची खरी शक्ती तुमच्या स्वतःच्या डेटा स्ट्रक्चर्ससाठी कस्टम इटरेटर्स परिभाषित करण्याच्या क्षमतेमध्ये आहे. यामुळे तुमचा डेटा कसा ट्रॅव्हर्स आणि ऍक्सेस केला जाईल यावर तुम्ही नियंत्रण ठेवू शकता.

कस्टम इटरेटर कसे तयार करावे हे येथे दिले आहे:

  1. तुमच्या कस्टम डेटा स्ट्रक्चरचे प्रतिनिधित्व करणारा क्लास किंवा ऑब्जेक्ट परिभाषित करा.
  2. तुमच्या क्लास किंवा ऑब्जेक्टवर Symbol.iterator मेथड लागू करा. या मेथडने एक इटरेटर ऑब्जेक्ट परत केला पाहिजे.
  3. इटरेटर ऑब्जेक्टमध्ये next() मेथड असणे आवश्यक आहे जी value आणि done प्रॉपर्टीज असलेला ऑब्जेक्ट परत करते.

उदाहरण: एका साध्या रेंजसाठी इटरेटर तयार करणे

चला Range नावाचा एक क्लास तयार करूया जो संख्यांची श्रेणी दर्शवतो. आपण रेंजमधील संख्यांवर पुनरावृत्ती करण्यासाठी इटरेटर प्रोटोकॉल लागू करू.


class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let currentValue = this.start;
    const that = this; // इटरेटर ऑब्जेक्टमध्ये 'this' वापरण्यासाठी 'this' कॅप्चर करा

    return {
      next() {
        if (currentValue <= that.end) {
          return {
            value: currentValue++,
            done: false,
          };
        } else {
          return {
            value: undefined,
            done: true,
          };
        }
      },
    };
  }
}

const myRange = new Range(1, 5);

for (const number of myRange) {
  console.log(number); // Output: 1, 2, 3, 4, 5
}

स्पष्टीकरण:

उदाहरण: लिंक्ड लिस्टसाठी इटरेटर तयार करणे

चला आणखी एक उदाहरण विचारात घेऊया: लिंक्ड लिस्ट डेटा स्ट्रक्चरसाठी इटरेटर तयार करणे. लिंक्ड लिस्ट हा नोड्सचा एक क्रम आहे, जिथे प्रत्येक नोडमध्ये एक व्हॅल्यू आणि लिस्टमधील पुढील नोडचा संदर्भ (पॉइंटर) असतो. लिस्टमधील शेवटच्या नोडचा संदर्भ null (किंवा undefined) असतो.


class LinkedListNode {
    constructor(value, next = null) {
        this.value = value;
        this.next = next;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
    }

    append(value) {
        const newNode = new LinkedListNode(value);
        if (!this.head) {
            this.head = newNode;
            return;
        }

        let current = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = newNode;
    }

    [Symbol.iterator]() {
        let current = this.head;

        return {
            next() {
                if (current) {
                    const value = current.value;
                    current = current.next;
                    return {
                        value: value,
                        done: false
                    };
                } else {
                    return {
                        value: undefined,
                        done: true
                    };
                }
            }
        };
    }
}

// वापराचे उदाहरण:
const myList = new LinkedList();
myList.append("London");
myList.append("Paris");
myList.append("Tokyo");

for (const city of myList) {
    console.log(city); // Output: London, Paris, Tokyo
}

स्पष्टीकरण:

जनरेटर फंक्शन्स

जनरेटर फंक्शन्स इटरेटर्स तयार करण्याचा अधिक संक्षिप्त आणि सुंदर मार्ग प्रदान करतात. ते मागणीनुसार व्हॅल्यूज तयार करण्यासाठी yield कीवर्ड वापरतात.

जनरेटर फंक्शन function* सिंटॅक्स वापरून परिभाषित केले जाते.

उदाहरण: जनरेटर फंक्शन वापरून इटरेटर तयार करणे

चला Range इटरेटरला जनरेटर फंक्शन वापरून पुन्हा लिहूया:


class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const myRange = new Range(1, 5);

for (const number of myRange) {
  console.log(number); // Output: 1, 2, 3, 4, 5
}

स्पष्टीकरण:

जनरेटर फंक्शन्स next() मेथड आणि done फ्लॅग आपोआप हाताळून इटरेटर निर्मिती सुलभ करतात.

उदाहरण: फिबोनाची क्रम जनरेटर

जनरेटर फंक्शन्स वापरण्याचे आणखी एक उत्तम उदाहरण म्हणजे फिबोनाची क्रम तयार करणे:


function* fibonacciSequence() {
  let a = 0;
  let b = 1;

  while (true) {
    yield a;
    [a, b] = [b, a + b]; // एकाच वेळी अपडेट करण्यासाठी डीस्ट्रक्चरिंग असाइनमेंट
  }
}

const fibonacci = fibonacciSequence();

for (let i = 0; i < 10; i++) {
  console.log(fibonacci.next().value); // Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

स्पष्टीकरण:

इटरेटर प्रोटोकॉल वापरण्याचे फायदे

प्रगत इटरेटर तंत्रे

इटरेटर्स एकत्र करणे

तुम्ही अनेक इटरेटर्सना एकाच इटरेटरमध्ये एकत्र करू शकता. जेव्हा तुम्हाला अनेक स्रोतांमधून डेटावर एकत्रितपणे प्रक्रिया करण्याची आवश्यकता असते तेव्हा हे उपयुक्त ठरते.


function* combineIterators(...iterables) {
  for (const iterable of iterables) {
    for (const item of iterable) {
      yield item;
    }
  }
}

const array1 = [1, 2, 3];
const array2 = ["a", "b", "c"];
const string1 = "XYZ";

const combined = combineIterators(array1, array2, string1);

for (const value of combined) {
  console.log(value); // Output: 1, 2, 3, a, b, c, X, Y, Z
}

या उदाहरणात, `combineIterators` फंक्शन युक्तिवाद म्हणून कितीही इटरेबल्स घेते. ते प्रत्येक इटरेबलवर पुनरावृत्ती करते आणि प्रत्येक आयटम yield करते. परिणामी एकच इटरेटर मिळतो जो सर्व इनपुट इटरेबल्समधील सर्व व्हॅल्यूज तयार करतो.

इटरेटर्सना फिल्टर आणि ट्रान्सफॉर्म करणे

तुम्ही असे इटरेटर्स देखील तयार करू शकता जे दुसऱ्या इटरेटरद्वारे तयार केलेल्या व्हॅल्यूजला फिल्टर किंवा ट्रान्सफॉर्म करतात. यामुळे तुम्हाला डेटावर पाइपलाइनमध्ये प्रक्रिया करता येते, प्रत्येक व्हॅल्यू तयार होताना त्यावर विविध ऑपरेशन्स लागू करता येतात.


function* filterIterator(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* mapIterator(iterable, transform) {
  for (const item of iterable) {
    yield transform(item);
    }
}

const numbers = [1, 2, 3, 4, 5, 6];

const evenNumbers = filterIterator(numbers, (x) => x % 2 === 0);
const squaredEvenNumbers = mapIterator(evenNumbers, (x) => x * x);

for (const value of squaredEvenNumbers) {
    console.log(value); // Output: 4, 16, 36
}

येथे, `filterIterator` एक इटरेबल आणि एक प्रेडिकेट फंक्शन घेते. ते फक्त त्या आयटम्सना yield करते ज्यासाठी प्रेडिकेट `true` परत करते. `mapIterator` एक इटरेबल आणि एक ट्रान्सफॉर्म फंक्शन घेते. ते प्रत्येक आयटमवर ट्रान्सफॉर्म फंक्शन लागू केल्याचा परिणाम yield करते.

वास्तविक-जगातील अनुप्रयोग

इटरेटर प्रोटोकॉल जावास्क्रिप्ट लायब्ररीज आणि फ्रेमवर्कमध्ये मोठ्या प्रमाणावर वापरला जातो, आणि तो विविध वास्तविक-जगातील अनुप्रयोगांमध्ये मौल्यवान आहे, विशेषतः मोठ्या डेटासेट किंवा असिंक्रोनस ऑपरेशन्स हाताळताना.

सर्वोत्तम पद्धती

निष्कर्ष

जावास्क्रिप्ट इटरेटर प्रोटोकॉल डेटा स्ट्रक्चर्समधून जाण्यासाठी एक शक्तिशाली आणि लवचिक मार्ग प्रदान करतो. इटरेबल आणि इटरेटर प्रोटोकॉल समजून घेऊन, आणि जनरेटर फंक्शन्सचा फायदा घेऊन, तुम्ही तुमच्या विशिष्ट गरजांनुसार कस्टम इटरेटर्स तयार करू शकता. यामुळे तुम्हाला डेटासोबत कार्यक्षमतेने काम करता येते, कोडची वाचनीयता सुधारते, आणि तुमच्या ऍप्लिकेशन्सचे कार्यप्रदर्शन वाढवते. इटरेटर्सवर प्रभुत्व मिळवल्याने जावास्क्रिप्टच्या क्षमतांची अधिक सखोल समज मिळते आणि तुम्हाला अधिक सुंदर आणि कार्यक्षम कोड लिहिण्यास सक्षम करते.

जावास्क्रिप्ट इटरेटर प्रोटोकॉल आणि कस्टम इटरेटर्सचे रहस्य उलगडणे | MLOG