קוראים כמוך עוזרים לתמוך ב-MUO. כאשר אתה מבצע רכישה באמצעות קישורים באתר שלנו, אנו עשויים להרוויח עמלת שותף. קרא עוד.

איטרציה על אוספי נתונים באמצעות לולאות מסורתיות עשויה להפוך במהירות למסורבלת ואטית, במיוחד כאשר עוסקים בכמויות אדירות של נתונים.

מחוללים ואיטרטורים של JavaScript מספקים פתרון לאיטרציה יעילה על פני אוספי נתונים גדולים. באמצעותם, אתה יכול לשלוט בזרימת האיטרציה, להניב ערכים אחד בכל פעם, ולהשהות ולחדש את תהליך האיטרציה.

כאן תכסו את היסודות והפנימיות של איטרטור JavaScript וכיצד תוכלו ליצור איטרטור באופן ידני ובאמצעות מחולל.

איטרטורים של JavaScript

איטרטור הוא אובייקט JavaScript שמיישם את פרוטוקול האיטרטור. חפצים אלה עושים זאת על ידי כך שיש להם א הַבָּא שיטה. שיטה זו מחזירה אובייקט שמיישם את IteratorResult מִמְשָׁק.

ה IteratorResult הממשק כולל שני מאפיינים: בוצע ו ערך. ה בוצע נכס הוא בוליאני שחוזר שֶׁקֶר אם האיטרטור יכול לייצר את הערך הבא ברצף שלו או נָכוֹן אם האיטרטור השלים את הרצף שלו.

ה ערך מאפיין הוא ערך JavaScript המוחזר על ידי האיטרטור במהלך הרצף שלו. כאשר איטרטור משלים את הרצף שלו (מתי בוצענָכוֹן), מאפיין זה חוזר לא מוגדר.

instagram viewer

כפי שהשם מרמז, איטרטורים מאפשרים לך "לחזור" על אובייקטי JavaScript כגון מערכים או מפות. התנהגות זו אפשרית עקב הפרוטוקול הניתן לחזרה.

ב-JavaScript, פרוטוקול איטרנס הוא דרך סטנדרטית להגדיר אובייקטים שאתה יכול לחזור עליהם, כגון ב- בשביל לוּלָאָה.

לדוגמה:

const פירות = ["בננה", "מנגו", "תפוח עץ", "ענבים"];

ל (const איטרטור שֶׁל פירות) {
לְנַחֵם.log (איטרטור);
}

/*
בננה
מנגו
תפוח עץ
ענבים
*/

דוגמה זו חוזרת על ה- פירות מערך באמצעות a בשביל לוּלָאָה. בכל איטרציה הוא רושם את הערך הנוכחי לקונסולה. זה אפשרי מכיוון שמערכים ניתנים לחזרה.

סוגים מסוימים של JavaScript, כגון מערכים, מחרוזות, סטים ומפות, הם איטרבלים מובנים מכיוון שהם (או אחד מהאובייקטים במעלה שרשרת האב-טיפוס שלהם) מיישמים @@iterator שיטה.

סוגים אחרים, כגון אובייקטים, אינם ניתנים לחזרה כברירת מחדל.

לדוגמה:

const iterObject = {
מכוניות: ["טסלה", "ב.מ. וו", "טויוטה"],
בעלי חיים: ["חתול", "כֶּלֶב", "אוֹגֵר"],
מזון: ["המבורגרים", "פיצה", "פסטה"],
};

ל (const איטרטור שֶׁל iterObject) {
לְנַחֵם.log (איטרטור);
}

// TypeError: iterObject אינו ניתן לחזרה

דוגמה זו מדגימה מה קורה כאשר אתה מנסה לחזור על אובייקט שאינו ניתן לחזרה.

הפיכת אובייקט לחזרה

כדי להפוך אובייקט לחזרה, עליך ליישם את א סמל.איטרטור שיטה על האובייקט. כדי להפוך לחזרה, שיטה זו חייבת להחזיר אובייקט שמיישם את IteratorResult מִמְשָׁק.

ה סמל.איטרטור סמל משרת את אותה מטרה כמו @@iterator וניתן להשתמש בו לסירוגין ב"מפרט" אך לא בקוד as @@iterator אינו תחביר JavaScript חוקי.

בלוקי הקוד שלהלן מספקים דוגמה כיצד להפוך אובייקט לחזרה באמצעות ה- iterObject.

ראשית, הוסף את סמל.איטרטור שיטה ל iterObject באמצעות תפקוד הַצהָרָה.

ככה:

