JS - symbol.iterator - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

JS - symbol.iterator


LOOSE

Recommended Posts

הי,

התחלתי ללמוד ES2015

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

 

נ.ב 

אני מבין שב ES2015 המושג var נעלם והתחלף לחלוטין ב let?

קישור לתוכן
שתף באתרים אחרים

Symbol.iterator משמש ליצירת איטרטורים על אובייקטים, כדי שיהיה אפשר להשתמש בהם בלולאת for..in. אם יש לך אובייקט איטרבילי בשם x (כמו מחרוזת, מערך או רשימה) אז אפשר לגשת לאיטרטור עליו באמצעות [x[Symbol.iterator. הביטוי [x[Symbol.iterator מצביע לפונקציה שמחזירה איטרטור חדש - לכן בשביל להשתמש בו צריך להפעיל אותה, דהיינו ()[x[Symbol.iterator. זה מחזיר אובייקט איטרטור. לאובייקט איטרטור יש פונקציה אחת בלבד - next. כל הפעלה שלה צריכה להחזיר את האיבר החדש ברשימה בתוך אובייקט עם שני שדות -done ו-value. השדה done הוא בוליאני ומכיל האם האיטרטור סיים את פעולתו, והשדה value ומכיל את האיבר מהרשימה. אם לדוגמה הייתי רוצה להשתמש באיטרטור בלי for..in אז הייתי צריך:

א. להפעיל את הפונקציה ()[x[Symbol.iterator ולשמור את התוצאה במשתנה חדש (נקרא לו לדוגמה y).

ב. להפעיל בלולאת while את הפונקציה ()y.next ולשמור את התוצאה בעוד משתנה (נקרא לו לדוגמה z).

ג. לבדוק אם z.done == true, ואם כן אז לעצור את הלולאה.

ד. להשתמש ב-z.value כאוות נפשי.

 

התחביר for..in עושה את כל זה עבורי כמובן.

מה שהוא Symbol.iterator מאפשר לעשות הוא להגדיר איטרטורים בעצמי, על ידי דריסת הפונקציה בפונקציה משלי. כלומר במקום להפעיל את [x[Symbol.iterator אני יכול לעשות { ... } x[Symbol.iterator] = function. שים לב שהפונקציה הזו צריכה להיות פונקציה שמחזירה אובייקט שיש לו פונקציה בשם next, דהיינו היא צריכה להיראות משהו כזה:

x[Symbol.iterator] = function() {
  return {
    next: function() {
      if (something) { return { value: somevalue, done: false } }
      else { return { done: true } }
    }
  };
};

לשמחתנו חסכו לנו את העבודה המיותרת של לתחזק את הפונקציה next באמצעות התחביר *function ו-yield. אם אני מגדיר פונקציה כ-*function במקום function אז הפונקציה הזו נקראת גנרטור, וקורים שני דברים:

א. אני צריך להשתמש במילה yield במקום במילה return, ומותר לי להשתמש בה כמה פעמים שאני רוצה במהלך הפונקציה.

ב. הפונקציה לא מחזירה ערך אחד, אלא מחזיר אובייקט איטרטור (עם next והכל). כל קריאה ל-next ממשיכה את הריצה של הפונקציה מהפעם הקודמת שהיא עצרה ועד שהיא נתקלת ב-yield, ואז מחזיר את הערך הזה.

 

ככה לדוגמה אם אני רוצה ליצור איטרטור שמחזיר את האיברים 1,2,3 בסדר הזה וזהו, אז אני אגדיר את האיטרטור ככה:

x[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

שזה אומר: בפעם הראשונה שיקראו ל-next תחזיר 1, בפעם השנייה תחזיר 2, בפעם השלישית תחזיר 3, ובפעם הרביעית תחזיר done == true. 

אפשר כמובן להשתמש ב-yield גם בתוך לולאה כדי להחזיר רשימה לא קבועה מראש של איברים. אפשר אפילו להשתמש בלולאה אינסופית של yield, ואז ליצור רשימה אינסופית (ואז מי שמשתמש ב-for..in צריך לדאוג לעצור את הלולאה בעצמו).

 

 

ההבדל בין var ו-let הוא ה-scope. משתנה שהוצהר כ-var קיים בכל הפונקציה, בעוד שמשתנה שהוצהר כ-let קיים רק בתוך הבלוק שבו הוא הוצהר, כמו ברוב שפות התכנות הפופולריות כיום. var לא הוחלף, הוא עדיין קיים, אבל אני מסכים שאין סיבה להשתמש בו כשיש let.

 

 

קישור לתוכן
שתף באתרים אחרים

ציטוט של שניצל

Symbol.iterator משמש ליצירת איטרטורים על אובייקטים, כדי שיהיה אפשר להשתמש בהם בלולאת for..in. אם יש לך אובייקט איטרבילי בשם x (כמו מחרוזת, מערך או רשימה) אז אפשר לגשת לאיטרטור עליו באמצעות [x[Symbol.iterator. הביטוי [x[Symbol.iterator מצביע לפונקציה שמחזירה איטרטור חדש - לכן בשביל להשתמש בו צריך להפעיל אותה, דהיינו ()[x[Symbol.iterator. זה מחזיר אובייקט איטרטור. לאובייקט איטרטור יש פונקציה אחת בלבד - next. כל הפעלה שלה צריכה להחזיר את האיבר החדש ברשימה בתוך אובייקט עם שני שדות -done ו-value. השדה done הוא בוליאני ומכיל האם האיטרטור סיים את פעולתו, והשדה value ומכיל את האיבר מהרשימה. אם לדוגמה הייתי רוצה להשתמש באיטרטור בלי for..in אז הייתי צריך:

א. להפעיל את הפונקציה ()[x[Symbol.iterator ולשמור את התוצאה במשתנה חדש (נקרא לו לדוגמה y).

ב. להפעיל בלולאת while את הפונקציה ()y.next ולשמור את התוצאה בעוד משתנה (נקרא לו לדוגמה z).

ג. לבדוק אם z.done == true, ואם כן אז לעצור את הלולאה.

ד. להשתמש ב-z.value כאוות נפשי.

 

התחביר for..in עושה את כל זה עבורי כמובן.

מה שהוא Symbol.iterator מאפשר לעשות הוא להגדיר איטרטורים בעצמי, על ידי דריסת הפונקציה בפונקציה משלי. כלומר במקום להפעיל את [x[Symbol.iterator אני יכול לעשות { ... } x[Symbol.iterator] = function. שים לב שהפונקציה הזו צריכה להיות פונקציה שמחזירה אובייקט שיש לו פונקציה בשם next, דהיינו היא צריכה להיראות משהו כזה:


x[Symbol.iterator] = function() {
  return {
    next: function() {
      if (something) { return { value: somevalue, done: false } }
      else { return { done: true } }
    }
  };
};

לשמחתנו חסכו לנו את העבודה המיותרת של לתחזק את הפונקציה next באמצעות התחביר *function ו-yield. אם אני מגדיר פונקציה כ-*function במקום function אז הפונקציה הזו נקראת גנרטור, וקורים שני דברים:

א. אני צריך להשתמש במילה yield במקום במילה return, ומותר לי להשתמש בה כמה פעמים שאני רוצה במהלך הפונקציה.

ב. הפונקציה לא מחזירה ערך אחד, אלא מחזיר אובייקט איטרטור (עם next והכל). כל קריאה ל-next ממשיכה את הריצה של הפונקציה מהפעם הקודמת שהיא עצרה ועד שהיא נתקלת ב-yield, ואז מחזיר את הערך הזה.

 

ככה לדוגמה אם אני רוצה ליצור איטרטור שמחזיר את האיברים 1,2,3 בסדר הזה וזהו, אז אני אגדיר את האיטרטור ככה:


x[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

שזה אומר: בפעם הראשונה שיקראו ל-next תחזיר 1, בפעם השנייה תחזיר 2, בפעם השלישית תחזיר 3, ובפעם הרביעית תחזיר done == true. 

אפשר כמובן להשתמש ב-yield גם בתוך לולאה כדי להחזיר רשימה לא קבועה מראש של איברים. אפשר אפילו להשתמש בלולאה אינסופית של yield, ואז ליצור רשימה אינסופית (ואז מי שמשתמש ב-for..in צריך לדאוג לעצור את הלולאה בעצמו).

 

 

ההבדל בין var ו-let הוא ה-scope. משתנה שהוצהר כ-var קיים בכל הפונקציה, בעוד שמשתנה שהוצהר כ-let קיים רק בתוך הבלוק שבו הוא הוצהר, כמו ברוב שפות התכנות הפופולריות כיום. var לא הוחלף, הוא עדיין קיים, אבל אני מסכים שאין סיבה להשתמש בו כשיש let.

 

 

 

קודם כל תודה ענקית על התגובה המאוד מושקעות ! 

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

 

כאילו מזה symbol ? אני מבין שזה primitive types כמו string, number , boolean וכו' אבל הוא לא הגיוני כמו האחרים. אני מבין את המהות של הprimitives האחרים לעומת symbol. 

בנוסף אתה בטוח שאתה מתכוון ל for in ולא for of ? כי הנה דוגמא:

let obj = {
	
	a: 'abx',
	b: 'cba',
	c: 'ccc',
	n: 1234
	
}

for(var i in obj){
	
	
console.log(i, obj[i])	
	
}

 אין לי פה שום בעיה לקבל את התוצאה שלה אני מצפה.

בנוסף ..מזה yield??? בחיים לא שמעתי על זה לפני.

בנוסף בנוסף , האם בעצם כל הסיפור פה זה ש for of חוסך לי צעד אחד בהקמת לופ (value במקום [value[i) ופשוט זה קצת בעייתי יותר לבצע את הלופ הזה על אובייקטים?

אם כן מה כלכך קשה לבצע for in במקום ? למה כל ה next , while, yield ...??? 

 

 

קישור לתוכן
שתף באתרים אחרים

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

 

ציטוט של LOOSE

כאילו מזה symbol ? אני מבין שזה primitive types כמו string, number , boolean וכו' אבל הוא לא הגיוני כמו האחרים. אני מבין את המהות של הprimitives האחרים לעומת symbol. 

זה לא אומר כלום. פשוט תתייחס ל-Symbol.iterator בתור מילת קסם שמציינת את פונקציית האיטרטור של אובייקט.

(אם אתה בכל זאת רוצה הסבר, אז Symbol זו מחלקה שמאפשרת לך להגדיר איזשהו מזהה ייחודי, ובין השאר מוגדרים בה כל מיני מזהים ייחודיים כגון Symbol.iterator)

 

צודק, התכוונתי for..of (אני רגיל ל-#C ששם זה foreach..in). אתה צודק שאתה יכול לעשות for..of על כל אובייקט, אבל מה אם אתה לא רוצה להשתמש באיטרטור הדיפולטי? מה אם אתה רוצה להחליט בעצמך על הסדר שיוחזרו איברים מהאובייקט? נניח לדוגמה שאתה יוצר סוג חדש של אובייקט, לדוגמה עץ בינארי, ואתה רוצה להיות מסוגל לעבור על כל האיברים שלו ב-for..in. בשביל זה אתה צריך להגדיר לו איטרטור משלך.

בשביל זה אתה צריך לדרוס את הפונקציה [Symbol.iterator] באמצעות הפונקציה שלך. את הפונקציה הזו אתה צריך לממש באמצעות yield-ים או באמצעות אובייקט עם פונקציית next כפי שהסברתי בהודעה הקודמת.

 

ציטוט של LOOSE

בנוסף בנוסף , האם בעצם כל הסיפור פה זה ש for of חוסך לי צעד אחד בהקמת לופ (value במקום [value[i) ופשוט זה קצת בעייתי יותר לבצע את הלופ הזה על אובייקטים?

כשאתה עובר על מערך או רשימה סדורה אז כן, זה רק חוסך לך צעד. אבל לא כל אוביקט הוא רשימה סדורה. לדוגמה, יכול להיות לך Set שזה אוסף לא סדור של איברים ייחודים (ללא כפילויות). אם אתה רוצה לעבור על איברי Set אז אתה חייב להשתמש ב-for..of, כי אי אפשר לגשת לאיבר ה-i ב-Set.

 

ציטוט של LOOSE

אם כן מה כלכך קשה לבצע for in במקום ? למה כל ה next , while, yield ...??? 

for..of הוא רק "סוכר תחבירי". כשאתה כותב for..of אז האינטרפרטר מאחורי הקלעים מתרגם את זה לקריאה לפונקציית ה-()[Symbol.iterator] והפעלת לולאת while עם קריאה ל-next שוב ושוב. אתה צודק שבעקרון אתה אף פעם לא צריך לכתוב את הקוד הזה בעצמך.

קישור לתוכן
שתף באתרים אחרים

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

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

 

אגב האם יש איזשהו ספר או מקור לימוד כזה או אחר שתוכל להמליץ עליו למישהו שרוצה להתקדם מרמה של מתחיל ל"שלב הבא" ?

קישור לתוכן
שתף באתרים אחרים

ארכיון

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

×
  • צור חדש...