iterObject[סֵמֶל.iterator] = פוּנקצִיָה () {
// קוביות הקוד הבאות עוברות לכאן...
}

לאחר מכן, תצטרך לגשת לכל המפתחות באובייקט שברצונך להפוך לאפשרי. אתה יכול לגשת למפתחות באמצעות אובייקט.מפתחות שיטה, המחזירה מערך של מאפיינים רבים מספור של אובייקט. כדי להחזיר מערך של iterObjectהמפתחות של, העבר את זֶה מילת מפתח כטיעון ל אובייקט.מפתחות.

לדוגמה:

לתת objProperties = לְהִתְנַגֵד.keys(זֶה)

גישה למערך זה תאפשר לך להגדיר את התנהגות האיטרציה של האובייקט.

לאחר מכן, עליך לעקוב אחר האיטרציות של האובייקט. אתה יכול להשיג זאת באמצעות משתני מונה.

לדוגמה:

לתת propertyIndex = 0;
לתת childIndex = 0;

אתה תשתמש במשתנה המונה הראשון כדי לעקוב אחר מאפייני האובייקט והשני כדי לעקוב אחר הילדים של הנכס.

לאחר מכן, תצטרך ליישם ולהחזיר את הַבָּא שיטה.

ככה:

לַחֲזוֹר {
next() {
// קוביות הקוד הבאות עוברות לכאן...
}
}

בתוך ה הַבָּא בשיטה, תצטרך לטפל במקרה של קצה המתרחש כאשר האובייקט כולו עבר איטרציה. כדי לטפל במארז הקצה, עליך להחזיר חפץ עם ה ערך מכוון ל לא מוגדר ו בוצע מכוון ל נָכוֹן.

אם המקרה הזה לא יטופל, ניסיון לבצע איטרציה על האובייקט יגרום ללולאה אינסופית.

הנה איך לטפל במארז הקצה:

אם (PropertyIndex > objProperties.אורך- 1) {
לַחֲזוֹר {
ערך: לא מוגדר,
בוצע: נָכוֹן,
};
}

לאחר מכן, תצטרך לגשת למאפייני האובייקט ולרכיבי הצאצא שלהם באמצעות משתני המונה שהצהרת קודם לכן.

ככה:

// גישה לנכסי אב וצאצא
const מאפיינים = זֶה[objProperties[propertyIndex]];

const נכס = מאפיינים[ילדאינדקס];

לאחר מכן, עליך ליישם לוגיקה כלשהי להגדלת משתני המונה. ההיגיון צריך לאפס את ילד אינדקס כאשר לא קיימים יותר אלמנטים במערך של מאפיין ועוברים למאפיין הבא באובייקט. בנוסף, זה צריך לעלות ילד אינדקס, אם עדיין יש אלמנטים במערך של המאפיין הנוכחי.

לדוגמה:

// לוגיקה להגדלת אינדקס
if (childIndex >= properties.length - 1) {
// אם אין יותר אלמנטים במערך הילד
// אִתחוּליֶלֶדאינדקס
childIndex = 0;

// עבור לנכס הבא
propertyIndex++;
} אַחֵר {
// עבור לרכיב הבא במערך הצאצא
childIndex++
}

לבסוף, החזר אובייקט עם ה בוצע מאפיין מוגדר ל שֶׁקֶר וה ערך מאפיין מוגדר לרכיב הבן הנוכחי באיטרציה.

לדוגמה:

לַחֲזוֹר {
בוצע: שֶׁקֶר,
ערך: נכס,
};

שלך הושלם סמל.איטרטור הפונקציה צריכה להיות דומה לגוש הקוד שלהלן:

iterObject[סֵמֶל.iterator] = פוּנקצִיָה () {
const objProperties = לְהִתְנַגֵד.keys(זֶה);
לתת propertyIndex = 0;
לתת childIndex = 0;

לַחֲזוֹר {
הַבָּא: () => {
//מקרה קצה טיפול
אם (PropertyIndex > objProperties.אורך- 1) {
לַחֲזוֹר {
ערך: לא מוגדר,
בוצע: נָכוֹן,
};
}

// גישה לנכסי אב וצאצא
const מאפיינים = זֶה[objProperties[propertyIndex]];

const נכס = מאפיינים[ילדאינדקס];

// לוגיקה להגדלת אינדקס
if (childIndex >= properties.length - 1) {
// אם אין יותר אלמנטים במערך הילד
// אִתחוּליֶלֶדאינדקס
childIndex = 0;

// עבור לנכס הבא
propertyIndex++;
} אַחֵר {
// עבור לרכיב הבא במערך הצאצא
childIndex++
}

לַחֲזוֹר {
בוצע: שֶׁקֶר,
ערך: נכס,
};
},
};
};

הפעלת א בשביל לולאה על iterObject לאחר יישום זה לא יזרוק שגיאה כפי שהוא מיישם א סמל.איטרטור שיטה.

יישום ידני של איטרטורים, כפי שעשינו לעיל, אינו מומלץ מכיוון שהוא מועד מאוד לשגיאות, וההיגיון יכול להיות קשה לניהול.

מחוללי JavaScript

מחולל JavaScript הוא פונקציה שניתן להשהות ולחדש את ביצועה בכל נקודה. התנהגות זו מאפשרת לה לייצר רצף של ערכים לאורך זמן.

פונקציית מחולל, שהיא פונקציה שמחזירה מחולל, מספקת חלופה ליצירת איטרטורים.

אתה יכול ליצור פונקציית מחולל באותו אופן שאתה יוצר הצהרת פונקציה ב-JavaScript. ההבדל היחיד הוא שאתה חייב לצרף כוכבית (*) למילת המפתח של הפונקציה.

לדוגמה:

פוּנקצִיָה* דוגמא () {
לַחֲזוֹר"גֵנֵרָטוֹר"
}

כאשר אתה קורא לפונקציה רגילה ב-JavaScript, היא מחזירה את הערך שצוין על ידי הפונקציה שלה לַחֲזוֹר מילת מפתח או לא מוגדר אחרת. אבל פונקציית מחולל לא מחזירה שום ערך באופן מיידי. זה מחזיר אובייקט Generator, אותו אתה יכול להקצות למשתנה.

כדי לגשת לערך הנוכחי של האיטרטור, התקשר ל- הַבָּא שיטה על אובייקט Generator.

לדוגמה:

const gen = example();

console.log (gen.next()); // { ערך: 'גֵנֵרָטוֹר', בוצע: נָכוֹן }

בדוגמה למעלה, ה ערך רכוש הגיע מא לַחֲזוֹר מילת מפתח, המפסיקה למעשה את המחולל. התנהגות זו בדרך כלל לא רצויה עם פונקציות מחולל, שכן מה שמבדיל אותן מפונקציות רגילות הוא היכולת להשהות ולהתחיל מחדש את הביצוע.

מילת המפתח של התשואה

ה תְשׁוּאָה מילת מפתח מספקת דרך לחזור על ערכים במחוללים על ידי השהיית ביצוע פונקציית מחולל והחזרת הערך שאחריה.

לדוגמה:

פוּנקצִיָה* דוגמא() {
תְשׁוּאָה"דגם S"
תְשׁוּאָה"דגם X"
תְשׁוּאָה"משאית סייבר"

לַחֲזוֹר"טסלה"
}

const gen = example();

console.log (gen.next()); // { ערך: 'דגם S', בוצע: שֶׁקֶר }

בדוגמה למעלה, כאשר ה הַבָּא השיטה נקראת על דוגמא מחולל, הוא ישהה ​​בכל פעם שהוא נתקל ב- תְשׁוּאָה מילת מפתח. ה בוצע הנכס יוגדר גם ל שֶׁקֶר עד שהוא פוגש א לַחֲזוֹר מילת מפתח.

מתקשר ל הַבָּא שיטה מספר פעמים על דוגמא מחולל כדי להדגים זאת, יהיה לך את הדבר הבא בתור הפלט שלך.

console.log (gen.next()); // { ערך: 'דגם X', בוצע: שֶׁקֶר }
console.log (gen.next()); // { ערך: 'משאית סייבר', בוצע: שֶׁקֶר }
console.log (gen.next()); // { ערך: 'טסלה', בוצע: נָכוֹן }

לְנַחֵם.log (gen.next()); // { value: undefined, done: true }

אתה יכול גם לחזור על אובייקט מחולל באמצעות ה בשביל לוּלָאָה.

לדוגמה:

ל (const איטרטור שֶׁל gen) {
לְנַחֵם.log (איטרטור);
}

/*
דגם S
דגם X
משאית סייבר
*/

שימוש באיטרטורים ומחוללים

למרות שאיטרטורים ומחוללים עשויים להיראות כמו מושגים מופשטים, הם לא. הם יכולים להיות מועילים כאשר עובדים עם זרמי נתונים אינסופיים ואיסוף נתונים. אתה יכול גם להשתמש בהם כדי ליצור מזהים ייחודיים. ספריות ניהול מדינה כמו MobX-State-Tree (MST) משתמשות בהן גם מתחת למכסה המנוע.